@@ -3,181 +3,211 @@ sidebar_position: 1
33pagination_next : reference/slices-segments
44---
55
6- import useBaseUrl from ' @docusaurus/useBaseUrl' ;
6+ import useBaseUrl from " @docusaurus/useBaseUrl" ;
77
88# Layer
99
10- Layer는 Feature-Sliced Design에서 ** 코드를 구분하는 가장 큰 범위** 입니다.
11- 코드를 나눌 때는 각 부분이 ** 어떤 기능을 담당하는지** 와 ** 다른 코드에 얼마나 의존하는지** 를 기준으로 합니다.
12- Layer마다 ** 어떤 역할을 맡는지** 에 대한 공통된 의미가 있습니다.
13-
14- 총 ** 7개의 Layer** 가 있으며, ** 담당 기능과 의존성이 많은 것** 부터 ** 적은 것** 순으로 나열합니다.
15-
16- <img src = { useBaseUrl (" /img/layers/folders-graphic-light.svg#light-mode-only" )} width = " 180" style = { { float: " right" , margin: " 0 1em" }} alt = " A file system tree, with a single root folder called src and then seven subfolders: app, processes, pages, widgets, features, entities, shared. The processes folder is slightly faded out." />
17- <img src = { useBaseUrl (" /img/layers/folders-graphic-dark.svg#dark-mode-only" )} width = " 180" style = { { float: " right" , margin: " 0 1em" }} alt = " A file system tree, with a single root folder called src and then seven subfolders: app, processes, pages, widgets, features, entities, shared. The processes folder is slightly faded out." />
18-
19- 1 . App
20- 2 . Processes (deprecated)
21- 3 . Pages
22- 4 . Widgets
23- 5 . Features
24- 6 . Entities
10+ Layer는 Feature-Sliced Design에서 코드를 나눌 때 사용하는 ** 가장 큰 구분 단위** 입니다.
11+ 코드를 나눌 때는 각 부분이 ** 어떤 역할을 맡는지** , 그리고 ** 다른 코드에 얼마나 의존하는지** 를 기준으로 합니다.
12+ 각 Layer는 ** 이 Layer에는 어떤 코드가 와야 하는지** 에 대해 ** 공통된 의미와 책임** 이 정해져 있습니다.
13+
14+ 총 ** 7개의 Layer** 가 있으며, 아래로 내려갈수록 ** 담당하는 기능과 의존성이 줄어드는 순서** 입니다.
15+
16+ <img
17+ src = { useBaseUrl (" /img/layers/folders-graphic-light.svg#light-mode-only" )}
18+ width = " 180"
19+ style = { { float: " right" , margin: " 0 1em" }}
20+ alt = " A file system tree, with a single root folder called src and then seven subfolders: app, processes, pages, widgets, features, entities, shared. The processes folder is slightly faded out."
21+ />
22+ <img
23+ src = { useBaseUrl (" /img/layers/folders-graphic-dark.svg#dark-mode-only" )}
24+ width = " 180"
25+ style = { { float: " right" , margin: " 0 1em" }}
26+ alt = " A file system tree, with a single root folder called src and then seven subfolders: app, processes, pages, widgets, features, entities, shared. The processes folder is slightly faded out."
27+ />
28+
29+ 1 . App
30+ 2 . Processes (deprecated)
31+ 3 . Pages
32+ 4 . Widgets
33+ 5 . Features
34+ 6 . Entities
25357 . Shared
2636
27- > 모든 Layer를 꼭 써야 하는 건 아닙니다. ** 필요할 때만** 추가하세요.
28- > 대부분의 프론트엔드 프로젝트는 최소한 ` shared ` , ` page ` , ` app ` 은 포함합니다.
37+ > 모든 Layer를 반드시 사용해야 하는 것은 아닙니다.
38+ > ** 필요한 경우에만** Layer를 추가하세요.
39+ > 대부분의 프론트엔드 프로젝트는 보통 최소한 ` shared ` , ` page ` , ` app ` 정도는 사용합니다.
2940
30- 실무에서는 폴더명을 소문자로 작성합니다(예: ` 📁 shared ` , ` 📁 page ` , ` 📁 app ` ).
31- ** 새로운 Layer를 만드는 것은 권장하지 않습니다** (역할이 이미 표준화되어 있음).
41+ 실무에서는 폴더명을 보통 소문자로 작성합니다. (예: ` 📁 shared ` , ` 📁 page ` , ` 📁 app ` )
42+ 또한, ** 새로운 Layer를 직접 정의해서 사용하는 것은 권장하지 않습니다.**
43+ (각 Layer의 역할이 이미 표준으로 충분히 정리되어 있기 때문입니다.)
3244
3345## Import 규칙
3446
35- Layer는 ** Slice(서로 밀접하게 연관된 모듈 묶음)** 로 구성됩니다.
36- Slice 간 연결은 ** Layer Import 규칙** 으로 제한합니다.
47+ 각 Layer는 여러 개의 ** Slice(서로 밀접하게 연관된 모듈 묶음)** 로 구성됩니다.
48+ Slice들 사이의 연결은 ** Layer Import 규칙** 을 통해 제한합니다.
49+
50+ > ** 규칙:**
51+ > 하나의 Slice 안에서 작성된 코드는
52+ > ** 자신이 속한 Layer보다 아래 Layer** 에 있는 * 다른 Slice* 만 import할 수 있습니다.
3753
38- > ** 규칙 ** : Slice 안의 코드는 ** 자신보다 아래 Layer ** 의 * 다른 Slice * 만 Import할 수 있습니다 .
54+ 예를 들어, ` 📁 ~/features/aaa/api/request.ts ` 파일은 다음과 같습니다 .
3955
40- 예: ` 📁 ~/features/aaa/api/request.ts ` 는
41- - 같은 Layer의 ` 📁 ~/features/bbb ` → ** 불가능**
42- - 더 아래 Layer(` 📁 ~/entities ` , ` 📁 ~/shared ` ) → ** 가능**
43- - 같은 Slice(` 📁 ~/features/aaa/lib/cache.ts ` ) → ** 가능**
56+ - 같은 Layer의 ` 📁 ~/features/bbb ` → ** import 불가능**
57+ - 더 아래 Layer(` 📁 ~/entities ` , ` 📁 ~/shared ` ) → ** import 가능**
58+ - 같은 Slice(` 📁 ~/features/aaa/lib/cache.ts ` ) → ** import 가능**
4459
45- ` app ` 과 ` shared ` 는 ** 예외** 입니다.
46- 두 Layer는 ** Layer이면서 동시에 하나의 큰 Slice처럼 동작** 하고, 내부는 ** Segment** 로 나눕니다.
47- 이 경우 Segment끼리 자유롭게 Import할 수 있습니다.
48- (` shared ` 는 비즈니스 도메인이 없고, ` app ` 은 모든 도메인을 묶는 역할을 합니다.)
60+ ` app ` 과 ` shared ` 는 조금 특이한 Layer입니다.
61+ 두 Layer는 ** Layer이면서 동시에 하나의 큰 Slice처럼 동작** 하고 내부 구조는 ** Segment** 로 나뉩니다.
62+
63+ 이 경우에는 Layer 내부에서 Segment끼리는 자유롭게 import할 수 있습니다.
64+ (` shared ` 는 비즈니스 도메인이 없고, ` app ` 은 모든 도메인을 묶는 상위 조정자 역할을 합니다.)
4965
5066## Layer별 역할
5167
52- 각 Layer가 어떤 의미를 가지며, 어떤 코드를 포함하는지 정리했습니다.
68+ 이제 각 Layer가 어떤 의미를 가지는지,
69+ 그리고 보통 어떤 종류의 코드가 해당 Layer에 들어오는지 정리해 보겠습니다.
5370
5471### Shared
5572
56- 앱의 ** 기본 구성 요소** 를 모아둡니다.
57- 백엔드, 서드파티 라이브러리, 실행 환경과의 연결, 그리고 ** 높은 응집도의 내부 라이브러리** 를 포함합니다.
58-
59- - ` app ` 과 마찬가지로 ** Slice 없이 Segment로만 구성** 합니다.
60- - 비즈니스 도메인이 없으므로 ** Shared 안의 파일끼리는 자유롭게 Import** 할 수 있습니다.
61-
62- Segment 예시
63- - ` 📁 api ` — API 클라이언트와 백엔드 요청 함수
64- - ` 📁 ui ` — 공통 UI 컴포넌트
65- - ** 비즈니스 로직 제외** , ** 브랜드 테마 적용 가능**
66- - 로고, 레이아웃, 자동완성/검색창 등 UI 로직 포함 가능
67- - 자동완성/검색창 등 ** UI 로직 컴포넌트** 도 가능
68- - ` 📁 lib ` — 내부 라이브러리
69- - 단순 ` utils/helpers ` 모음이 아님 ([ 이 글 참고] [ ext-sova-utility-dump ] )
70- - 하나의 주제(날짜, 색상, 텍스트 등)에 집중
71- - README로 역할과 범위를 문서화
73+ Shared Layer는 앱의 ** 기본 구성 요소와 기반 도구들을 모아두는 곳** 입니다.
74+ 백엔드, 서드파티 라이브러리, 실행 환경과의 연결,
75+ 그리고 여러 곳에서 사용하는 ** 응집도 높은 내부 라이브러리** 가 여기에 위치합니다.
76+
77+ ` app ` 과 마찬가지로 ** Slice 없이 Segment로만 구성** 합니다.
78+ 비즈니스 도메인이 없기 때문에, ** Shared 내부의 파일들은 서로 자유롭게 import** 할 수 있습니다.
79+
80+ Segment 예시:
81+
82+ - ` 📁 api ` — API 클라이언트와 공통 백엔드 요청 함수
83+ - ` 📁 ui ` — 공통 UI 컴포넌트
84+ - ** 비즈니스 로직은 포함하지 않지만** , ** 브랜드 테마는 적용 가능**
85+ - 로고, 레이아웃, 자동완성/검색창 등 ** UI 자체 로직** 을 포함하는 컴포넌트는 허용
86+ - ` 📁 lib ` — 내부 라이브러리
87+ - 단순히 ` utils/helpers ` 를 모아두는 폴더가 아닙니다. ([ 이 글 참고] [ ext-sova-utility-dump ] )
88+ - 날짜, 색상, 텍스트 등 ** 하나의 주제에 집중** 해야 합니다.
89+ - README를 통해 역할과 범위를 문서화하는 것을 권장합니다.
7290- ` 📁 config ` — 환경변수, 전역 Feature Flag
7391- ` 📁 routes ` — 라우트 상수/패턴
7492- ` 📁 i18n ` — 번역 설정, 전역 문자열
7593
76- > Segment 이름은 ** 무엇을 하는 폴더인지 ** 드러나야 합니다.
77- > ` components ` , ` hooks ` , ` types ` 처럼 역할이 불명확한 이름은 피하세요.
94+ > Segment 이름은 ** 이 폴더가 무엇을 하는지 ** 를 명확하게 드러내야 합니다.
95+ > ` components ` , ` hooks ` , ` types ` 처럼 역할이 모호한 이름은 가급적 피하세요.
7896
7997### Entities
8098
81- 프로젝트에서 다루는 ** 핵심 데이터 개념** 을 나타냅니다 .
82- 비즈니스 용어(예: ` User ` , ` Post ` , ` Product ` )와 일치하는 경우가 많습니다 .
99+ Entities Layer는 프로젝트에서 다루는 ** 핵심 비즈니스 개념** 을 표현합니다 .
100+ 대부분의 경우, 실제 도메인 용어(예: ` User ` , ` Post ` , ` Product ` )와 일치합니다 .
83101
84- Entity Slice에는 다음을 포함할 수 있습니다:
102+ 각 Entity Slice에는 다음과 같은 것들을 포함할 수 있습니다.
85103
86- 구성
87- - ` 📁 model ` — 데이터 상태와 검증 스키마
88- - ` 📁 api ` — 해당 Entity의 API 요청
89- - ` 📁 ui ` — Entity의 시각적 표현
90- - 완전한 UI 블록이 아니어도 됨
91- - 여러 페이지에서 재사용 가능하게 설계
92- - 비즈니스 로직은 props/slot으로 연결 권장
104+ 구성:
93105
106+ - ` 📁 model ` — 데이터 상태, 도메인 로직, 검증 스키마
107+ - ` 📁 api ` — 해당 Entity와 관련된 API 요청
108+ - ` 📁 ui ` — Entity의 시각적 표현
109+ - 완성된 큰 UI 블록이 아니어도 됩니다.
110+ - 여러 페이지에서 재사용 가능한 형태로 설계합니다.
111+ - 비즈니스 로직은 가능하면 props/slot으로 외부에서 주입하는 방식을 권장합니다.
94112
95113#### Entity 간 관계
96114
97- 원칙적으로 Slice끼리는 서로 모르면 좋습니다.
98- 하지만 현실적으로 ** 다른 Entity를 포함** 하거나 ** 상호작용** 하는 경우가 있습니다.
99- 이때는 로직을 ** 상위 Layer(Feature/Page)** 로 올려 처리하세요.
115+ 원칙적으로는 Entity Slice끼리는 서로 ** 서로를 모르는 상태** 가 이상적입니다.
116+ 하지만 실제 애플리케이션에서는 한 Entity가 다른 Entity를 ** 포함하거나**
117+ 여러 Entity가 서로 ** 상호작용** 하는 일이 자주 발생합니다.
118+
119+ 이런 경우, 두 Entity 간의 구체적인 상호작용 로직은
120+ ** 상위 Layer(Feature 또는 Page)** 로 올려서 처리하는 것이 좋습니다.
100121
101- 만약 한 Entity 데이터에 다른 Entity가 포함된다면 ,
102- ` @x ` 표기를 사용해 ** 교차 Public API** 로 연결을 명시적으로 드러내세요 .
122+ 만약 한 Entity의 데이터 안에 다른 Entity가 포함되어야 한다면 ,
123+ ` @x ` 표기법을 사용해 ** 교차 Public API** 를 통해 연결되었음을 명시해 주세요 .
103124
104125``` ts title="entities/artist/model/artist.ts"
105126import type { Song } from " entities/song/@x/artist" ;
106127
107128export interface Artist {
108- name: string ;
109- songs: Array <Song >;
129+ name: string ;
130+ songs: Array <Song >;
110131}
111132```
112133
113- 자세한 내용은 [ Cross-Import를 위한 Public API] [ public-api-for-cross-imports ] 를 참고하세요.
134+ 자세한 내용은 [ Cross-Import를 위한 Public API] [ public-api-for-cross-imports ] 문서를 참고하세요.
114135
115136### Feature
116137
117- 사용자가 앱에서 수행하는 주요 기능을 담습니다.
118- 보통 특정 Entity와 연결됩니다.
138+ Features Layer에는 ** 사용자가 애플리케이션에서 수행하는 주요 기능** 이 들어갑니다.
139+ 보통 하나 이상의 Entity와 연관되어 동작합니다.
140+
141+ - 모든 동작을 무조건 Feature로 만들 필요는 없습니다.
142+ - ** 여러 페이지에서 재사용되는 기능** 일 때 Feature로 추출하는 것을 고려하세요.
143+ - 예: 여러 종류의 에디터에서 동일한 댓글 기능을 사용한다면, ` comments ` 를 Feature로 만들 수 있습니다.
144+ - Feature가 너무 많아지면, 중요한 기능이 어디 있는지 찾기 어려워질 수 있습니다.
119145
120- - 모든 기능을 Feature로 만들 필요는 없습니다. 여러 페이지에서 재사용되는 경우에만 고려하세요.
121- - 예: 여러 에디터에서 같은 댓글 기능을 쓴다면, comments를 Feature로 만듭니다.
122- - Feature가 너무 많으면 중요한 기능을 찾기 어려워집니다.
146+ 구성:
123147
124- 구성
125- - 📁 ui — 상호작용 UI(예: 폼)
126- - 📁 api — 기능 관련 API 요청
127- - 📁 model — 검증, 내부 상태
128- - 📁 config — Feature Flag
148+ - ` 📁 ui ` — 상호작용 UI (예: 폼, 검색 바 등)
149+ - ` 📁 api ` — 해당 기능과 직접 관련된 API 요청
150+ - ` 📁 model ` — 검증 로직, 내부 상태 관리
151+ - ` 📁 config ` — Feature Flag 등 기능별 설정
129152
130- > 새로운 팀원이 들어왔을 때 Page와 Feature만 봐도 앱 기능 구조를 파악할 수 있도록 구성하세요.
153+ > 새로운 팀원이 프로젝트에 합류했을 때,
154+ > Page와 Feature만 훑어봐도 ** 이 앱이 어떤 기능을 제공하는지** 를 대략 이해할 수 있도록 구성하는 것이 목표입니다.
131155
132156### Widget
133157
134- 독립적으로 동작하는 큰 UI 블록입니다 .
135- 여러 페이지에서 재사용되거나, 한 페이지에 큰 블록이 여럿 있을 때 유용합니다.
158+ Widgets Layer는 ** 독립적으로 동작하는 비교적 큰 UI 블록 ** 을 두는 곳입니다 .
159+ 여러 페이지에서 재사용되거나, 한 페이지에서 ** 큰 섹션 단위로 나누어지는 UI 블록 ** 이 있을 때 유용합니다.
136160
137161:::tip
138162
139- - 재사용되지 않고 특정 페이지의 핵심 콘텐츠라면 Widget으로 분리하지 말고 Page 안에 두세요.
140- - Nested Routing(예: [ Remix] [ ext-remix ] ) 환경에서는 Widget이 ** Page와 유사한 방식 ** 으로 동작합니다 .
141- 예를 들어, 데이터 로딩, 로딩 상태 표시, 에러 처리 등을 포함해 ** 하나의 라우터 단위 블록** 을 구성할 수 있습니다.
163+ 재사용되지 않고 특정 페이지의 핵심 콘텐츠에만 쓰인다면, 굳이 Widget으로 분리하지 말고 Page 내부에 두는 것이 좋습니다.
164+ Nested Routing(예: [ Remix] [ ext-remix ] ) 환경에서는 Widget이 ** Page와 비슷한 역할 ** 을 할 수 있습니다 .
165+ 예를 들어 데이터 로딩, 로딩 상태 표시, 에러 처리 등을 모두 포함하는 ** 하나의 라우터 단위 UI 블록** 으로 동작할 수 있습니다.
142166
143167:::
144168
145169### Page
146170
147- 웹/앱의 ** 화면(screen) 또는 액티비티(activity)** 에 해당합니다.
148- 대부분 페이지 1개 = Slice 1개이지만, 유사한 페이지는 하나의 Slice로 묶을 수 있습니다.
171+ Pages Layer는 웹/앱에서 보이는 ** 화면(screen) 또는 액티비티(activity)** 에 해당합니다.
172+ 일반적으로 “페이지 1개 = Slice 1개” 구조를 많이 사용하지만,
173+ 구조가 유사한 페이지들은 하나의 Slice로 묶는 것도 가능합니다.
149174
150- - 코드 찾기만 쉽다면 Page Slice 크기 제한은 없습니다.
151- - 재사용되지 않는 UI는 Page 안에 둡니다.
152- - 보통 전용 모델은 없고, 간단한 상태만 컴포넌트 내부에서 관리합니다.
175+ 코드를 찾기만 쉽다면, Page Slice의 크기에 특별한 제한은 없습니다.
176+ 재사용되지 않는 UI는 그대로 Page 내부에 두면 됩니다.
177+ Page Layer에는 보통 전용 model이 없으며, 필요한 경우 간단한 상태만 컴포넌트 내부에서 관리합니다.
153178
154- 구성 예
155- - 📁 ui — UI, 로딩 상태, 에러 처리
156- - 📁 api — 데이터 패칭/변경 요청
179+ 구성 예:
180+
181+ - ` 📁 ui ` — 페이지 UI, 로딩 상태, 에러 상태 처리
182+ - ` 📁 api ` — 페이지에서 사용하는 데이터 패칭/변경 요청
157183
158184### Process
159185
160186:::caution
161187
162- Deprecated — 기존 코드는 feature나 app으로 옮기세요 .
188+ ** Deprecated** — 기존에 사용하던 코드는 가능하면 Feature나 App Layer로 이동하세요 .
163189
164190:::
165191
166- 과거에는 여러 페이지를 넘나드는 기능을 위한 탈출구 역할이었지만,
167- 정의가 모호해 대부분의 앱에서는 사용하지 않습니다.
168- 라우터, 서버 레벨 로직은 App Layer에 두고, App이 너무 커질 때만 제한적으로 고려하세요.
192+ 과거에는 여러 페이지를 넘나드는 복잡한 기능을 처리하기 위한 ** 탈출구 같은 Layer** 로 사용되었습니다.
193+ 하지만 역할이 모호하고, 대부분의 애플리케이션에서는 굳이 사용하지 않아도 충분히 설계가 가능합니다.
194+
195+ 라우터, 서버 연동 같은 전역적인 로직은 보통 App Layer에 둡니다.
196+ App Layer가 너무 복잡해질 때 정말 필요한 경우에만 제한적으로 고려할 수 있습니다.
169197
170198### App
171199
172- 앱 전역에서 동작하는 ** 환경 설정** 과 ** 공용 로직** 을 관리하는 Layer입니다.
173- 예를 들어, 라우터 설정, 전역 상태 관리, 글로벌 스타일, 진입점 설정 등 ** 앱 전체에 영향을 주는 코드** 를 둡니다.
200+ App Layer는 앱 전역에서 동작하는 ** 환경 설정** 과 ** 공용 로직** 을 관리하는 곳입니다.
201+ 예를 들어 라우터 설정, 전역 상태 관리(Store 설정), 글로벌 스타일 앱 진입점(Entry Point) 설정 등과 같이
202+ ** 앱 전체에 영향을 주는 코드** 가 위치합니다.
203+ ` shared ` 와 마찬가지로 Slice 없이 ** Segment만으로 구성** 합니다.
204+
205+ 대표적인 Segment 예:
174206
175- - shared처럼 Slice 없이 Segment로 구성합니다.
176- - 대표 Segment:
177- - ` 📁 routes ` — Router 설정
178- - ` 📁 store ` — Global State Store 설정
179- - ` 📁 styles ` — Global Style
180- - ` 📁 entrypoint ` — Application Entry Point와 Framework 설정
207+ - ` 📁 routes ` — Router 설정
208+ - ` 📁 store ` — Global State Store 설정
209+ - ` 📁 styles ` — Global Style
210+ - ` 📁 entrypoint ` — Application Entry Point와 Framework 설정
181211
182212[ public-api-for-cross-imports ] : /docs/reference/public-api#public-api-for-cross-imports
183213[ ext-remix ] : https://remix.run
0 commit comments