-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[4주차] 이나현 미션 제출합니다. #16
base: main
Are you sure you want to change the base?
Conversation
- localStorage의 key는 string이고, 이것을 id라는 props에 숫자처럼 전달해줘서 생긴 문제였다.. id는 "1"처럼 문자로 전달되고 있었는데 나는 숫자로 전달되는지 알고 있었다.. 으악 콘솔 찍어봐도 모르겠더라
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
redux는 안써보고 recoil만 써봤는데... 너무 대단해요 > <
새로 알게된 코드도 많았고 전역상태라이브러리랑 라우팅 처음이라고 하셨는데 짱짱 수고하셨어요 👍😊
src/GlobalStyle.tsx
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
jsx와 tsx는 컴포넌트 관련 파일에만 사용해서 그래서 GlobalStyle처럼 리액트 컴포넌트를 사용하지 않는 스타일 파일은 보통 ts 확장자를 많이 쓴다고 해요 > <
<Wrapper> | ||
{buttonData.map(({ id, text, selectedIcon, unselectedIcon }) => ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저의 경우에는 여러 개 props인 경우 가독성을 위해 아래와 같이 구조분해 할당을 하는데요, 기능은 같으니까 참고만하시면 될 것 같아요 ㅎㅎ
.map((btn) => {
const { id, text, selectedIcon, unselectedIcon } = btn;
const ButtonWrapper = styled.div<{ isactive: boolean }>` | ||
display: flex; | ||
flex-direction: column; | ||
gap: 0.25rem; | ||
align-items: center; | ||
cursor: pointer; | ||
|
||
// 선택된 상태일 때 스타일 적용 | ||
color: ${(props) => (props.isactive ? colors.black : colors.gray300)}; | ||
`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
styled component에 props 전달까지 최곤데요 ? ㅎㅎ
그런데 한가지 주의해야할 점이 있는데요 바로 표준 속성 워닝입니당 ㅎㅎ
표준 속성 워닝이란?
React와 styled-components에서는 표준 HTML 속성이 아닌 사용자 정의 속성이 DOM 엘리먼트에 전달되는 것을 기본적으로 허용하지 않아서 발생하는 에러
따라서 styled component 5.1버전 이상부터는 $를 prefix로 써서 warning을 없애고 DOM에 불필요한 속성이 전달되는 것을 막아주었어요 !!
혹시나 구체적인 내용 더 확실히 알고 싶을 수 있으니 관련 아티클 첨부할게요 > <
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
전달할 때 isactive
을 $isactive
으로 !!
icon?: string | null; | ||
} | ||
|
||
const PageHeader = ({ title, search, icon }: PageHeaderProps) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const PageHeader = ({ title, search, icon }: PageHeaderProps) => { | |
const PageHeader = (PageHeaderProps) => { | |
const {title, search, icon} = PageHeaderProps |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요기도 이렇게 바꿔보면 개인적으로 가독성이 더 좋더라고요 ㅎㅎ
{ | ||
"text": "짱구야 노올자~", | ||
"sender": 0, | ||
"timestamp": "5월 3일 (금) 9:21 AM" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저번 코드리뷰때 저도 알게된 사실인데 보통 서버에서 받은 응답은
"createdAt": "2023-12-31T21:28:50.589Z" 같이 ISO 8601 날짜 및 시간 포맷을 따르는 문자열이 반환된다고 하네요 > <
리팩토링하면서 참고해도 좋을 것 같아요 👍
<Profile | ||
src={`img/userProfile/${userData.users[idNum].profileImg}`} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저는 이미지 배열을 굳이굳이 만들어서 map했는데 url로 식별하는 방법 너무 좋은데요? > <
|
||
ReactDOM.createRoot(document.getElementById("root")!).render( | ||
<React.StrictMode> | ||
<Provider store={store}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
redux는 Provider...! 신기하네요 > <
&::placeholder { | ||
${colors.gray300} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
placeholder까지!! 최고입니당
interface TemplateProps { | ||
children: ReactNode; | ||
} | ||
|
||
const Template = ({ children }: TemplateProps) => { | ||
return <Wrapper>{children}</Wrapper>; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
중첩라우팅보다 나현님처럼 children으로 넘겨주는게 props drilling 해결 측면에서 더 나을 것 같기도 하네용
덕분에 children 어떻게 쓰는지 감이 잡혔어요 > <
const MsgText = styled.span<{ | ||
bgcolor: string; | ||
txtcolor: string; | ||
maxwidth: number; | ||
}>` | ||
max-width: ${(props) => props.maxwidth}rem; | ||
${typography.body2}; | ||
background-color: ${(props) => props.bgcolor}; | ||
border-radius: 1.25rem; | ||
padding: 0.75rem; | ||
color: ${(props) => props.txtcolor}; | ||
text-align: start; | ||
`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
처음 보는 형태에요 !! 완전 신기하네요 ㅎㅎ
import friend_selected from "../../assets/footer/friend_selected.svg"; | ||
import friend_unSelected from "../../assets/footer/friend_unSelected.svg"; | ||
import chatting_unSelected from "../../assets/footer/chatting_unSelected.svg"; | ||
import chatting_selected from "../../assets/footer/chatting_selected.svg"; | ||
import you_selected from "../../assets/footer/you_selected.svg"; | ||
import you_unSelected from "../../assets/footer/you_unSelected.svg"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import 문이 많으면 가독성이 안좋을 수 있으니 index.ts하나 만들어서 요것들을 싹 넣어놓고
import {friend_selected, friend_uselected, ...} from "../assets"
여기서는 이렇게 한줄로 한번에 처리하는 방법도 있어요 ㅎㅎ
src/core/router.tsx
Outdated
<Route path="/" element={<FriendsList />} /> | ||
<Route path="/*" element={<FriendsList />} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
path를 /*로 설정한 이유가 궁금하네용 !!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아앗 제가 배포 오류 수정하고 있을 때 리뷰 남겨주신 것 같아요ㅎㅎ 리팩토링하면서 이 부분 쓸모없는 것 같아서 삭제했습니다!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이번주 과제 고생하셨습니다!
redux를 사용한 상태관리와 localStorage 저장까지 구현해주셔서 멋진 디스코드가 완성된 거 같아요! 리팩토링을 하게된다면 반응형을 고려해서 만들어보면 좋을 거 같아요!
"eslint-plugin-react-hooks": "^4.6.0", | ||
"eslint-plugin-react-refresh": "^0.4.6", | ||
"typescript": "^5.2.2", | ||
"vite": "^5.2.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vite를 설치하신 이유가 있는지 궁금합니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
일반적으로 index.html
파일은 public
에 넣어서 사용하는데, src
에서 바로 사용하신 이유가 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
App.js
에서 렌더링되는게 하나라면, 해당컴포넌트를 바로 App.js
에서 사용해도 괜찮을 것 같아요!
<Routes> | ||
<Route path="/" element={<FriendsList />} /> | ||
<Route path="/chats" element={<ChattingList />} /> | ||
<Route path="/room" element={<ChattingRoom />} /> | ||
<Route path="/profile" element={<MyProfile />} /> | ||
</Routes> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사용자가 올바르지 않은 URL을 타고 들어올 수도 있는데 이럴때는 대비한 에러페이지를 만들어줬으면 좋았을 것 같아요!
import ChattingList from "../pages/ChattingList/ChattingList"; | ||
import ChattingRoom from "../pages/ChattingRoom/ChattingRoom"; | ||
import FriendsList from "../pages/FriendsList/FriendsList"; | ||
import MyProfile from "../pages/MyProfile/MyProfile"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
상대경로를 사용해도 지장없지만 baseUrl과 path를 활용해서 절대경로를 사용해도 좋았을 거 같아요! 현재 저희는 cra를 사용하고 있기에 커스터마이징을 위해 craco,react-app-alias를 사용해야 하긴 하지만 한번 쯤 해보는 것도 좋은 거 같아요! 참고자료 첨부할게요!
|
||
const Wrapper = styled.div` | ||
width: 375px; | ||
height: 812px; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이번 과제는 375x812크기에 맞는 구현이라 요구사항은 아니였지만, 맥북같은 경우 기본 뷰가 812px보다 작기 때문에 화면이 전부 안보여요! 리팩토링 할때height: 100dvh
혹은 height: 100%
를 사용해 반응형을 고려하면 더 좋을 거 같아요.
<Wrapper> | ||
<TopWrapper> | ||
<Title>{title}</Title> | ||
{icon && <img src={editprofile} alt="프로필 편집" />} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
optional(?)을 사용해 확장성있는 컴포넌트가 되어서 좋아요!
import you_selected from "../../assets/footer/you_selected.svg"; | ||
import you_unSelected from "../../assets/footer/you_unSelected.svg"; | ||
|
||
const buttonData = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
공통되는 버튼들을 배열형식으로 구현해서 가독성이 훨씬 좋아요!
if (!isEmpty) { | ||
const newChat = { | ||
text: value, | ||
sender: currOpponent === 0 ? opponent : 0, | ||
timestamp: getDate(), | ||
}; | ||
dispatch(chatsActions.addChat({ newChat, opponent })); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
동일한 채팅방에서 같은 시간에 동일한 텍스트를 입력하면 완전히 동일한 객체가 생성되어 배열에 push되는거 같아요. 이렇게 하면 나중에 변경하거나 삭제하려고 할 때 오류가 발생할 수 있으니, 고유키를 넣어주면 더 좋을 거 같아요!
return ( | ||
<ChattingItemWrapper key={index}> | ||
<TimeStamp>{chat.timestamp}</TimeStamp> | ||
<MsgBox align={isMine ? "end" : "start"}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
동적스타일링을 통해 나의 채팅인지 상대의 채팅인지 구분할 수 있어서 좋아요! 하지만 이대로 콘솔창을 켜보면 아마 에러가 뜰 거에요. 스타일드컴포넌트를 위한 props
전달은 DOM으로 전달되는걸 방지하기 위해 $
을 사용한다고 해요!
3주차 미션: React-Messenger
🐻 배포 링크
https://discord-react-typescript.vercel.app/
👩💻 구현 기능
🥳 후기
Redux, Routing 기능을 처음 공부해서 사용해봤습니다! 걱정을 많이 했는데 생각보다 Redux가 어렵지 않았고 코드 구현을 하면서 재미도 많이 느꼈습니다. Routing도 페이지가 많지 않아서 그런 건지 쉽게 구현했습니다.
오히려 타입스크립트와 localStorage 관련 부분에서 더 어려움을 겪었던 것 같습니다.
그리고 프로젝트를 하면서 점점 느끼는 것은 무작정 코드를 짜기 시작하는 것보다 전체적인 구조와 세부 컴포넌트 틀, 전역으로 관리할 상태 등을 미리 정리하고 코드를 짜는 게 중요하다는 생각이 들었습니다. 그 과정이 처음에 할 때 오래 걸릴지 몰라도 개발에 들어가면 훨씬 수월하게 개발할 수 있는 것 같습니다.
💡 새롭게 배운 점
🔥 어려웠던 부분 / 의문점
✨ 더 구현해보고 싶은 기능
❓ Key Questions
1. Routing이란?
2. SPA란?
↔ 멀티 페이지 애플리케이션에서는 사용자가 다른 페이지로 이동할 때마다 새로운 html을 받아오고, 페이지를 로딩할 때마다 서버에서 CSS, JS, 이미지 파일 등의 리소스를 전달받아 브라우저 화면에 보여 주었다 !
3. 상태관리란?