[항해플러스] WIL 8주차
항해플러스
- 01 [항해플러스] WIL 1주차: Chapter 1-1: 프론트엔드 테스트 코드 익숙해지기
- 02 [항해플러스] WIL 2주차: Chapter1-2. AI를 활용한 안정적인 기능 개발을 위한 TDD 적용
- 03 [항해플러스] WIL 3주차
- 04 [항해플러스] WIL 4주차
- 05 유난은 때로 철학이 된다.
- 06 [항해플러스] WIL 5주차
- 07 [항해플러스] WIL 6주차
- 08 [항해플러스] WIL 7주차
- 09 [항해플러스] WIL 8주차
- 10 [항해플러스] 9주차 회고
- 11 [항해플러스] 10주차 회고
- 12 [항해플러스] 최종 회고
항해99는 아예 다른 프로그램이라는걸 깨닫고 시리즈 및 태그를
항해플러스로 변경하였습니다.
개요
이번 주는 FSD(Feature-Sliced Design)를 적용하며 700줄짜리 컴포넌트와 싸운 주차였답니다
또방어
지난 주 발제 끝나고 방어를 먹으러 갔습니다
발제 때 7팀 가서 비쵸비도 뺏어왔습니다. 연욱님 잘 먹었습니다~~!
4팀 중간회고
이번 주는 우리끼리 각자 회고 + 서로에게 남겨주는 회고를 했습니다. 적다보니 제 개인적으로 스스로에게 너무 기준치를 높게 잡고 있다는 생각이 들었던 것 같습니다. 팀원들이 남겨준 리뷰를 기반으로 보니, 저의 강점으로 꼽아주신 것들에는
- AI 활용 능력
- 그리고 다방면으로 고민하고 이를 실천하는 실행력
에 대한 칭찬이 많았던 것 같아요.
더 발전하면 좋을 부분은
- 스스로 의심하지 않고 자신감 더 가졌으면 좋겠다.
- 조금 더 유연한 사람이 되었으면 좋겠다
- 과도한 애착 버리기
는 피드백이 있었어요~!
한 줄 평으로는, 매일 올려드리는 공지에 대한 좋은 평가가 있어 뿌듯했구요,, 마지막으로 제게 배우거나 느낄 점에서는
- 부지런함 + 적극성 + 다양한 관심사 에 대한 의견들이 있었네요.
정성스러운 리뷰 감사합니다 4팀 여러분~~! 리뷰 보면서 생각보다 내가 잘 해내고 있을지도 모르겠다는 생각이 들었던 것 같습니다.
과제
목표
전역상태관리를 이용한 적절한 분리와 계층에 대한 이해를 통한 FSD 폴더 구조 적용하기
700줄짜리 PostsManagerPage.tsx를 가장 작은 단위로 줄이는 것이 목표였구요,,
결론부터 말하면 성공했습니다!
FSD가 뭔데?
Feature-Sliced Design의 약자구요, 소련 친구들이 만든 프론트엔드 아키텍처 방법론이랍니다. 핵심은 레이어 기반 구조와 단방향 의존성입니다.
app → pages → widgets → features → entities → shared
상위 레이어는 하위 레이어만 import 가능해요. 거꾸로 가면 안된다고 해요..?
각 레이어가 뭘 하는데?
| 레이어 | 역할 | 예시 |
|---|---|---|
| shared | 비즈니스 로직 없는 재사용 코드 | Button, Input, Dialog |
| entities | 비즈니스 엔티티 (데이터 모델) | Post, Comment, User |
| features | 사용자 액션 | AddPostDialog, SearchInput |
| widgets | 독립적 UI 블록 (entities + features 조합) | PostsTable, CommentsSection |
| pages | 라우팅 진입점 | PostsManagerPage |
| app | 앱 초기화, 프로바이더 | App.tsx |
실제로 적용한 구조
src/
├── app/ui/ # App Shell (Header, Footer)
├── pages/ # URL ↔ Store 동기화만
│ └── PostsManagerPage.tsx # 700줄 → 66줄
├── widgets/ # 독립적 UI 블록
│ ├── posts-table/
│ ├── comments-section/
│ └── pagination/
├── features/ # 사용자 액션
│ ├── post-crud/ # AddPostDialog, EditPostDialog
│ ├── comment-crud/
│ ├── search-posts/ # SearchInput, TagFilter
│ └── user-modal/
├── entities/ # 비즈니스 엔티티
│ ├── post/
│ │ ├── api.ts
│ │ ├── types.ts
│ │ ├── model/store.ts # Zustand store
│ │ └── ui/PostRow.tsx
│ ├── comment/
│ ├── user/
│ └── tag/
└── shared/ # 공통 유틸리티
├── api/client.ts
├── lib/highlightText.tsx
├── model/uiStore.ts
└── ui/ # Button, Input, Dialog...
Before vs After
Before (700줄)
const PostsManagerPage = () => {
// 20+ useState
const [posts, setPosts] = useState([])
const [comments, setComments] = useState([])
const [showAddDialog, setShowAddDialog] = useState(false)
// ... 17개 더
// 모든 API 호출 함수
const fetchPosts = async () => { ... }
const addPost = async () => { ... }
// ... 10개 더
// 모든 렌더 함수
const renderPostRow = () => { ... }
const renderComments = () => { ... }
// ... 끝도 없음
return (
// 500줄짜리 JSX
)
}
After (66줄)
const PostsManagerPage = () => {
// URL ↔ Store 동기화만
return (
<Card>
<CardHeader>
<CardTitle>게시물 관리자</CardTitle>
</CardHeader>
<CardContent>
<PostsTable />
</CardContent>
<AddPostDialog />
<EditPostDialog />
<PostDetailDialog />
<UserModal />
</Card>
);
};
삽질 포인트
1. fetch 책임 충돌
처음에 features에서도 fetch 하고, widgets에서도 fetch 해서 요청이 두 번 갔었어요,,
features: 상태만 변경 (fetch ❌) widgets: fetch orchestration 담당
이렇게 책임을 단일화하니까 깔끔해졌습니다.
widgets에서 fetch orchestration을 담당하는 것이 FSD 원칙에 맞는 방식인지 궁금합니다.
이 부분은 별도로 PR에 위의 질문을 남겼고, 아래의 답변(일부만 발췌)을 받았습니다.
맞아요. widgets는 entities와 features를 조합해서 완성된 UI 블록을 만드는 레이어니까, 데이터를 가져와서 뿌려주는 역할을 하는 게 자연스럽습니다.
2. widgets vs features 구분
“CommentsSection은 features야 widgets야?” 고민이 있었는데요,,
독립적인 UI 블록이면 widgets 사용자 액션(버튼 클릭, 폼 제출)이면 features
이 기준으로 정리했습니다.
3. 코드 스타일 통일
- import 확장자 (.ts vs .tsx)
- 세미콜론 유무
- 중복 import (
import * as React+import { forwardRef })
이런 것들 때문에 ESLint/Prettier 규칙 통일하는 데 시간 꽤 썼어요,,
배운 점
- 폴더를 보면 코드의 역할이 예측되는 구조가 좋은 구조
features/post-crud/ui/AddPostDialog.tsx→ 게시물 추가 다이얼로그구나~!
- 단일 책임 원칙이 실제로 변경 범위를 줄여줌
- PostRow 수정해도 PostsTable은 안 건드려도 됨
- Props Drilling 지옥에서 탈출
- Zustand store로 필요한 상태만 구독
- 불필요한 리렌더링 방지
여담
700줄 컴포넌트 보고 “어디서부터 건드려야 하지?” 싶었는데,, 작은 단위로 쪼개니까 각 컴포넌트가 하나의 역할만 해서 수정할 때 자신감이 생겼습니다.
FSD 처음엔 오버엔지니어링 아닌가? 싶었는데, 규모가 커질수록 진가를 발휘할 것 같아요. 새 프로젝트 시작할 때 FSD 구조 기본으로 잡고, 팀 컨벤션 또한 초기에 정하는 게 중요하다고 느꼈습니다.
여담
눈와서 신난다고 눈사람 만들다가 그대로 감기 걸렸습니다,, 다들 몸 안좋아보인다고 걱정해주셔서 감사하구요 이제 남은 시간이 정말 없는 만큼, 모두 아쉬워하시는 것 같더라구요. 10주 동안 함께해서 즐거웠고, 끝나고도 종종 만나는 관계가 되었으면 좋겠다는 생각을 많이들 하고 계신 것 같아요. 이런 관계가 된 것 만으로도 지난 항해가 너무 알차고 좋은 시간이었던 것 같습니다.


![[항해플러스] 최종 회고](https://velog.velcdn.com/images/suadesu/post/2d89266f-dc6a-43c9-a491-1996be5f2c2a/image.png)
![[항해플러스] 9주차 회고](https://velog.velcdn.com/images/suadesu/post/a6fab038-cc4c-4401-96c4-c566d575081d/image.jpeg)