들어가기
최근 FSD(Feature-Sliced Design) 관련 글들을 많이 보게 되더라고요. 실제로 채용 과제에서 FSD 구조로 프로젝트를 작성해 오는 지원자도 봤었습니다. 한동안 Atomic Design 아키텍처가 많이 쓰였던 것 같은데, 요즘 FSD가 왜 이렇게 떠오르고 있는지 궁금해서 좀 찾아보았고, 그에 대해 제 생각도 정리해 봤습니다.
일반적인 프로젝트 구조의 한계
많은 프론트엔드 프로젝트에서 다음과 같은 구조를 흔히 볼 수 있습니다 (아닐 수도 있습니다... 😅):
src/
├── components/ # 공통 component
├── utils/ # 공통 유틸 함수
├── typings/ # 타입 정의
├── hooks/ # 공통 커스텀 훅
└── pages/ # 페이지
대부분의 경우, 모든 컴포넌트는 components
폴더에 위치하고, 각 페이지마다 별도의 폴더로 관리됩니다. 저희 회사에서는 더 단순한(flat) 구조를 선호하여 components
폴더 바로 아래에 모든 컴포넌트를 위치시키고 있습니다.
하지만 이와 같은 구조는 프로젝트가 커질수록 여러 한계를 드러냅니다:
- 컴포넌트 수가 증가하면서 찾기 어려워짐
- 관련 컴포넌트들의 응집도가 떨어짐
- 유지보수의 어려움 증가
Atomic Design의 한계점
회사의 예전 디자인 시스템(현재는 뉴 디자인시스템을 구축한 상태입니다)을 구축할 때 Atomic Design을 적용했지만, 다음과 같은 문제점이 발생했습니다:
- 실제 디자인과 구현된 컴포넌트 간의 지속적인 차이 발생
- 차이를 해결하기 위한 과도한 flag props 사용
- 너무 많은 props로 컴포넌트 사용의 어려움
- 예외 케이스 증가로 인한 유사 컴포넌트(molecules, organisms) 중복 생성
- 결과적으로 컴포넌트 재사용성 저하
왜 FSD가 떠오르는가?
위와 같은 한계를 극복하기 위한 새로운 접근 방식 중 하나가 FSD입니다
FSD의 개념에 대해 잘 정리되었다고 생각되는 글입니다.
https://velog.io/@teo/fsd#for-all-other-components
FSD는 다음과 같은 장점들을 제공합니다:
- 명확한 책임 분리
- 기능 단위로 코드 구성
- 도메인 로직과 UI 컴포넌트의 자연스러운 분리
- 확장성과 유지보수성
- 새로운 기능 추가가 용이
- 관련 코드들의 높은 응집도
하지만 다음과 같은 도전 과제도 존재한다고 생각합니다:
- 러닝 커브
- 새로운 팀원들이 적응하는 데 시간이 필요함
- 기존 코드베이스를 마이그레이션 하기 어려움
- 구조 결정의 어려움
- 공통 컴포넌트를 분류하는 기준이 모호할 수 있음
- features, entities, widgets 간 경계를 설정하는 데 어려움이 있음
예를 들어, "A라는 컴포넌트를 어디에 포함시켜야 하는가?"라는 질문이 있을 때, 이 컴포넌트가 특정 기능의 핵심적인 부분을 담당하는 경우 features에 포함될 수도 있고, 데이터와 로직을 담당하는 경우 entities에 해당될 수 있습니다. 또한, 사용자 인터페이스 요소로 주로 쓰인다면 widgets에 들어갈 가능성도 있습니다. 이러한 결정은 팀의 컨벤션에 따라 어느 정도 가이드라인이 있더라도, 개별적인 컴포넌트의 성격에 대해 주관적인 판단이 개입될 수 있기 때문에 항상 명확하지는 않을 수 있습니다.
제가 생각하는 아키텍처는...
FSD 공식문서는 엄격한 규칙보다는 가이드라인을 제공하기 때문에 팀에 따라 각기 다른 방식으로 해석될 여지가 큽니다. 저는 다음과 같은 구조가 효율적이라고 생각했습니다:
src/
├── components/ # 공통 컴포넌트 (button, input 등)
├── utils/ # 공통 유틸리티
├── types/ # 공통 타입 정의
├── hooks/ # 공통 커스텀 훅
└── pages/ # 페이지별 구성요소
├── login/ # 로그인 페이지 관련
│ ├── components/ # 로그인 전용 컴포넌트
│ ├── utils/ # 로그인 관련 유틸
│ ├── types/ # 로그인 관련 타입
│ └── hooks/ # 로그인 관련 훅
└── other-feature/ # 다른 기능들도 동일한 구조
이 구조에서 핵심적인 접근 방식은 다음과 같습니다:
- 공통 컴포넌트의 관리
src 폴더
아래에는 진정으로 모든 페이지에서 공통으로 사용되는 요소들만 배치합니다. 기본적인 UI 컴포넌트(button, input 등)가 여기에 해당합니다. 이러한 방식으로 공통 컴포넌트를 관리하여 공통 컴포넌트의 중복을 줄이고 일관된 재사용이 가능해집니다. - 점진적인 추상화 도입
새로운 컴포넌트는 우선 해당 기능 폴더 내에 구현하고, 재사용성이 확인될 때 비로소 공통 폴더로 이동시키는 방식으로 접근합니다. 예를 들어, 로그인 페이지에서 사용하는LoginButton
컴포넌트는 처음에는 login 폴더 내에 위치시키지만, 이후 여러 페이지에서 해당 버튼을 사용할 필요가 생기면 이를src/components
폴더로 이동시켜 재사용성을 높입니다. 이렇게 실제 사용 빈도에 따라 컴포넌트를 이동시키는 점진적인 추상화를 통해 불필요한 복잡성을 줄이고, 효율적인 유지보수를 가능하게 합니다.
마무리
저는 이 구조를 주로 저만 작업하는 프로젝트에만 적용해 봤고, 개인적으로는 꽤 만족스러운 결과를 얻었습니다. 하지만 팀원들에게 이를 공유하고 설득하는 과정은 쉽지 않을 것이라 생각합니다. 왜냐하면 이게 명확한 아키텍처 방법론에 근거한 것이 아니라 제 개인적인 생각이기 때문에, "이렇게 하면 개선된다!"라고 할 수 있는 객관적인 근거가 부족하기 때문입니다... (비슷한 고민을 하신 분들은 어떻게 이를 해결하신 지 궁금합니다. 😂)
현재 회사에서 작업하는 프로젝트들이 대부분 규모가 크기 때문에, FSD를 적용하면 좋겠다고 생각하고 있습니다. 하지만 복잡한 구조와 팀원들에게 가이드를 제공해야 하는 부담, 그리고 기존의 익숙한 아키텍처와 다른 새로운 접근 방식은 팀원들에게 오히려 혼란을 줄 수 있다는 점도 걱정됩니다.
비록 도입에는 도전 과제가 있지만, 잘만 적용하면 팀의 코드 구조를 크게 개선할 수 있는 가능성이 있다고 생각합니다!
참고
- FSD 공식문서: https://feature-sliced.design/kr/docs
'Frontend > Thoughts on Development' 카테고리의 다른 글
주석을 언제 작성해야 할까? (3) | 2024.10.27 |
---|---|
빈 태그 사용을 피해야 하는 이유 (0) | 2024.03.16 |
React Query에서 Non-null Assertion Operator 사용하기 좋은 방법일까? (0) | 2024.02.18 |
똑똑한 컴포넌트, 어디까지가 좋을까? (0) | 2024.01.26 |
React Custom Hook 디자인 패턴: Query/Mutation 훅 분리 (0) | 2023.12.31 |