[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -2,149 +2,221 @@
|
||||
id: wiki-2026-0508-folder-structure-best-practices
|
||||
title: Folder Structure Best Practices
|
||||
category: 10_Wiki/Topics
|
||||
status: needs_review
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: []
|
||||
aliases: [Project Structure, Code Organization, Frontend Folder Layout]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.92
|
||||
tags: [uncategorized]
|
||||
confidence_score: 0.85
|
||||
verification_status: applied
|
||||
tags: [architecture, folder-structure, frontend, project-organization]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-08
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
inferred_by: Claude Opus 4.7 (auto-normalize 2026-05-08)
|
||||
tech_stack:
|
||||
language: unspecified
|
||||
framework: unspecified
|
||||
language: typescript
|
||||
framework: react-next
|
||||
---
|
||||
|
||||
# [[Folder Structure Best Practices|Folder Structure Best Practices]]
|
||||
# Folder Structure Best Practices
|
||||
|
||||
## 📌 한 줄 통찰 (The Karpathy Summary)
|
||||
React 등 프론트엔드 프로젝트에서 코드의 유지보수성, 확장성, 그리고 협업 효율성을 높이기 위해 파일과 디렉터리를 체계적으로 구성하는 방법론입니다 [1]. 현대적인 애플리케이션에서는 과거의 파일 유형 기반(유형별 분류) 구조에서 벗어나, 기능(Feature)이나 도메인 중심으로 관련된 로직을 묶는 하이브리드 또는 기능 기반 방식이 모범 사례로 권장됩니다 [2, 3]. 이를 통해 UI, 비즈니스 로직, 상태 관리 등의 관심사를 명확히 분리하고 프로젝트가 커짐에 따라 발생하는 기술 부채를 최소화할 수 있습니다 [4].
|
||||
## 매 한 줄
|
||||
> **"매 folder structure 의 골 — colocation by feature, not by file-type"**. 매 modern frontend (2026) 의 favored: feature-sliced design, screaming architecture, Next.js app router colocation. 매 type-based folders (`components/`, `utils/`) 의 rejection — feature 의 boundary 의 unclear.
|
||||
|
||||
## 📖 Core 소스 Content
|
||||
## 매 핵심
|
||||
|
||||
* **구조의 진화와 한계:**
|
||||
* 초기 소규모 프로젝트는 주로 모든 컴포넌트를 `components` 폴더에, 모든 훅을 `hooks` 폴더에 넣는 플랫(Flat) 구조나 파일 유형 기반 구조로 시작합니다 [5, 6].
|
||||
* 하지만 앱의 규모가 커지면 단일 기능을 수정하기 위해 여러 폴더를 넘나들어야 하므로, 개발 속도가 느려지고 디버깅이 어려워지며 코드베이스가 복잡해지는 한계가 발생합니다 [3, 6, 7].
|
||||
* **기능 기반(Feature-based) 및 하이브리드 구조:**
|
||||
* 2025년 기준 가장 권장되는 접근 방식은 파일 유형이 아닌 비즈니스 기능이나 모듈을 중심으로 폴더를 구성하는 것입니다 [2, 8, 9].
|
||||
* 각 기능(Feature)은 캡슐화되어 다른 기능과 독립적으로 작동할 수 있으므로, 규모 확장 시 기존 코드에 영향을 주지 않고 새로운 기능을 매끄럽게 추가할 수 있습니다 [8, 10].
|
||||
* **권장 디렉터리 구성 (src/ 하위):**
|
||||
* `assets/`: 이미지, 폰트 등 정적 미디어 리소스 보관 [11, 12].
|
||||
* `components/`: 여러 기능에서 공통으로 재사용되는 도메인에 구애받지 않는 UI 요소 (예: 버튼, 모달, 네비게이션 바 등) [2, 12, 13].
|
||||
* `features/` (또는 `modules/`): 인증(Auth), 대시보드(Dashboard) 등 도메인별 비즈니스 로직. 이 폴더 내부에는 해당 기능에만 쓰이는 컴포넌트, 훅, API 등을 캡슐화하여 보관합니다 [2, 9, 13].
|
||||
* `hooks/`: 폼 처리, 데이터 페칭 등 앱 전반에서 재사용 가능한 커스텀 훅 [9, 14].
|
||||
* `pages/` (또는 `routes/`): 라우팅에 매핑되는 페이지 레벨 컴포넌트 [15, 16].
|
||||
* `services/`: 서드파티 서비스 연동이나 API 요청 등 외부 통신 로직 [16, 17].
|
||||
* `store/` (또는 `context/`): Redux, Zustand, Context API를 활용하는 전역 상태 관리 로직 [14-16].
|
||||
* `utils/`: 날짜 포맷팅, 데이터 유효성 검사 등 상태를 가지지 않는 유틸리티 함수 [17, 18].
|
||||
* `styles/`: 글로벌 CSS, 테마(Theme) 등 전역 스타일링 파일 [18, 19].
|
||||
* `types/`: TypeScript 사용 시 전역으로 사용되는 타입 및 인터페이스 보관 [18].
|
||||
* `config/`: 환경 변수나 애플리케이션 전역 설정(API 기본 URL 등) 관리 [18, 20].
|
||||
* **Feature-Sliced Design (FSD):**
|
||||
* 기능 기반 폴더 구조보다 더 엄격하게 의존성의 방향을 통제하는 프론트엔드 아키텍처 방법론입니다 [21].
|
||||
* `shared` -> `entities` -> `features` -> `widgets` -> `pages` -> `app` 이라는 고정된 다층 계층(Layer)을 가집니다 [22, 23].
|
||||
* 상위 계층은 하위 계층의 코드를 가져올 수 있지만(Import), 하위 계층은 상위 계층을 참조할 수 없는 단방향 의존성 규칙을 통해 순환 의존성을 방지합니다 [22, 24].
|
||||
* **Next.js 환경에서의 라우트 그룹 (Route Groups):**
|
||||
* Next.js 프로젝트에서는 괄호를 사용한 폴더명 `(folderName)` 방식을 통해, 실제 URL 경로에는 영향을 주지 않으면서도 관련 기능이나 논리에 따라 라우트를 깔끔하게 그룹화할 수 있습니다 [25-27].
|
||||
### 매 Type-based (anti) vs Feature-based (good)
|
||||
- **Type-based**: `/components`, `/hooks`, `/utils`, `/services`, `/types` — every change touches 5 folders.
|
||||
- **Feature-based**: `/features/checkout/{components,hooks,api,types}` — change scope = single folder.
|
||||
|
||||
## 🔗 지식 연결 (Graph)
|
||||
### Related Concepts
|
||||
- [[Feature-Sliced Design|Feature-Sliced Design]]
|
||||
- 연결 이유: 대규모 React 애플리케이션의 폴더 구조를 구축하기 위해 고안된 전문적인 프론트엔드 아키텍처 방법론이기 때문입니다 [21].
|
||||
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 폴더 간의 단방향 의존성 규칙과 각 폴더(Layer, Slice, Segment)가 담당해야 하는 역할의 엄격한 분리 방식 [22, 28].
|
||||
### 매 Levels of Hierarchy
|
||||
1. **Routes / pages** (Next.js `app/`, Remix `routes/`).
|
||||
2. **Features** (`features/<feature>/...`) — domain boundaries.
|
||||
3. **Shared** (`lib/`, `shared/`, `ui/`) — cross-feature reusables.
|
||||
4. **Entities/domain** (Feature-Sliced: `entities/user`, `entities/cart`).
|
||||
|
||||
- [[_뇌와 팔다리의 분리_ - 관심사의 분리 (Separation of Concerns)|Separation of Concerns]] (관심사의 분리)
|
||||
- 연결 이유: 폴더 구조를 설계하는 근본적인 목적이 UI 렌더링, 전역 상태 관리, 데이터 통신(API) 등의 책임을 각기 다른 위치로 분리하는 데 있기 때문입니다 [4, 29].
|
||||
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: `services/`, `store/`, `components/` 등의 폴더를 분리하여 단일 책임 원칙(SRP)을 프론트엔드 아키텍처 전반에 적용하는 방법 [4, 30].
|
||||
### 매 Boundaries
|
||||
- Features 의 each other 의 import X (cross-feature 의 shared layer 의 통과).
|
||||
- Lower layers 의 higher 의 import X (UI 의 feature 의 import X).
|
||||
- 매 ESLint `no-restricted-imports` 또는 `eslint-plugin-boundaries` 의 enforce.
|
||||
|
||||
- [[Naming Conventions|Naming Conventions]] (명명 규칙)
|
||||
- 연결 이유: 일관된 폴더 및 파일 명명 규칙(예: 폴더명은 kebab-case, 컴포넌트는 PascalCase)은 폴더 구조 내에서 파일을 예측 가능하게 찾고 충돌을 방지하는 핵심 규칙이기 때문입니다 [31-33].
|
||||
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 다양한 운영체제와 CI/CD 파이프라인에서 빌드 에러를 방지하고 팀 내 코드 가독성을 유지하는 방법 [34, 35].
|
||||
### 매 응용
|
||||
1. Next.js 15 app router project.
|
||||
2. Vite + React SPA.
|
||||
3. Monorepo (Turborepo, Nx).
|
||||
4. React Native app.
|
||||
|
||||
### Deeper Research Questions
|
||||
- 기능 기반(Feature-based) 폴더 구조에서 각 기능이 상호작용해야 할 때 발생하는 교차 관심사(Cross-cutting concerns)나 공유 의존성을 어떻게 관리하고 해결할 수 있는가?
|
||||
- 레거시 파일 유형 기반(File-type based) React 프로젝트를 기능 기반 혹은 Feature-Sliced Design으로 점진적으로 마이그레이션하기 위한 가장 안전하고 효율적인 단계는 무엇인가?
|
||||
- Feature-Sliced Design의 단방향 의존성 원칙을 ESLint와 같은 정적 분석 도구로 자동 강제화(Governance)하는 방법은 무엇인가?
|
||||
- 폴더 구조를 모듈화할 때 발생하는 파일 중첩 문제와 이를 피하기 위한 적절한 인덱스(Barrel) 파일 사용 전략의 장단점은 무엇인가?
|
||||
- 상태 관리 라이브러리(Context API, Zustand, Redux 등)의 종류에 따라 권장되는 `store/` 폴더 내부의 구조는 어떻게 달라져야 하는가?
|
||||
## 💻 패턴
|
||||
|
||||
### Practical Application Contexts
|
||||
- **Implementation:** React 컴포넌트를 생성할 때, 모든 요소를 `components/` 폴더에 넣지 않고 특정 도메인(예: 인증)에만 쓰이는 요소는 `features/auth/components/`로 격리하여 캡슐화를 실천합니다.
|
||||
- **System Design:** 프로젝트 초기 세팅 단계에서 비즈니스 도메인을 분석하여 어떤 코드가 전역(`shared/` 또는 `components/`)에 속하고 어떤 코드가 로컬(`features/`)에 속할지 기준을 마련합니다.
|
||||
- **Operation / Maintenance:** 기능에 버그가 발생했을 때, 해당 기능의 폴더(`features/feature-name/`)만 확인하면 UI, 상태, API 요청 로직이 모여 있어 디버깅 및 유지보수 속도가 크게 향상됩니다.
|
||||
- **Learning Path:** 처음에는 단순한 플랫 구조로 React를 학습한 후, 컴포넌트가 30개 이상으로 늘어나는 시점에 기능 기반 폴더 구조를 도입하여 아키텍처 설계 역량을 기를 수 있습니다.
|
||||
- **My Project Relevance:** 현재 진행 중이거나 리팩토링해야 할 React 코드베이스에서, 거대해진 `components/` 폴더를 도메인 단위의 `features/` 폴더로 나누고 재사용 불가 로직들을 분리하는 데 직접적으로 적용됩니다.
|
||||
|
||||
### Adjacent Topics
|
||||
- [[상태 관리(State Management)|State Management]]
|
||||
- 확장 방향: 전역 상태(Global State)와 로컬 상태(Local State)를 어디에 보관해야 하는지, Zustand와 같은 도구가 `store/` 폴더의 구조를 어떻게 단순화하는지 확장하여 조사할 수 있습니다.
|
||||
- [[Code Splitting|Code Splitting]] (코드 스플리팅)
|
||||
- 확장 방향: 라우트 혹은 폴더(Feature) 단위로 코드 스플리팅과 지연 로딩(Lazy Loading)을 적용하여 초기 번들 크기를 줄이고 성능을 최적화하는 전략과 연결됩니다.
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-30*
|
||||
|
||||
## 📖 구조화된 지식 (Synthesized Content)
|
||||
|
||||
**추출된 패턴:**
|
||||
> *(TODO)*
|
||||
|
||||
**세부 내용:**
|
||||
- *(TODO)*
|
||||
|
||||
## 🤖 LLM 활용 힌트 (How to Use This Knowledge)
|
||||
|
||||
**언제 이 지식을 쓰는가:**
|
||||
- *(TODO)*
|
||||
|
||||
**언제 쓰면 안 되는가:**
|
||||
- *(TODO)*
|
||||
|
||||
## 🧪 검증 상태 (Validation)
|
||||
|
||||
- **정보 상태:** needs_review
|
||||
- **출처 신뢰도:** A
|
||||
- **검토 이유:** *(P-Reinforce Phase 1 자동 정규화. 본문 검증 필요.)*
|
||||
|
||||
## 🧬 중복 검사 (Duplicate Check)
|
||||
|
||||
- **기존 유사 문서:** *(TODO: 인덱서 클러스터 리포트 참조)*
|
||||
- **처리 방식:** UPDATE (자동 정규화)
|
||||
- **처리 이유:** Phase 1 정규화 — 옛 템플릿/누락 필드 보강.
|
||||
|
||||
## ⚠️ 모순 및 업데이트 (Contradictions & Updates)
|
||||
|
||||
- **과거 데이터와의 충돌:** 없음
|
||||
- **정책 변화:** 없음
|
||||
|
||||
## 🕓 변경 이력 (Changelog)
|
||||
|
||||
| 날짜 | 변경 내용 | 처리 방식 | 신뢰도 |
|
||||
|------|-----------|-----------|--------|
|
||||
| 2026-05-08 | P-Reinforce Phase 1 정규화 (frontmatter + 헤더 표준화) | UPDATE | A |
|
||||
|
||||
## 💻 코드 패턴 (Code Patterns)
|
||||
|
||||
**패턴 1:** *(TODO: 이 프로젝트 컨벤션 반영한 구조 스켈레톤)*
|
||||
|
||||
```text
|
||||
# TODO
|
||||
### Feature-Sliced (recommended 2026)
|
||||
```
|
||||
src/
|
||||
├── app/ # app entry, providers, global styles
|
||||
├── pages/ # (or routes/) page-level composition
|
||||
├── features/ # business actions (toggle-cart, send-comment)
|
||||
│ └── send-comment/
|
||||
│ ├── ui/
|
||||
│ ├── model/ # state, hooks
|
||||
│ ├── api/
|
||||
│ └── index.ts # public API
|
||||
├── entities/ # business entities (user, post, cart)
|
||||
│ └── user/
|
||||
│ ├── ui/
|
||||
│ ├── model/
|
||||
│ └── api/
|
||||
├── shared/ # reusable, domain-agnostic
|
||||
│ ├── ui/ # buttons, inputs (design system)
|
||||
│ ├── lib/ # utilities
|
||||
│ ├── api/ # base http client
|
||||
│ └── config/
|
||||
└── widgets/ # composite UI blocks (Header, Sidebar)
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준 (Decision Criteria)
|
||||
### Next.js 15 App Router (colocation)
|
||||
```
|
||||
app/
|
||||
├── layout.tsx
|
||||
├── page.tsx
|
||||
├── (marketing)/ # route group, no URL segment
|
||||
│ └── about/page.tsx
|
||||
├── dashboard/
|
||||
│ ├── layout.tsx
|
||||
│ ├── page.tsx
|
||||
│ ├── _components/ # private (underscore = not routable)
|
||||
│ │ └── Sidebar.tsx
|
||||
│ ├── settings/
|
||||
│ │ ├── page.tsx
|
||||
│ │ └── actions.ts # server actions
|
||||
│ └── api/
|
||||
│ └── route.ts
|
||||
src/
|
||||
├── lib/ # shared utilities, db, auth
|
||||
├── components/ui/ # design system
|
||||
└── features/ # cross-page features
|
||||
```
|
||||
|
||||
**선택 A를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
### Vite + React SPA (mid-size)
|
||||
```
|
||||
src/
|
||||
├── main.tsx
|
||||
├── App.tsx
|
||||
├── routes/ # react-router routes
|
||||
├── features/
|
||||
│ └── auth/
|
||||
│ ├── components/
|
||||
│ ├── hooks/
|
||||
│ ├── api.ts
|
||||
│ ├── types.ts
|
||||
│ └── index.ts
|
||||
├── components/ui/ # design system
|
||||
├── hooks/ # generic hooks
|
||||
├── lib/
|
||||
└── types/
|
||||
```
|
||||
|
||||
**선택 B를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
### Monorepo (Turborepo)
|
||||
```
|
||||
apps/
|
||||
├── web/ # Next.js app
|
||||
├── mobile/ # Expo
|
||||
└── docs/
|
||||
packages/
|
||||
├── ui/ # shared design system
|
||||
├── config/ # eslint, ts, tailwind presets
|
||||
├── api/ # tRPC / generated client
|
||||
└── db/ # Prisma schema
|
||||
```
|
||||
|
||||
**기본값:**
|
||||
> *(TODO)*
|
||||
### Feature Public API (barrel)
|
||||
```ts
|
||||
// features/checkout/index.ts — explicit exports only
|
||||
export { CheckoutPage } from './ui/CheckoutPage';
|
||||
export { useCheckout } from './model/useCheckout';
|
||||
export type { CheckoutState } from './model/types';
|
||||
// internals (api/, model/internal) NOT re-exported
|
||||
```
|
||||
|
||||
## ❌ 안티패턴 (Anti-Patterns)
|
||||
### Boundary Enforcement (eslint-plugin-boundaries)
|
||||
```js
|
||||
// eslint.config.js
|
||||
import boundaries from 'eslint-plugin-boundaries';
|
||||
|
||||
- **[안티패턴]:** *(TODO: 무엇을 하면 안 되는가 + 이유 + 대신 무엇을)*
|
||||
export default [{
|
||||
plugins: { boundaries },
|
||||
settings: {
|
||||
'boundaries/elements': [
|
||||
{ type: 'shared', pattern: 'src/shared/*' },
|
||||
{ type: 'entity', pattern: 'src/entities/*' },
|
||||
{ type: 'feature', pattern: 'src/features/*' },
|
||||
{ type: 'widget', pattern: 'src/widgets/*' },
|
||||
{ type: 'page', pattern: 'src/pages/*' },
|
||||
],
|
||||
},
|
||||
rules: {
|
||||
'boundaries/element-types': ['error', {
|
||||
default: 'disallow',
|
||||
rules: [
|
||||
{ from: 'page', allow: ['widget', 'feature', 'entity', 'shared'] },
|
||||
{ from: 'widget', allow: ['feature', 'entity', 'shared'] },
|
||||
{ from: 'feature', allow: ['entity', 'shared'] },
|
||||
{ from: 'entity', allow: ['shared'] },
|
||||
{ from: 'shared', allow: ['shared'] },
|
||||
],
|
||||
}],
|
||||
},
|
||||
}];
|
||||
```
|
||||
|
||||
### TS Path Aliases
|
||||
```json
|
||||
// tsconfig.json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"@shared/*": ["src/shared/*"],
|
||||
"@features/*": ["src/features/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| Small SPA (<20 components) | Flat or simple `components/` + `pages/` |
|
||||
| Mid-size (20-200) | Feature-based (`features/` + `shared/`) |
|
||||
| Large / team scale | Feature-Sliced Design + boundary lint |
|
||||
| Next.js project | App router colocation + `src/features/` |
|
||||
| Monorepo | Turborepo `apps/` + `packages/` |
|
||||
|
||||
**기본값**: feature-based + shared layer + ESLint boundary enforcement.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Software Architecture]] · [[Frontend Architecture]]
|
||||
- 변형: [[Feature-Sliced Design]] · [[Atomic Design]] · [[Screaming Architecture]]
|
||||
- 응용: [[Monorepo]] · [[Design System]]
|
||||
- Adjacent: [[Next.js]] · [[Turborepo]] · [[Module Boundaries]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: project structure 의 design, refactor folder layout, boundary rule 의 setup.
|
||||
**언제 X**: tiny prototype — overhead 의 not worth.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Type-based root** (`/components`, `/hooks`, `/utils`): scales poorly.
|
||||
- **Deeply nested barrels**: circular import / slow build.
|
||||
- **God `utils/`**: dump folder. 매 specific domain 의 split.
|
||||
- **Cross-feature imports**: feature isolation 의 break — shared 의 use.
|
||||
- **`index.ts` 의 every file의 leak**: tree-shaking break, public API 의 unclear.
|
||||
- **`pages/` 안에 business logic**: route 는 thin, feature 의 delegate.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Feature-Sliced Design docs, Next.js project structure docs, Bulletproof React).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — folder structure best practices full content |
|
||||
|
||||
Reference in New Issue
Block a user