Skip to content

프로젝트 구조 개선

Choi Jeongmin edited this page Jan 23, 2025 · 2 revisions

📄 프로젝트 구조 개선: FSD 도입

기존 프로젝트 폴더 구조는 Feature-Sliced Design를 어느정도 차용한 상태로 진행했으나, 확장 과정에서 복잡해지면서 유지보수 및 확장성이 저하되는 문제가 발생했습니다.

이를 해결하기 위해 FSD를 엄격하게 적용하고, 역할과 책임에 따라 폴더 구조를 재설계함으로써 높은 응집도와 낮은 결합도를 달성하였습니다.

이 문서는 프로젝트 구조 개선 과정을 기록하고, 이를 통해 얻은 인사이트와 성과를 공유합니다.

🧩 배경 및 필요성

  • 프로젝트 확장으로 인한 복잡성 증가

    프로젝트가 빠르게 성장하면서, 기존 폴더 구조는 점차 모호해지고 중복되는 기능이나 위치가 생기는 문제가 발생했습니다.

  • 일관성 부족

    유사한 기능임에도 폴더 위치나 구조가 상이해 혼동이 잦았고, 유지보수가 어려워졌습니다.

  • 역할 구분 모호

    동일하거나 유사한 기능이 여러 군데에서 산발적으로 구현되어, 재사용성이 낮고 코드 충돌 가능성이 높았습니다.

  • 효율성 및 확장성 확보

    기능을 분담하고 빠른 대응이 가능한 구조가 필요하다고 판단했습니다. 특히 앞으로 추가될 AI 기능이나 요구사항 변경에 유연하게 대응하기 위한 구조적 정비가 절실했습니다.

🔍 기술적 분석 및 비교

1. 기존 구조 vs Feature-Sliced Design(FSD)

  • 기존 구조
    • 장점: 초기 개발 속도가 빠르고, 작게 시작할 때는 직관적
    • 단점: 기능별로 모듈화되지 않고 레이어가 뒤섞여, 확장 시 충돌 잦음
  • FSD 구조
    • 장점: 기능(Feature) 단위로 모델, UI, 비즈니스 로직(api 등)을 엄격히 구분해 높은 응집도와 낮은 결합도 달성
    • 단점: 구조적 설계가 미흡할 경우 오히려 복잡도가 증가할 우려가 있음

2. Layered Architecture 와의 조합

  • 레이어드 아키텍처를 적용해 데이터(layer)와 행위(기능) 레이어를 분리하고, 이를 상위에서 조합하는 방식을 채택했습니다.
  • 예시
    • app 레이어: 앱 전역 설정 (라우팅, 전역 상태 등)
    • entities 레이어: 핵심 엔티티의 데이터 모델과 UI
    • features 레이어: 기능(Feature) 별 세부 구현(모델, api, ui)
    • shared 레이어: 공통 모듈 및 재사용 가능한 컴포넌트

3. 참고 자료

🗺️ 문제 해결 과정

1. 문제 진단 및 재설계 방향 수립

  • 폴더 구조 분석: 기존에 components, features, pages 등이 중첩되면서, 어디에 어떤 책임이 있는지 모호하다고 판단.
  • 재설계 원칙
    1. 역할 단위 분리
    2. 규칙의 일관성 유지
    3. 기능 내 완결성

2. 새로운 FSD 기반 폴더 구조

아래는 개선된 구조 예시입니다.

perl
복사
apps/client/src/
├── app
│   └── config
├── entities
│   └── session
│       ├── model
│       └── ui
├── features
│   ├── auth
│   │   ├── api
│   │   ├── model
│   │   └── ui
│   ├── close-question
│   │   └── api

... 생략 ...

│   ├── terminate-session
│   │   ├── api
│   │   └── ui
│   └── update-session-host
│       └── api
├── pages
│   ├── home
│   │   └── ui
│   ├── my
│   │   └── ui
│   └── session
│       ├── model
│       └── ui
├── routes
│   └── session
│       └── $sessionId
│           └── $questionId
├── shared
│   ├── model
│   │   └── validation-status
│   └── ui
│       ├── InputField
│       ├── button
│       ├── modal
│       └── toast
│           ├── model
│           └── ui
└── widgets
    ├── chatting-list
    ├── header
    ├── question-list
    │   └── ui
    └── reply-list
        └── ui
  • app: 앱을 실행하는 모든 것 - 라우팅, 진입점, 전역 스타일, 프로바이더.
  • pages: 전체 페이지 또는 중첩 라우팅에서 페이지의 주요 부분.
  • widgets: 독립적으로 작동하는 대규모 기능 또는 UI 컴포넌트, 보통 하나의 완전한 기능.
  • features: 서비스 전반에 걸쳐 재사용되는 기능 구현체로, 사용자에게 실질적인 비즈니스 가치를 제공하는 동작.
  • entities: 프로젝트가 다루는 비즈니스 엔티티.
  • shared: 재사용 가능한 기능, 특히 프로젝트/비즈니스의 특성과 분리되어 있을 때.

3. 구현 과정 및 결과

  • 폴더 이동: 기존 componentsfeatures 폴더에 산재해있던 로직을 FSD 구조로 재배치.
  • 중복 제거: 여러 폴더에 흩어져 있던 모달, 토스트 등 공통 컴포넌트를 shared/ui로 통합.
  • UI/Model/API 분리: 기능 내에서 어떤 부분이 비즈니스 로직(model), 네트워크 통신(api), UI인지 명확히 분리해 가독성 개선.

📈 결과 및 성과

  • 유지보수성 향상
    • 기능을 기준으로 코드를 분리함으로써, 해당 기능에 필요한 로직이 한 곳에 모이도록 구조화되었습니다.
    • 따라서, 문제가 발생했을 때 어디서 해결해야 하는지가 명확해져, 디버깅과 수정 작업이 훨씬 수월해질 것으로 기대합니다.
  • 높은 응집도와 낮은 결합도
    • 각 기능 내에서 관련된 로직만 모아두어 응집도가 높아졌으며, 다른 기능과의 의존성은 필요한 최소 수준으로 관리됩니다.
    • 기능 변경이 다른 부분에 미치는 영향을 최소화하여 유지보수 부담을 줄였습니다.
  • 확장성 및 규모 확장 용이
    • 새 기능을 추가할 때 독립된 슬라이스를 생성하면 되므로, 기존 코드에 대한 대규모 수정 없이 기능 확장이 가능합니다.
    • 기능을 나누어 개발하기에도 용이해 충돌이 줄어들고, 확장 요구 사항에 유연하게 대응할 수 있습니다.
    • 예컨대, AI 기능을 새롭게 추가해야 할 때도 기존 코드를 대폭 수정할 필요 없이 손쉽게 확장할 수 있습니다.
  • 코드 가독성 및 구조화 향상
    • 기능 별로 코드가 명확히 분리되어 있어 설계 의도가 직관적으로 드러납니다.
  • 재사용성 증대
    • shared 레이어에 공통 로직(예: 유틸 함수, 공통 컴포넌트 등)을 모아놓아, 여러 기능에서 손쉽게 활용할 수 있습니다.
    • 반복되는 코드를 각 기능으로부터 분리함으로써, 관리 효율이 높아지고 중복 구현을 예방합니다.