[항해플러스] WIL 8주차
HANGHAE-PLUS · 항해플러스

[항해플러스] WIL 8주차

· 6min read
시리즈

항해플러스

9 / 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 규칙 통일하는 데 시간 꽤 썼어요,,

배운 점

  1. 폴더를 보면 코드의 역할이 예측되는 구조가 좋은 구조
  • features/post-crud/ui/AddPostDialog.tsx → 게시물 추가 다이얼로그구나~!
  1. 단일 책임 원칙이 실제로 변경 범위를 줄여줌
  • PostRow 수정해도 PostsTable은 안 건드려도 됨
  1. Props Drilling 지옥에서 탈출
  • Zustand store로 필요한 상태만 구독
  • 불필요한 리렌더링 방지

여담

700줄 컴포넌트 보고 “어디서부터 건드려야 하지?” 싶었는데,, 작은 단위로 쪼개니까 각 컴포넌트가 하나의 역할만 해서 수정할 때 자신감이 생겼습니다.

FSD 처음엔 오버엔지니어링 아닌가? 싶었는데, 규모가 커질수록 진가를 발휘할 것 같아요. 새 프로젝트 시작할 때 FSD 구조 기본으로 잡고, 팀 컨벤션 또한 초기에 정하는 게 중요하다고 느꼈습니다.

여담

눈와서 신난다고 눈사람 만들다가 그대로 감기 걸렸습니다,, 다들 몸 안좋아보인다고 걱정해주셔서 감사하구요 이제 남은 시간이 정말 없는 만큼, 모두 아쉬워하시는 것 같더라구요. 10주 동안 함께해서 즐거웠고, 끝나고도 종종 만나는 관계가 되었으면 좋겠다는 생각을 많이들 하고 계신 것 같아요. 이런 관계가 된 것 만으로도 지난 항해가 너무 알차고 좋은 시간이었던 것 같습니다.