리액트로 어플리케이션을 만들때마다 가장 고민하는 것 중에 하나는 컴포넌트 구조를 어떻게 설계하는가 이다. 만약 디렉토리 구조를 컴포넌트, 훅, 유틸처럼 기능으로만 설계하고 이에 대한 불편함을 느낀 개발자라면 이 글을 읽어보면 도움이 많이 될 것 같다. FSD는 전통적인 방식의 고질적 문제를 효과적이고 합리적으로 해결한다.
우선 전통적인 디렉토리 구조를 정의할 필요가 있다. 이 글에서 말하는 전통적인 디렉토리 구조란 에셋, API, 컴포넌트처럼 파일 타입으로 분류된 것을 의미한다.
위와 같은 구조의 대표적인 문제점은 폴더 중첩에 한계가 없다는 점이다. 그렇다고 모두 상위로 위치시키면 원하는 파일을 찾기 위해서 스크롤을 하염없이 내리게 될 수도 있다. 결국에는 자식 컴포넌트는 부모 컴포넌트 하위에 위치하게 되는 경우가 많다. 이런 폴더가 2-3개일때는 크게 불편함을 느끼지 못하지만 10개 이상이 되어가면 어느새 컴포넌트를 찾기 위해 컴포넌트 이름을 검색하게 된다.
모든 파일을 컴포넌트 폴더 하위에 둘 수 없는 노릇
컴포넌트 폴더가 복잡해짐으로 import 문도 난잡해질뿐더러 무엇보다 DX가 저해된다.
전통적인 디렉토리 구조의 또 다른 문제점은 어느 파일이 어디에서 사용되는지 정의되어있지 않아 추적이 어렵다는 것이다. 예를 들어 API같은 경우 특정 컴포넌트에서 사용되는 것임에도 불구하고 api란 폴더안에 배치되어있어 어디에서 필요한 것인지 바로 알 수 없다. 컴포넌트 간에도 구분이 명확하지 않아 여기저기 쓰이게되고 상황에 맞게 컴포넌트를 변경하다보면 어느새 다른 곳에서는 사용할 수없는 컴포넌트가 되어버리기도 한다.
이런 구조가 마치 MVC 패턴처럼 느껴지기도 한다. MVC 패턴의 양방향 소통 문제를 해결하려고 FLUX 패턴이 나온 것처럼 고전적인 디렉토리 구조의 문제점을 해결하기 위해 FSD라는 새로운 개념의 디렉토리 구조가 탄생했다.
위에서 언급한 문제는
이런 문제를 FSD는 계층화를 통해서 매우 효과적이고 직관적으로 해결하는데 먼저 FSD가 무엇인지 알아보자.
FSD는 크게 Layer와 Slice 그리고 Segment로 구성되어 있다. Layer 하위에 Slice가 존재하고 그 하위에 Segment가 위치힌다. 이 특징때문에 FSD 구조에서 폴더의 최대 깊이는 3으로 제한된다.
FSD의 디렉토리 구조
이제 Layer와 Slice 그리고 Segment가 어떤 역할을 하는지 차근차근 알아보자.
FSD에서 가장 중요한 키워드는 계층이다. 계층화를 통해서 폴더 관계를 명시하기도 하고 접근을 제한하기도 한다. 디렉토리 구조의 기준이 폴더 타입이나 기능이 아니라 FSD에서 명시한 계층 특징이 된다. FSD는 총 6개의 계층을 추상화하는데 각 계층은 담고 있는 폴더의 특징들이 다르고 무엇보다 중요한 특징은 하위 계층은 상위 계층의 폴더에 접근할 수 없다. 그렇다면 어떤 계층에 어떤 폴더를 넣어야할까? 공식 문서에서 말하는 기준은 다음과 같다.
FSD를 몇 번 적용해보고 느낀 점은 위 정의가 생각보다 모호하다는 것이다. 사전에 해당 프로젝트만의 명확한 기준을 세우고 설계하는 것이 필요하다.
Slice는 Layer의 하위 풀더로 Layer와 달리 네이밍 컨벤션이 없다. 왜냐하면 프로젝트마다 필요한 폴더가 다르기 때문이다. 예를 들어 블로그 프로젝트에서는 post나 list와 같은 Slice가 필요하지만 쇼핑몰 프로젝트에서는 shoppingCart나 payment와 같은 Slice가 필요할 수 있다.
Slice의 중요한 점 중 하나는 폴더의 import를 담당하는 Public API가 반드시 있어야 한다는 것인데 이는 뒤에서 더 설명하겠다.
Segment는 마지막 하위 폴더로 Layer처럼 몇 가지 정해진 네이밍 켠벤션이 있다. 이는 모든 폴더가 공유하는 특징이 있기 때문이다.
앞서 언급했듯이 Slice는 import를 관리하는 Public API가 반드시 있어야 한다. 그리고 상위 폴더는 이 Public API만을 통해서 Slice 내부 파일에 접근할 수 있다. 다시 말해서 Public API에 명시되어 있지 않은 폴더는 다른 폴더가 접근할 수 없다는 것이다. 이와 같이 규칙을 만든 이유는 다음과 같다.
그럼 Public API는 어떻게 만들어야 할까? 매우 쉽다.
우선 Slice 내부에 index.ts를 만들고 원하는 함수를 export 해주면 된다.
FSD는 분명히 이전의 문제들을 효과적으로 해결하지만 그렇다고 만능은 아니다. 4개정도의 중소 규모의 프로젝트에 적용해봤을때 느꼈던 단점은 다음과 같다.
첫 번째부터 설명하자면 소규모 프로젝트에서는 가급적 사용하지 않는 편이 좋은 것 같다. 기본적으로 만들어야 하는 폴더들 때문에 배보다 배꼽이 더 크다는 느낌을 받았다. 두 번째로는 Layer와 Slice를 미리 정해놓아야한다고 느꼈다. 물론 이는 사용하는 사람마다 다룰 수 있지만 내가 느낀 바로는 widgets, features, entities의 역할이 명확하지 않다고 느꼈고 어디 들어가도 이상하지 않을 컴포넌트들이 분명히 있었다. 그래서 만약 협업을 한다면 회의를 통해 미리 정하는 것이 좋을 것 같다. 마지막으로 FSD의 개념자체가 조금 생소해서 익히는데 길진 않지만 시간이 조금 걸리는 것 같다. 처음 사용해보는 사람이라면 감을 잡는데 며칠 걸리지 않을까 하는 생각이 있다.
당연한 이야기지만 FSD는 만능이 아니니 상황에 따라 적합하게 사용하는 것이 좋다.
FSD는 폴더 구조가 복잡해지고 무분별한 폴더간 참조로 인한 오염 문제를 2번의 계층화를 통해서 해결한다. 나는 계층화라는 컨셉이 너무나도 매력적인 것 같다. 룰이 존재하지 않는 세상에서 역할을 추상화함으로서 질서를 만들어 생산성을 올리는 발상이 참 멋있는 것 같다. 아무리 생각해봐도 React의 탄생과 매우 비슷한 것 같다.
만약 FSD를 진심으로 적용해보고 싶다면 FSD 공식 문서를 읽는 것을 꼭 추천드리고 이 FSD를 사용한 프로젝트의 소스 코드를 읽어보는 것을 추천한다. FSD 공식 홈페이지에 나와있는 예시보다 더 잘 적용되어있다고 생각한다.