폴더가 약속이 되는 패턴
폴더가 약속이 되는 패턴
폴더 구조는 단순한 파일 분류가 아니라, 코드 사이의 의존 방향과 변경 영향 범위를 약속하는 장치가 될 수 있습니다. 이 글은 Feature-Sliced Design · Domain-Driven Design · Hexagonal Architecture 의 사실.
1. 폴더가 약속이 되는 자리
폴더 이름이 그저 "잡동사니 / 유틸 / 도우미" 일 때, 새 코드의 위치를 결정하는 것은 사람의 즉흥. 시간이 지나면 같은 책임의 코드가 여러 폴더에 흩어지고, 한 폴더 안에 다른 책임이 뒤섞입니다.
폴더가 "이 디렉터리는 이런 책임만 가진다", "이 디렉터리는 위쪽 디렉터리만 import 한다" 같은 규칙을 가지면, 폴더 자체가 의존 방향과 책임을 강제하는 약속이 됩니다.
2. Feature-Sliced Design (FSD)
feature-sliced.design 의 공식 문서가 정의하는 프런트엔드 구조 표준. 1.0 사양 정착은 2022 년 무렵.
레이어 — 위에서 아래로 의존만 가능. 같은 레이어끼리는 원칙적으로 import 금지:
| 레이어 | 역할 |
|---|---|
app |
진입 · 전역 프로바이더 · 라우팅 루트 |
pages |
라우트별 합성. 여러 위젯 · 기능을 페이지에 배치 |
widgets |
페이지 단위 큰 블록 (헤더 · 사이드바) |
features |
사용자 행위 (로그인 · 결제) |
entities |
도메인 객체 (User · Product) |
shared |
UI 키트 · 라이브러리 · 설정 |
레이어 안은 도메인 단위로 쪼개고 (슬라이스), 그 안은 기술 단위 (ui/ · model/ · api/ · lib/) 로 쪼갬.
이 구조의 의도 (공식 문서):
- 프로젝트 규모가 커져도 변경의 영향 범위를 폴더 경계로 가둘 수 있음.
- 새 팀원의 온보딩이 쉬워짐.
- 재사용성과 격리를 레이어 위치로 조절.
- 비즈니스 도메인 중심 명명.
- 한 레이어의 모듈은 같은 레이어나 위 레이어를 import 할 수 없음 (격리).
3. Domain-Driven Design (DDD)
Eric Evans 의 2003 년 책 Domain-Driven Design: Tackling Complexity in the Heart of Software 가 출처. 중심 개념이 Bounded Context.
각 컨텍스트는 자신만의 모델 · 언어 (Ubiquitous Language) 를 가지고, 다른 컨텍스트와는 명시적 통합 패턴 (Anti-Corruption Layer · Open Host Service 등) 으로만 연결. 폴더 구조로는 도메인 단위 디렉터리가 컨텍스트 경계의 1차 표시:
ordering/
billing/
inventory/
shared-kernel/
같은 "Customer" 라도 ordering 의 Customer 와 billing 의 Customer 는 같은 코드일 필요가 없음. 한 컨텍스트 안에서 응집되고 컨텍스트 사이는 느슨하게 결합됩니다.
4. Hexagonal Architecture (Ports and Adapters)
Alistair Cockburn 이 2005 년 글 "Hexagonal architecture" 에서 정리. 핵심은 도메인 로직을 가운데에 두고, 외부 세계 (웹 · DB · 메시지 큐 · CLI) 는 포트 (인터페이스) + 어댑터 (구현) 로 둘러싸는 구조.
폴더로 옮기면:
domain/ # 비즈니스 규칙. 외부 라이브러리 의존 금지.
application/ # 유스케이스. 포트 (인터페이스) 정의.
adapters/
in/web/ # HTTP 컨트롤러
in/cli/
out/persistence/ # DB 어댑터
out/messaging/
핵심 규칙 — 의존은 안쪽으로만. domain 은 application 을 모르고, application 은 adapters 를 모름. 이 규칙이 지켜지면 DB 를 바꾸거나 HTTP 를 gRPC 로 갈아도 도메인 코드는 건드리지 않음.
Robert C. Martin 의 Clean Architecture (2017) 가 같은 아이디어를 동심원 다이어그램으로 다시 제시.
5. 강제 수단
폴더 약속이 깨지지 않도록 도구로 강제 가능:
- import 경계 린터 — ESLint 의
import/no-restricted-paths·eslint-plugin-boundaries· JS Steiger 등. - 의존성 그래프 검사 — dependency-cruiser · madge.
- JVM 의 ArchUnit — 패키지 의존 규칙을 테스트로 검증.
- 모듈 시스템 — Java 9+ 모듈 · TypeScript 의
paths/ 프로젝트 레퍼런스 · Rust 의 모듈 / 크레이트.
도구가 있어도 결국 사람이 합의한 규칙이 있어야 동작.
6. 다른 길
세 패턴 모두 환경에 따라 적합도가 다름:
- FSD — 프런트엔드, 특히 React / Vue 의 중대형 SPA 에서 자주 채택.
- DDD — 비즈니스 규칙이 풍부하고 모델링이 핵심인 도메인에 어울림. 단순 CRUD 에는 무거움.
- Hexagonal · Clean — 외부 의존 (DB · 외부 API) 이 자주 바뀌거나 테스트 가능성이 중요할 때 효과가 분명.
작은 프로젝트는 단순한 평면 폴더로 충분한 경우가 많음. 구조의 비용은 항상 0 이 아닙니다.
7. 자주 걸리는 자리
폴더는 약속의 표시 일 뿐 실행은 아님 — 강제 수단 (린터 · 테스트) 이 없으면 한 사람의 import 가 약속을 깸.
DDD 의 컨텍스트 경계를 잘못 그으면 모든 변경이 여러 컨텍스트에 동시에 미침 — 도메인 전문가와의 대화 없이 폴더만 갈라 두는 형태가 가장 흔한 실패.
FSD 의 슬라이스를 너무 잘게 쪼개면 작은 변경에도 여러 폴더를 오가야 함 — 응집과 분리의 균형은 프로젝트마다 다름.
Hexagonal 의 인터페이스 추상이 구현 1 개로 끝나면 YAGNI 에 어긋남 — 어댑터가 둘 이상 필요해지는 시점에 도입하는 편이 비용에 합당.
하고픈 말
폴더가 단순 분류를 넘어 의존 방향과 책임의 약속이 될 때, 큰 코드베이스의 변경 영향 범위가 확연히 좁아집니다. FSD · DDD · Hexagonal 셋 모두 적합한 자리가 다름. 작은 프로젝트는 단순한 평면이 정답일 수도. 도구 (ESLint · ArchUnit · dependency-cruiser) 로 약속을 강제하면 사람의 즉흥이 폴더를 흐트리는 일을 막을 수 있습니다.
Next
- tradeoff-not-bestpractice
- progressive-refactor
Feature-Sliced Design 공식 문서 · Alistair Cockburn Hexagonal Architecture · Domain-Driven Design Reference (Eric Evans, 2015) · Robert C. Martin Clean Architecture · ArchUnit · Vaughn Vernon Implementing DDD · dependency-cruiser 를 참고합니다.