원본: https://dev.to/m_midas/feature-sliced-design-the-best-frontend-architecture-4noj
개요
프론트엔드 개발을 할 때 앱 내 구조에 관한 문제는 흔히 발생한다. 확장성이 있으면서 디펜던시가 강하지 않으며 앱 모듈 간 높은 응집력이 기반이되는 구조의 사용이 요구되기 때문이다.
본 글은 저자가 생각하기에 리액트에서 선택할 수 있는 최고의 옵션인 Feature-Sliced Design (FSD) 구조를 논의하며, FSD가 어떤 아이디어에 기반하고 있고 이러한 구조적 접근법이 어떤 문제들을 해결하는지 살펴본다. FSD와 다른 전통적인 모듈 기반 구조들을 비교하고, 장단점도 논의할 예정이다.
시작하기에 앞서, 레이어, 슬라이스, 그리고 세그먼트 이 3개의 컨셉을 먼저 이해하도록 하자.
FSD의 주요 요소
1. 레이어 (Layer)
레이어란 최상위 레벨 디렉토리를 뜻한다. 레이어는 제한된 수 이내로 만드는데, 경우에 따라 옵셔널일 때도 있지만 최대 7개를 쓰는게 정형화되어 있다. 현재, 아래와 같은 레이어들이 있다:
각각의 레이어는 각자의 책임 영역이 있으며, 비즈니스를 기반으로 한다. 각 레이어를 상세하게 살펴보자.
- app: 앱의 로직이 초기화되는 곳이다. Provider, 라우터, 전역 스타일, 전역 타입 선언 등이 정의된다. 앱의 진입 포인트의 역할을 한다.
- processes: 멀티 스탭 가입과 같이 여러 페이지에서 사용되는 프로세스를 관리하는 레이어이다. 이 레이어는 deprecated 된 것으로 여겨지긴 하지만, 간혹 반영되어있는 걸 볼 수 있다. Processes 또한 옵셔널 레이어이다.
- pages: 앱의 페이지들을 포함하고 있는 레이어이다.
- widgets: 페이지들에서 사용되는 standalone UI 컴포넌트들이 있는 곳이다.
- features: 비즈니스 가치를 지닌 유저 시나리오와 기능에 관한 것들이 포함된 레이어이다. 예를 들어, 좋아요, 리뷰 작성, 상품 별점 주기 등이 여기에 포함된다. 여기 또한 옵셔널 레이어이다.
- entities: 유저, 리뷰, 댓글 등과 같은 비즈니스 개체(entities)를 뜻한다. 옵셔널 레이어이다.
- shared: 특정 비즈니스 로직을 위한 것만이 아닌 범용적으로 사용되는 재사용가능한 컴포넌트와 유틸리티를 포함하는 레이어이다. UI 키트, axios 설정, 앱 설정, 또는 비즈니스 로직에 묶여있지 않은 각종 핼퍼 등이 여기에 포함된다.
위와 같은 레이어 정리는 코드베이스를 정리하고 유지보수성과 확장성 높은 모듈 기반의 구조를 만드는데 도움을 준다.
Feature-Sliced Design의 주요 특징 중 하나는 계층적 구조 (hierarchical structure)를 가진다는 점이다. 이러한 구조에선, 개체들은 피처(feature)가 제공하는 기능(functionality)을 사용할 수 없다. 피처가 더 높은 계층에 있기 때문이다. 동일한 논리를 따라 피처(feature)는 위젯(widget) 또는 프로세스(process)의 컴포넌트를 사용할 수 없다. 상위 계층의 레이어는 자신보다 낮은 계층의 레이어만 사용할 수 있기 때문이다. 이는 한 방향으로만 흐르는 선형 플로우(linear flow)를 유지하기 위한 목적을 가지고 있다.
레이어가 계층 구조의 하단에 위치할수록 해당 레이어에 변경 사항을 반영하는게 위험해진다. 코드 내에서 더 많은 곳에서 사용되고 있을 가능성이 높기 때문이다. 예를들어, shared 레이어에 있는 UI 키트는 features, widgets, 그리고 pages 레이어에서까지 사용될 수 있기 때문에 수정 사항을 반영했을 때 사이드 이펙트가 발생할 가능성이 크다.
2. 슬라이스 (Slice)
각자 레이어 안에는 서브 디랙토리가 존재하며, 이를 슬라이스라고 부른다. 즉, 슬라이스란 앱 구조에서 두번째 단계의 폴더들을 뜻하는 말이다. 슬라이스 안에는, 추상적인 것에 대한 연결이 아닌 명확한 비즈니스 개체(entity)에 대한 연결이 존재한다. 슬라이스의 주요 목적은 코드의 가치(value)에 따라 그들을 그룹화하는 것이다.
슬라이스의 이름은 정해진 것이 없다. 프로젝트의 비즈니스에 따라 직접적으로 정해지기 때문이다. 예를 들어, 만약 프로젝트가 사진 갤러리라면 photo, album, gallery와 같은 섹션들이 존재할 수 있을 것이다. 만약 소셜 네트워크 앱이라면, post, user, newsfeed와 같은 섹션들이 있을 수도 있다.
밀접하게 연관된 파편들은 디렉토리 안에서 구조적으로 그룹화될 수 있으나, 그들 또한 다른 슬라이스들과 마찬가지로, 해당 코드에 공유되는 접근이 디랙토리 내에 없어야 한다는 동일한 분리(isolation) 규칙을 따라야 한다.
3. 세그먼트 (Segment)
각 슬라이스는 세그먼트들로 구성된다. 세그먼트는 슬라이스 내 코드를 각 코드들의 목적에 따라 나눠준다. 팀 내 동의 여부에 따라, 세그먼트의 구성과 이름은 바뀔 수 있다. 아래는 가장 널리 사용되는 세그먼트 이름이다:
- api: 필수적인 서버 요청들
- UI: 슬라이스의 UI 컴포넌트들
- model: 비즈니스 로직 (예: 상태(state) 핸들링, actions, selectors, etc)
- lib: 슬라이스 안에서 사용되는 핼퍼 기능들
- config: 슬라이스에서 요구되는 필수적인 설정들. (잘 없음)
- consts: 상수들
4. Public API
각각의 슬라이스 및 세그먼트는 Public API를 갖고 있다. Public API는 index.js 또는 index.ts 파일을 통해 대변된다. 이를 통해 슬라이스 및 세그먼트 내 꼭 필요한 기능만을 추출하고 비필수적인 기능들은 고립시키는 것이 핵심이다. index 파일이 이러한 행위의 진입 포인트 역할을 한다.
Public API 규칙
- 앱 내 슬라이스와 세그먼트는 Public API index 파일에 정의된 기능과 컴포넌트만을 사용한다.
- Public API에 정의되지 않은 슬라이스 또는 세그먼트의 내부 부분들은 고립된(isolated) 상태로 여겨지며, 슬라이스 또는 세그먼트 자체 내부에서만 접근이 가능하도록 한다.
Public API는 import, export를 사용한 작업을 단순하게 만들어준다. 그렇기 때문에, 앱에 변경 사항을 반영할 때 코드 내에 이를 import 하는 모든 곳에 각종 변경 사항을 만들지 않아도 된다는 이점이 있다.
FSD: 상세 설명
1. 추상화와 비즈니스 로직
높은 계층에 위치한 레이어일수록 특정 비즈니스에서만 사용하며, 더 많은 비즈니스 로직을 담고 있다. 반면에, 레이어의 계층이 낮을 수록, 더 많은 추상화와 재사용성을 기대할 수 있으며, 레이어의 독립성은 제한된다.
2. FSD가 구조적 문제를 해결하는 방법
FSD의 주요 목적 중 하나는 높은 응집도와 낮은 결합도(high cohesion and loose coupling)를 달성하는 것이다. 그렇기에 FSD가 어떻게 이러한 결과를 불러오는지를 이해하는 것이 중요하다.
OOP에선, 이러한 문제들을 다형성(polymorphism), 캡슐화(encapsulation), 상속(inheritance), 그리고 추상화(abstraction)와 같은 컨셉들로 해결한다. 이러한 개념들은 코드의 고립화와 재사용성, 그리고 다재다능성(versatility)을 보장해주며, 이를 통해 컴포넌트 또는 기능(functionality)이 어떻게 사용되느냐에 다른 결과가 만들어질 수 있도록 해준다. FSD는 이러한 원칙들을 프론트엔드에서 적용할 수 있도록 도와준다.
추상화와 다형성은 레이어를 통해 얻을 수 있다. 하위 계층에 있는 레이어들은 추상적이기 때문에, 높은 레이어들에서 재사용할 수 있다. 또한, 조건에 따라, 컴포넌트 또는 기능은 특정 파라미터 또는 props에 따라 다른 기능을 보여줄 수도 있다.
캡슐화는 Public API의 사용을 통해 보장받는다. Public API는 슬라이스와 세그먼트 외부에서 불필요한 기능들을 고립시킨다. 슬라이스의 내부 세그먼트로의 접근은 제한되며, Public API를 통해서만 슬라이스 또는 세그먼트의 기능과 컴포넌트에 접근할 수 있다.
상속 또한 레이어를 통해 보장된다. 상위 계층에 있는 레이어들이 하위 게층들을 재사용할 수 있기 때문이다.
3. 전통적 구조와의 비교
아마 여기서 말하는 전통적 구조의 리액트 프로젝트를 여럿 보았을 것이다. 전통적 구조는 단순하다는 장점이 있기 때문에 대다수의 교육 아티클 및 유튜브 비디오들에서 자주 볼 수 있다. 전통적 구조에 대해 특별히 정해진 기준은 없다. 다만, 전통적 구조는 주로 아래의 구조와 크게 다르지 않다:
이러한 전통적 구조엔 눈에 띄는 단점이 있다. 그 중 가장 큰 단점은 프로젝트 내의 컴포넌트와 모듈들 간 암시된 연결 구조를 관리하기 어렵다는 점이다. 이러한 점은 시간이 지날 수록 극명하게 나타난다. 프로젝트의 존속 기간이 늘어날수록, 앱의 구조는 해체하기 어려운 집합체로 변경되어간다. 그렇기 때문에 이러한 전통적 구조는 추가적은 유지보수가 필요 없이 작은 프로젝트나 토이 프로젝트에 적합하다.
Feature-Sliced Design은 전통적 구조가 가지고 있는 이러한 문제들을 해결해준다. 그러나, FSD는 전통적 구조로 작업할 때보다 개발자에게 더 높은 이해와 스킬을 요구한다. 주로, 2년차 이하 개발자들은 FSD에 대해 들어본 적이 없다고 한다. 그러나 FSD로 일할 땐, 문제들은 “나중”보다 “지금” 다뤄져야 한다. 코드 내 이슈와 컨셉으로부터의 이탈이 극명하게 나타나기 때문이다.
4. 단순 모듈러 구조와의 비교
간단한 모듈러 구조는 여러 단점들을 갖고 있다:
- 때때로, 기능을 모듈 또는 컴포넌트에 넣는걸 어디서 해야하는지 불명확하다.
- 다른 모듈 안에서 또 다른 모듈을 사용하는게 어렵다.
- 강한 비즈니스 개체 내에 이슈가 있을 수 있다.
- 전역 함수들 내 불명확한 디펜던시로 인해 얽힌 구조가 될 수 있다.
복잡한 구조를 가진 어떤 프로젝트에서도, Feature-Sliced Design은 단순한 모듈러 구조보다 선호되어야 한다. FSD가 많은 근본적인 구조적 문제를 해결할 수 있기 때문이다.
간결함과 개발 속도 측면에서, 간단한 모듈러 구조가 FSD 보다 더 나을 수도 있다. 만약 MVP가 필요한 상황이거나 단기간 존속할 프로젝트를 만들고 있는 경우라면, 간단한 모듈러 구조가 FSD 보다 더 좋을 수 있다. 하지만 그런 상황이 아니라면 FSD가 더 선호되는게 사실이다.
5. Next.js와의 호환
최근들어, Next.js를 FSD와 함께 사용하는 게 트렌드화되고 있다. Next.js는 FSD와 잘 맞지만, pages 에서의 파일 라우팅과 app이 없다는 부분에서 컨플릭트가 생길 수 있다.
(1) Pages
Next.js에선, pages는 파일 라우팅을 담는 디랙토리이다. 각각의 컴포넌트가 하나의 라우트를 상징한다. FSD에선, pages가 페이지들의 리스트이다. 이를 해결하기 위해선, Next.js의 page들을 application의 root에 넣을 수 있다: [root]/pages/ . 또한, FSD pages들은 src 폴더에 들어갈 수 있다: [root]/src/pages
또 다른 해결법은, 두개의 디랙토리를 유지하는 것이다. FSD를 위한 별도의 네이밍을 가진 pages와 (pages-flat과 같은 이름으로), Next.js의 pages를 가져가는 것이다.
(2) App
Next.js는 app 레이어의 모든 기본 기능들을 다룬다. 그러나, 만약 여전히 페이지와는 독립적으로 앱 전체에 대해 무언가를 돌려야하는 상황이라면, Layout Pattern을 사용해서 앱 전체를 위한 레이아웃을 생성하면 된다.
=> ps. app routing을 사용할 경우 해당되지 않음
6. FSD의 잠재력
FSD는 아직 만들어지지 얼마되지 않은 신생 접근법이다. 그러나 이미 여러 서비스들 - 은행, 핀테크, B2B, e-commerce 회사들이 이를 도입하고 있다. 문서 또한 활발하게 작성되고 있으며, 텔레그램 및 디스코드의 FSD 개발 팀과 커뮤니티에서 구조 관련 질문들이 24시간 돌아가고 있다.
때문에 FSD의 잠재력은 굉장히 높은 것으로 생각되어지고 있으며, 전세계 많은 큰 기업들에서의 사용도 또한 증가하고 있다. 올바르게 적용하기만 한다면, FSD는 프론트엔드 개발 분야에서 주요한 구조 솔루션이 될 잠재력을 가지고 있다.
7. 장단점
(1) 장점
- 구조 컴포넌트가 쉽게 대체되거나 추가되거나 제거될 수 있다.
- 구조의 표준화
- 확장성
- 개발 스택으로부터 독립적인 방법론
- 모듈간 기대하지 못한 사이드 이펙트 없는 컨트롤되면서 명확한 연결
- 비즈니스 기반 구조적 방법론
(2) 단점
- 다른 구조적 솔루션 대비 높은 진입 장벽
- 컨셉에 대한 이해, 팀 문화, 컨셉 충실도 필요
- 이슈 발생 시 즉각적인 대응이 요구됨. 코드 문제와 컨셉으로부터 벗어난 코드가 즉각적으로 눈에 들어오게 됨. 그러나, 사실 이건 장점으로 볼 수도 있음.
결론
FSD는 프론트엔드 개발자들이 잘 알고 자주 사용해야할 흥미로운 발견이다. FSD는 개발팀에게 융통성있으면서 표준화된, 확장성 가능한 구조 및 개발 문화를 제공한다. 그러나, 해당 방법론을 사용하면서 긍정적인 결과를 이끌어내는데엔 지식과 이해, 그리고 팀 내의 규율이 요구되는 것도 사실이다. 다른 구조들과 비교했을 때 FSD는 명확하게 비즈니스를 기반으로 하며, 마찬가지로 명확한 개체 정의와 앱 내 기능 및 컴포넌트 구조를 제공한다.
FSD 공식 문서를 통해 FSD의 실 사용 예시를 볼 수 있다:
Example. Nike Sneaker and Footwear Store
'Learn to Code' 카테고리의 다른 글
[디자인패턴] 자바스크립트로 전략 패턴(Strategy Pattern) 구현하기 (1) | 2024.06.11 |
---|---|
[디자인패턴] React + Tanstack Query 서비스 레이어 패턴으로 구조짜기 (0) | 2024.06.03 |
[Next.js] 브라우저 캐싱 문제 해결 방법 (0) | 2023.10.20 |
[Next.js] 버전 13+으로 업데이트하기 (0) | 2023.10.07 |
[React] 커스텀훅(Custom Hook) (0) | 2023.08.16 |
댓글