Skip to content

[4주차] 송아영 미션 제출합니다. #12

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

Open
wants to merge 82 commits into
base: master
Choose a base branch
from

Conversation

gustn99
Copy link

@gustn99 gustn99 commented Apr 23, 2025

배포 링크

https://react-messenger-21th-git-gustn99-gustns-projects.vercel.app/

과제를 하며...

  • 요 근래 부쩍 테일윈드와 가까워졌습니다.이 정도 편리함을 누리기 위해 html이 조금 더러워지는 것쯤은 충분히 감안할 수 있을 것 같습니다.
  • zustand를 거의 처음 써 보는데 상태와 관련 로직을 한 곳에서 관리한다는 점이 좋았습니다. 굳이 전역으로 관리할 필요가 없어 보였던 메시지 내역도 어쩌다 보니 zustand로 관리하게 되었는데요, 로직들이 store 파일 안으로 모두 들어가면서 매우매우 간단해져 마음에 들었습니다. persist를 사용하면 로컬스토리지에 저장하기도 간편하고요!
  • 그런데 뭔가의 초기화 이슈를 맞딱드렸습니다. recoil을 사용할 때는 초기화에 대한 예외처리를 해 본 적이 없는 것 같은데, 새로고침을 하면 json에서 데이터를 읽어 초기화하는 로직과 로컬스토리지에서 데이터를 읽어 덮어씌우는 로직이 충돌하여 json의 데이터로 값이 계속 초기화되더라고요. recoil을 사용한 것도 오래 전이라 기억이 틀린 걸지도 모르지만,,, 조금 더 깊이 알아보려고 합니다.
  • 혼자 하는 개발은 별로 재미있지 않은 것 같습니다. . . 디자이너 분이 세오스를 나가시게 되면서 4주차 과제를 하는 동안 쓸쓸했네요 ㅠ. 그 동안 제 얘기를 들어주던 백, 프론트, 디자이너 분들의 소중함을 느꼈어요. . .
  • 그런 핑계로... 채팅방은 하나밖에 활성화되어 있지 않습니다... 클릭이 안 되는 것은 버그가 아니고, 호버에 반응하는 그 채팅방만,,, 활성화된 것이 맞습니다,,, 여유가 되면 추가로 개발해 보겠습니다
  • 모바일 최적화 프로젝트라는 것에 맞춰서 호버보다는 액티브 상태에 대한 css를 추가했습니다!

Key Questions

1. React Router의 동적 라우팅(Dynamic Routing)이란 무엇이며, 언제 사용하나요?

  • 동적 라우팅은 경로의 일부분을 마치 변수처럼 사용할 수 있는 기능입니다.
  • 상세 페이지나 유저(프로필) 페이지처럼 id를 기반으로 데이터를 불러와야 하는 경우에 주로 사용합니다.
  • 또는 어떤 페이지들이 서로 높은 의존성과 유사성을 가지는 경우 각각을 별도의 페이지로 분리하는 대신 동적 라우팅을 사용해 특정 부분만 분기하는 식으로 처리할 수 있습니다.

2. 네트워크 속도가 느린 환경에서 사용자 경험을 개선하기 위해 사용할 수 있는 UI/UX 디자인 전략과 기술적 최적화 방법은 무엇인가요?

  • UI/UX 디자인 전략: 로딩바, 스켈레톤UI 등 사용자에게 웹이 멈추지 않고 응답 요청 중이라는 메시지를 전달할 수 있는 요소들
  • 기술적 최적화: 이미지 lazy loading, 코드 스플리팅 등 전체 페이지가 로딩되기를 기다리는 대신 당장 로드된 데이터를 먼저 보여주거나 반복되는 요청에 대해 데이터를 캐싱해 응답 속도를 높일 수도 있음

3. React에서 useState와 useReducer를 활용한 지역 상태 관리와 Context API 및 전역 상태 관리 라이브러리의 차이점을 설명하세요.

지역 상태 관리

  • useState나 useReducer는 다른 컴포넌트로 state를 공유하려면 props를 통해 전달해야 합니다.
  • 그 중 useReducer는 상태와 상태 관련 로직들을 한 곳에서 관리하기 때문에 자주 Context API와 함께 사용되는 것 같습니다.
  • 만일 동일 레벨의 컴포넌트 간에 state 공유가 필요한 경우 그들의 공통된 부모 컴포넌트에서 state를 선언하고 각각 props로 전달해야 합니다.
  • props를 통해 state를 전달하는 과정에서 실제로 state를 사용하지 않음에도 거쳐가는 컴포넌트가 생길 수 있습니다.
  • 관여하는 컴포넌트가 많아지면서 복잡도가 증가하는 상태를 props drilling이라고 합니다.
  • 학습 초기에는 drilling을 얼마나 경계해야 하나 감을 잘 못 잡았는데, 저는 컴포넌트가 아주 복잡하지 않은 이상 3개의 컴포넌트 간 props 전달까지는 그냥 작성하는 편입니다. 아무래도 다른 라이브러리보다는 useState가 가장 익숙하니까요,,,

전역 상태 관리

  • 반면 Context API나 다른 전역 상태 관리 라이브러리는 이러한 의존성으로부터 비교적 자유롭습니다. 원하는 컴포넌트에서 원하는 state를 사용할 수 있습니다.
  • 하지만 특히 Context API 같은 경우에는 value가 변경될 때 Provider 내부의 컴포넌트들이 리렌더링되므로 성능 측면에 신경 써야 합니다. useReducer와 함께 사용하는 경우에도 이 부분에 주의해야 합니다.
  • 이번 과제에서도 그렇고 최근에는 단순히 state 공유만의 문제가 아니라, 로직 작성의 편리성 측면에서 전역 상태 관리의 장점들을 많이 느끼고 있습니다.

gustn99 added 30 commits March 25, 2025 12:08
gustn99 added 29 commits April 10, 2025 22:13
Copy link

@Programming-Seungwan Programming-Seungwan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

멋진 과제 진행하시느라 고생 많으셨습니다.

Comment on lines +1 to +24
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yarn 패키지 매니저를 이용해서 의존성을 관리하고 있어요. yarn 1 버전을 이용하면 node_modules에 의존성을 설치할 수 있지만 그렇지 않은 사용자들이 있으니 .yarn 도 추가해줍시다

Comment on lines +3 to +10
import Frame from './components/Frame';
import Chats from './pages/ChatList';
import ChatRoom from './pages/ChatRoom';

import '@/styles/global.css';
import Home from './pages/Home';
import Profile from './pages/Profile';

Copy link

@Programming-Seungwan Programming-Seungwan Apr 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

관련 ts 옵션으로 절대 경로를 만들어주신 것 같은데, @component/ 방식으로도 쓸 수 있게 더 설정하셔서 코드 더 깔끔하게 유지할 수 있을 것 같습니다

Comment on lines +1 to +59
import { getRecentActiveFriends } from '@/apis/getRecentActiveFriends';
import MainTopBar from '@/components/MainTopBar';
import RecentActiveFriend from '@/components/RecentActiveFriend';
import { getAllFriends } from '@/apis/getAllFriends';
import FriendsItem from './components/FriendItem';
import DefaultProfile from '@/components/DefaultProfile';
import { getCurrentUserInfo } from '@/apis/getCurrentUserInfo';
import { getProfileColor } from '@/utils/getProfileColor';
import Divider from '@/components/Divider';
import NavBar from '@/components/NavBar';
import { useNavigate } from 'react-router-dom';

export default function Home() {
const nav = useNavigate();

const friends = getAllFriends();
const recentActiveFriends = getRecentActiveFriends();
const currentUserInfo = getCurrentUserInfo();

const currentUserBgColor = getProfileColor('background', currentUserInfo?.color || 'blue');
const currentUserPathColor = getProfileColor('path', currentUserInfo?.color || 'blue');

return (
<div>
<MainTopBar content="친구" />

<div className=" h-[38.5rem] overflow-scroll no-scrollbar">
<button
onClick={() => nav('/profile')}
className="flex w-full gap-2.5 items-center px-5 py-[1.125rem] head2-semibold
active:bg-black-100 transition-colors"
>
<DefaultProfile isMyProfile={true} bgColor={currentUserBgColor} pathColor={currentUserPathColor} />
{currentUserInfo?.name || '알수없음'}
</button>

<Divider />
<div className="text-black-500 caption1-medium mx-5 mt-1 mb-2">최근 접속한 친구</div>

<div className="px-5 py-2.5 flex gap-2 overflow-scroll no-scrollbar">
{recentActiveFriends.map((friend) => (
<RecentActiveFriend key={friend.id} {...friend} />
))}
</div>

<Divider />
<div className="text-black-500 caption1-medium mx-5 mt-1 mb-2">친구 {friends.length}</div>

<div className="pb-2.5">
{friends.map((friend) => (
<FriendsItem {...friend} />
))}
</div>
</div>

<NavBar />
</div>
);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 index.tsx 파일에서 컴포넌트 코드를 작성해주시고 있어요. js는 특정 디렉터리를 파일 명시 없이 import 하면 index.js를 가져오니까, 아예 Home 디렉터리 아래에 HomePage.tsx 컴포넌트에 지금 코드를 작성해두고 index.ts에서는 export * from "./Homepage." 구문만 써주면 프로젝트 내 다른 파일에서 편리하게 import할 수 있을 것 같아요.

email: string;
color: Color;
isActive: boolean;
lastActiveAt: string;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

실제 사용되는 코드를 보면, 단순 문자열이라기보다는 Date 객체가 들어가는 것 같아요. 따라서 타입도 Date로 적용해주시는 것은 어떨까요?

Comment on lines +1 to +22
export interface MessageDto {
id: string;
content: string;
createdAt: string;
fromUser: UserDto;
}

export interface ChatRoomDto {
chatRoomId: number;
chatRoomName: string | null;
joinedUsers: UserDto[];
latestMessage: MessageDto;
}

export interface UserDto {
id: number;
name: string;
email: string;
color: Color;
isActive: boolean;
lastActiveAt: string;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

전체적으로 Dto 라는 이름으로 타입(인터페이스라고도 하지만요)을 만들어 주셨는데, Dto는 사실 백엔드와 프론트엔드 간에 왔다 갔다하는 데이터를 의미하는 것에 더 가깝다고 생각해요.
물론 본 과제가 실제 백엔드 연동까지 함께한다면 의도하신 것처럼 apis 디렉터리에 넣는 것은 참 좋은 생각이에요! 그런데 dto는 대체로 백엔드 애플리케이션에서 프론트로 보낼 데이터의 타입을 네이밍할 때 많이 쓴다는 것을 알아두시면 좋을 것 같습니다.

content,
}: {
opacity: number;
Icon: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 작은 한 줄의 코드가 정말 중요하다는 것을 말씀드리고 싶어요. FC 같은 타입도 정말 중요하고, SVGProps 같은 타입이 굉장히 중요하거든요. 현재 에셋으로 가지고 있는 svg 파일들이 그대로 바로 react 컴포넌트로 적용되는데 어떻게 가능한지 svgr이라는 주제를 중심으로 살펴보셔도 좋습니다.

Comment on lines +43 to +45
<div>
<GroupProfile bgColors={bgColors} pathColors={pathColors} />
</div>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

굳이 불필요하게 div 요소를 추가하기 보다는 빈 <></> 이나 </Fragment/> 내부에 컴포넌트 코드를 넣으셔도 될 것 같습니다.

Comment on lines +1 to +40
import Profile from '@/assets/icons/profile.svg?react';
import clsx from 'clsx';

export default function DefaultProfile({
bgColor,
pathColor,
isMyProfile = false,
hasBorder = true,
profileImgUrl,
isActive,
}: {
bgColor: string;
pathColor: string;
isMyProfile?: boolean;
hasBorder?: boolean;
profileImgUrl?: string;
isActive?: boolean;
}) {
const size = isMyProfile ? 30 : 27;
return (
<div
className={clsx(
'relative flex justify-center items-center border rounded-full',
isMyProfile ? 'w-11 h-11' : 'w-9 h-9',
hasBorder ? 'border-black-100' : 'border-transparent',
bgColor,
)}
>
{profileImgUrl ? <div></div> : <Profile width={size} height={size} className={pathColor} />}
{isActive !== undefined && (
<div
className={clsx(
'absolute right-0.5 -bottom-0.5 w-2.5 h-2.5 border-2 border-black-50 rounded-full',
isActive ? 'bg-main-400' : 'bg-black-400',
)}
></div>
)}
</div>
);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 DefaultProfile 컴포넌트 뿐만 아니라 동일 레벨의 Divider 등의 컴포넌트들은 여기저기에서 쓰이고 있죠? 아예 ui-common 이라는 디렉터리 내부에 위치시키는 것이 협업 시에 더 좋지 않을까 싶어요

}

export const useMessagesStore = create<MessagesStore>()(
persist(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나중에도 브라우저를 열때 데이터가 남아있도록 zustand-persist를 사용해주셨군요! 지금은 프론트엔드 솔로 프로젝트라 이 방식을 택하지만, 사실 채팅 내역은 중요한 정보잖아요. 이런 건 원래 백엔드가 디비에서 잘 관리할 일이고 인프라, 백 코드 개선 등으로 속도는 알아서 개선할 일이니 마음껏 짬을 때립시다.

return (
<div className="sticky px-5 py-2.5 grid grid-cols-[4.75rem_auto_4.75rem] items-center">
<button
onClick={() => nav(-1)}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 잠깐 탭을 닫아버리고 주소만 복사해서 바로 다시 들어갔는데, 이때에는 History가 없잖아요. 그런 경우에는 뒤로 돌아갈 페이지가 없는 이상한 동작이 발생할 수도 있습니다. 이 조건도 검사해서 아예 버튼을 deactivate 해주거나 합시다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants