Conversation
|
류원님 이번주차 과제도 고생 많으셨습니당!!! ⸜(ˊᗜˋ)⸝⭐ ⭐ 아, 그리고 친구 목록에 있는 친구들 저도 훔치고 싶어요... (˶• ﻌ •˶) |
jiwonnchoi
left a comment
There was a problem hiding this comment.
안녕하세요 류원님:)
저번 주차에 이은 채팅방 구현 완료하시느라 수고많으셨습니다-!! 👊❤️🔥
| <div className=" md:max-w-[375px] mx-auto"> | ||
| {" "} | ||
| {chatRoomMatch ? null : <BottomNav></BottomNav>} | ||
| </div> |
There was a problem hiding this comment.
이 부분에서 null을 반환하는 대신 && 연산자를 사용하면 더 좋을 것 같아요!
| <div className=" md:max-w-[375px] mx-auto"> | |
| {" "} | |
| {chatRoomMatch ? null : <BottomNav></BottomNav>} | |
| </div> | |
| <div className=" md:max-w-[375px] mx-auto"> | |
| {!chatRoomMatch && <BottomNav />} | |
| </div> |
| const isHomeActive = location.pathname === "/"; | ||
| const isChatActive = location.pathname === "/chat-list"; | ||
| const isStoryActive = location.pathname === "/story"; |
There was a problem hiding this comment.
아래에서 location.pathname === "path" 조건이 반복되는 코드를 간단히 하기 위해 여기선 이렇게 배열로 정의해두고,
| const isHomeActive = location.pathname === "/"; | |
| const isChatActive = location.pathname === "/chat-list"; | |
| const isStoryActive = location.pathname === "/story"; | |
| const navItems = [ | |
| { path: "/", label: "홈", icon: home }, | |
| { path: "/chat-list", label: "채팅", icon: chat }, | |
| { path: "/story", label: "스토리", icon: story }, | |
| ]; |
| <button | ||
| className={`flex flex-col items-center ${ | ||
| location.pathname === "/" ? "text-blue-500" : "text-gray-500" | ||
| }`} | ||
| onClick={() => nav("/")} | ||
| > | ||
| <img | ||
| src={home} | ||
| style={{ filter: isHomeActive ? activeFilter : "none" }} | ||
| className="w-6 h-6" | ||
| alt="" | ||
| /> | ||
| <span className="text-xs mt-1">홈</span> | ||
| </button> |
There was a problem hiding this comment.
요 버튼 단위의 코드가 중복되고 있으니 위에서 정의한 배열을 map으로 돌려서 다음과 같이 쓰면 중복을 줄일 수 있겠습니당
조건부에서도 반복되는 location.pathname === ~~ 부분을 줄이기 위해 변수로 따로 뺐습니다! (suggestion은 아래쪽 68줄까지를 대신하는건데 아래쪽까지 드래그가 다 안되어서..! 여기까지로 잡았습니다 🥲 )
| <button | |
| className={`flex flex-col items-center ${ | |
| location.pathname === "/" ? "text-blue-500" : "text-gray-500" | |
| }`} | |
| onClick={() => nav("/")} | |
| > | |
| <img | |
| src={home} | |
| style={{ filter: isHomeActive ? activeFilter : "none" }} | |
| className="w-6 h-6" | |
| alt="" | |
| /> | |
| <span className="text-xs mt-1">홈</span> | |
| </button> | |
| {navItems.map(({ path, label, icon }) => { | |
| const isActive = location.pathname === path; | |
| return ( | |
| <button | |
| key={path} | |
| className={`flex flex-col items-center ${isActive ? "text-blue-500" : "text-gray-500"}`} | |
| onClick={() => nav(path)} | |
| > | |
| <img | |
| src={icon} | |
| style={{ filter: isActive ? activeFilter : "none" }} | |
| className="w-6 h-6" | |
| alt={`${label} 아이콘`} | |
| /> | |
| <span className="text-xs mt-1">{label}</span> | |
| </button> | |
| ); | |
| })} |
| const match = useMatch("/"); | ||
|
|
||
| return ( | ||
| <div className=" md:max-w-[375px] flex items-center justify-between py-[11px] px-[16px] bg-white border-b-[1px] fixed top-0 w-full md:max-w-[375px] h-[56px] mx-auto"> |
There was a problem hiding this comment.
tailwind css에서 저도 계속 적응 안되던게 값 지정이었는데요 😅..
arbitary value로 [ ] 안에 넣어줘도 되지만 16px, 56px 등은 class로 적용 가능하니까 익숙해져보시는 것도 좋을 것 같아요!
| ); | ||
| }; | ||
|
|
||
| const DateTimeText = styled.div<{ isCurrentUser: boolean }>` |
There was a problem hiding this comment.
위에서는 테일윈드를 쓰셨는데 styled component와 혼용한 이유가 있으신가요?!
| const getProfilePic = (sender: string): string => { | ||
| return sender === "CEOS" ? "/Profile_image.svg" : "/Profile_image.svg"; | ||
| }; |
| <DateTimeText isCurrentUser={message.sender === currentUser.name}> | ||
| {formatDateTime(message.time)} | ||
| </DateTimeText> | ||
| )} | ||
| <MessageContainer | ||
| key={`${message.id}-${currentSender}`} | ||
| isCurrentUser={message.sender === currentUser.name} | ||
| > | ||
| {message.sender !== currentUser.name && ( | ||
| <img | ||
| src={getProfilePic(message.sender)} | ||
| alt="Profile" | ||
| className="w-8 h-8 rounded-full mr-2" | ||
| /> | ||
| )} | ||
|
|
||
| <MessageContent> | ||
| {message.sender === currentUser.name ? ( |
There was a problem hiding this comment.
message.sender === currentUser.name 이 계속 반복되고 있어서 const isCurrentUser = message.sender === currentUser.name; 와 같이 변수로 따로 빼주어서 활용하면 가독성이 더 좋을 것 같습니다!
배포링크
💗key question💗
동적 라우팅이란 실행 중일 때 라우트를 정의하고 관리하는 방식임. URL의 특정 부분(파라미터)을 동적으로 받아와서 다른 컴포넌트를 렌더링함. 예를 들어, "/post/:postId" 형태의 URL에서 postId에 따라 각기 다른 페이지 내용 보여줌.
언제 사용?
상품 상세 페이지: URL ID에 따라 다른 상품 정보 표시
유저 프로필 페이지: 유저 ID에 따라 다른 프로필 보여줌
게시판: 게시물 ID로 특정 게시물 페이지로 이동할 때 사용함.
네트워크 느릴 때 UX/UI 개선 전략 및 최적화 방법
네트워크가 느리면 사용자가 불편해할 가능성 큼. 이를 줄이기 위해 몇 가지 디자인 전략과 최적화 방법 사용 가능.
Skeleton UI: 로딩 중에 뼈대 화면 보여줌. 기다림 시간 최소화됨.
Lazy Loading & Progressive Loading: 필요한 데이터부터 먼저 로드해 유저가 빨리 볼 수 있게 함.
로딩 인디케이터: 로딩 중임을 알림.
프리로딩(Preloading): 이후 필요할 가능성 큰 리소스 미리 로드하여 다음 페이지 빠르게 전환함.
기술적 최적화 방법
코드 스플리팅(Code Splitting): 필요한 코드만 불러와 초기 로딩 속도 개선
이미지 최적화, CDN 활용: 이미지 용량 줄이고 CDN 통해 빠르게 제공
React.lazy와 Suspense: 컴포넌트 지연 로딩으로 초기 번들 크기 줄임
데이터 캐싱: React Query나 SWR 써서 데이터를 캐싱, 불필요한 네트워크 요청 줄임.
useState와 useReducer로 지역 상태 관리 / Context API, 전역 상태 관리 라이브러리 차이
상태 관리는 주로 지역 상태와 전역 상태로 나뉨. 필요와 목적에 따라 사용 방식 다름.
useState: 간단한 상태 관리에 사용. 상태랑 상태 변경 함수 쌍 반환, 컴포넌트 내에서 직접 관리함.
useReducer: 복잡한 상태 로직에 유리함. 액션이 여러 개고 상태가 서로 얽혀 있으면 유용함.
Context API
Context API는 props drilling을 방지하기 위한 전역 상태 관리 방식임. 부모가 자식에 직접 데이터를 전달할 수 있어서 여러 단계 거치지 않고도 공유 가능.
주 용도: 인증 상태, 테마, 유저 설정 등 애플리케이션 전반에 필요한 데이터에 적합.
제약: 상태 많아지고 복잡해지면 Context API만으론 관리 어려워질 수 있음.
전역 상태 관리 라이브러리 (Redux, MobX, Recoil 등)
전역 상태 관리 라이브러리는 상태 체계적으로 관리할 때 사용함. 큰 규모, 복잡한 앱에서 데이터 흐름과 상태 변화 추적 쉽게 해줌.
Redux: 액션, 리듀서, 스토어 통해 상태 변화를 예측 가능하게 관리. 한 곳에서 관리해 애플리케이션 전체 상태 추적 쉬움.
Recoil: 최신 상태 관리 라이브러리, 비동기 상태 관리나 컴포넌트별 전역 상태 관리에 유리하게 설계됨.