Skip to content

Conversation

@totter15
Copy link

@totter15 totter15 commented Dec 8, 2025

배포링크

https://totter15.github.io/front_7th_chapter3-3/

과제 체크포인트

기본과제

목표 : 전역상태관리를 이용한 적절한 분리와 계층에 대한 이해를 통한 FSD 폴더 구조 적용하기

  • 전역상태관리를 사용해서 상태를 분리하고 관리하는 방법에 대한 이해
  • Context API, Jotai, Zustand 등 상태관리 라이브러리 사용하기
  • FSD(Feature-Sliced Design)에 대한 이해
  • FSD를 통한 관심사의 분리에 대한 이해
  • 단일책임과 역할이란 무엇인가?
  • 관심사를 하나만 가지고 있는가?
  • 어디에 무엇을 넣어야 하는가?

체크포인트

  • 전역상태관리를 사용해서 상태를 분리하고 관리했나요?
  • Props Drilling을 최소화했나요?
  • shared 공통 컴포넌트를 분리했나요?
  • shared 공통 로직을 분리했나요?
  • entities를 중심으로 type을 정의하고 model을 분리했나요?
  • entities를 중심으로 ui를 분리했나요?
  • entities를 중심으로 api를 분리했나요?
  • feature를 중심으로 사용자행동(이벤트 처리)를 분리했나요?
  • feature를 중심으로 ui를 분리했나요?
  • feature를 중심으로 api를 분리했나요?
  • widget을 중심으로 데이터를 재사용가능한 형태로 분리했나요?

심화과제

목표: 서버상태관리 도구인 TanstackQuery를 이용하여 비동기코드를 선언적인 함수형 프로그래밍으로 작성하기

  • TanstackQuery의 사용법에 대한 이해
  • TanstackQuery를 이용한 비동기 코드 작성에 대한 이해
  • 비동기 코드를 선언적인 함수형 프로그래밍으로 작성하는 방법에 대한 이해

체크포인트

  • 모든 API 호출이 TanStack Query의 useQuery와 useMutation으로 대체되었는가?
  • 쿼리 키가 적절히 설정되었는가?
  • fetch와 useState가 아닌 선언적인 함수형 프로그래밍이 적절히 적용되었는가?
  • 캐싱과 리프레시 전략이 올바르게 구현되었는가?
  • 낙관적인 업데이트가 적용되었는가?
  • 에러 핸들링이 적절히 구현되었는가?
  • 서버 상태와 클라이언트 상태가 명확히 분리되었는가?
  • 코드가 간결하고 유지보수가 용이한 구조로 작성되었는가?
  • TanStack Query의 Devtools가 정상적으로 작동하는가?

최종과제

  • 폴더구조와 나의 멘탈모데일이 일치하나요?
  • 다른 사람이 봐도 이해하기 쉬운 구조인가요?

과제 셀프회고

fsd에 대해 설명만 읽었을때는 어떤기준을 사용해서 분리하는건지 알것 같았는데 막상 사용해보니 모호한 컴포넌트들, hook들이 생기면서 이게 feature인가? entities인가? 에 대해 계속 고민하게 되었던것 같습니다.

폴더를 나눈 기준

shared

  • 비지니스 로직이 없는 공통 ui, util등

entities

  • 도메인 데이터
  • /api: 도메인 데이터에 대한 API
  • /model: 도메인 type, 도메인 데이터 조회 query, 상태만 있을경우

features

  • 사용자의 단일 행위(댓글수정, 게시글 추가등)
  • /model: 행위에 대한 query(추가, 수정, 삭제등), feature/ui의 상태 store
  • /ui: 단일 행위에 대한 독립적인 컴포넌트(댓글 수정 Dialog, 댓글 추가 Dialog)

widgets

  • 여러 복합적인 기능을 가진 독립적인 컴포넌트
  • /model: widget/ui에서 사용되는 hook
  • /ui: features가 조합되어 복합적인 기능을 가진 독립적인 컴포넌트(Comments, DetailPostDialog 등...)
image

최종적으로는 아래의 폴더 구조를 만들었습니다.

./src
├── App.tsx
│   ├── comment
│   │   ├── api
│   │   │   ├── addCommentApi.ts
│   │   │   ├── deleteCommentApi.ts
│   │   │   ├── getCommentsApi.ts
│   │   │   ├── likeCommentApi.ts
│   │   │   └── updateCommentApi.ts
│   │   └── model
│   │       ├── comment.ts
│   │       ├── useCommentsQuery.ts
│   │       └── useSelectedCommentStore.ts
│   ├── post
│   │   ├── api
│   │   │   ├── addPostApi.ts
│   │   │   ├── deletePostApi.ts
│   │   │   ├── getPostsApi.ts
│   │   │   ├── getSearchPostsApi.ts
│   │   │   ├── getTagPostsApi.ts
│   │   │   └── updatePostApi.ts
│   │   └── model
│   │       ├── post.ts
│   │       ├── usePostQuery.ts
│   │       ├── useSearchPostsQuery.ts
│   │       ├── useSelectedPostStore.ts
│   │       └── useTagPostsQuery.ts
│   ├── tag
│   │   ├── api
│   │   │   ├── getTagApi.ts
│   │   │   └── getTagsApi.ts
│   │   └── model
│   │       ├── tag.ts
│   │       └── useTagQuery.ts
│   └── user
│       ├── api
│       │   ├── getUserApi.ts
│       │   └── getUsersApi.ts
│       └── model
│           ├── useSelectedUserStore.ts
│           ├── useUserQuery.ts
│           ├── useUsersQuery.ts
│           ├── user.dto.ts
│           ├── user.mapper.ts
│           └── user.ts
├── features
│   ├── comment
│   │   ├── model
│   │   │   ├── useAddCommentDialogStore.ts
│   │   │   ├── useAddCommentQuery.ts
│   │   │   ├── useDeleteCommentQuery.ts
│   │   │   ├── useEditCommentDialogStore.ts
│   │   │   ├── useLikeCommentQuery.ts
│   │   │   └── useUpdateCommentQuery.ts
│   │   └── ui
│   │       ├── AddCommentDialog.tsx
│   │       └── EditCommentDialog.tsx
│   ├── post
│   │   ├── model
│   │   │   ├── useAddPostDialogStore.ts
│   │   │   ├── useAddPostQuery.ts
│   │   │   ├── useDeletePostQuery.ts
│   │   │   ├── useEditPostDialogStore.ts
│   │   │   ├── useEditPostQuery.ts
│   │   │   ├── usePostParams.ts
│   │   │   └── usePostWithParams.ts
│   │   └── ui
│   │       ├── AddPostDialog.tsx
│   │       └── EditPostDialog.tsx
│   └── user
│       ├── model
│       │   └── useUserDialogStore.ts
│       └── ui
│           └── UserDialog.tsx
├── pages
│   └── PostManager
│       └── PostsManagerPage.tsx
├── shared
│   ├── api
│   │   └── apiClient.ts
│   └── ui
│       ├── Button.tsx
│       ├── Card.tsx
│       ├── Dialog.tsx
│       ├── Footer.tsx
│       ├── Header.tsx
│       ├── HighlightText.tsx
│       ├── Input.tsx
│       ├── Select.tsx
│       ├── Table.tsx
│       ├── Textarea.tsx
│       └── index.tsx
└── widgets
    ├── comment
    │   └── ui
    │       ├── CommentItem.tsx
    │       └── Comments.tsx
    └── post
        ├── model
        │   ├── useDetailPostDialogStore.ts
        │   └── usePostTable.tsx
        └── ui
            ├── DetailPostDialog.tsx
            ├── PostContent.tsx
            └── PostTable.tsx

이번 과제를 통해 이전에 비해 새롭게 알게 된 점이 있다면 적어주세요.

api dto와 ui의 type이 다를때 mapper를 사용하는법
api에서 user데이터를 조회했을때 필요없는 데이터가 너무 많이 조회되었습니다.
이걸 ui에서 쓰기엔 불필요하다 생각이 들었는데 찾아보니 mapper라는 중간 함수를 이용해서 dto를 ui에서 사용하는 props만 return하게 하는 방식이 있음을 알게되었습니다.

//GetUserResponse => User만 가져와서 사용

export const mapUserDtoToUser = (dto: GetUserResponse): User => ({
  id: dto.id,
  firstName: dto.firstName,
  lastName: dto.lastName,
  age: dto.age,
  email: dto.email,
  phone: dto.phone,
  username: dto.username,
  address: dto.address,
  company: dto.company,
})

entities와 feature의 차이

과제 시작전 entities에 api함수가 들어가는지? 들어간다면 조회하는 api만 들어가는지? 수정,삭제등의 api도 들어가는지?에 대한 고민이 있었습니다. 이에 대한 기준을 세웠는데 기본적인 데이터api는 entities에 실제로 해당 데이터를 저장하고 상태로 관리하는건 useQuery를 통해 관리되기에 해당 api를 통한 상태관리를 하는 query는 features/api에 넣었습니다.
selectedPost/selectedUser에 대해서도 상태관리 store를 따로 뒀는데 이둘 또한 선택된 게시글, 선택된 유저의 상태라는 entity라 생각되어 entities 계층에 두었습니다.

feature과 widgets의 차이

feature은 하나의 기능 widgets는 여러개의 기능이 복합적으로 사용됨을 기준으로 폴더를 분리했습니다.

아직은 막연하다거나 더 고민이 필요한 부분을 적어주세요.

원래 action함수들을 props으로 내려주는 형식으로 코드를 많이 작성하는데 이번에는 하나의 기능을 가진 컴포넌트가 되도록 내부에서 hook을 호출하는 방식으로 작업을 진행했습니다. 이렇게 했을때 이 컴포넌트가 외부에 의존하지 않으니 어디든 이동하기엔 쉬운데 이 컴포넌트 자체를 테스트하기 어려운 형태가 아닐까 생각이 듭니다. 어떨때에 props로 내려주고 어떨때에 내부 hook을 이용할지에 대해 좀더 고민이 필요할 것 같습니다.

챕터 셀프회고

클린코드와 아키테쳑 챕터 함께 하느라 고생 많으셨습니다!
지난 3주간의 여정을 돌이켜 볼 수 있도록 준비해보았습니다.
아래에 적힌 질문들은 추억(?)을 회상할 수 있도록 도와주려고 만든 질문이며, 꼭 질문에 대한 대답이 아니어도 좋으니 내가 느꼈던 인사이트들을 자유롭게 적어주세요.

클린코드: 읽기 좋고 유지보수하기 좋은 코드 만들기

  • 더티코드를 접했을 때 어떤 기분이었나요? ^^; 클린코드의 중요성, 읽기 좋은 코드란 무엇인지, 유지보수하기 쉬운 코드란 무엇인지에 대한 생각을 공유해주세요

결합도 낮추기: 디자인 패턴, 순수함수, 컴포넌트 분리, 전역상태 관리

  • 거대한 단일 컴포넌트를 봤을때의 느낌! 처음엔 막막했던 상태관리, 디자인 패턴이라는 말이 어렵게만 느껴졌던 시절, 순수함수로 분리하면서 "아하!"했던 순간, 컴포넌트가 독립적이 되어가는 과정에서의 깨달음을 들려주세요

응집도 높이기: 서버상태관리, 폴더 구조

  • "이 코드는 대체 어디에 둬야 하지?"라고 고민했던 시간, FSD를 적용해보면서의 느낌, 나만의 구조를 만들어가는 과정, TanStack Query로 서버 상태를 분리하면서 느낀 해방감(?)등을 공유해주세요

리뷰 받고 싶은 내용이나 궁금한 것에 대한 질문

  1. 어떤경우에 컴포넌트에서 props로 내려받고 어떤경우에 hook으로 기능을 내부에 구현하면 좋을까요? 이번과제에서는 feature/ui, widget/ui는 모두 내부에 hook으로 함수를 호출해서 독립적인 컴포넌트가 될수있도록 만들어 주었습니다. 하지만 이렇게 하니 테스트하기가 어려워 지는게 아닌가란 생각이 듭니다.

  2. 제가 잡은 fsd 폴더 기준이 통용적으로 사용되는 기준일까요? 아니면 실제로 제가 세운 기준과는 다르게 사용이 될까요?

천진아 added 2 commits December 8, 2025 19:33
@totter15 totter15 changed the title init [1팀 천진아] Chapter 3-3 기능 중심 아키텍처와 프로젝트 폴더 구조 Dec 8, 2025
천진아 and others added 16 commits December 10, 2025 22:45
- entities 레이어에 store 및 비즈니스 로직 이동
- widgets UI 컴포넌트를 ui 폴더로 재구성
- dialog 상태 관리를 위한 store 추가 (useDetailPostDialogStore, useUserDialogStore)
- PostsManagerPage에서 테이블 로직을 widgets로 분리 (usePostTable)
- features와 entities 간 역할 분리 개선
- Post 관련 파라미터 관리 hooks를 features로 이동
- DetailPostDialog 상태 관리를 widgets로 이동
- PostParams 타입을 entities로 분리
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.

1 participant