공식 React 파일 구조는 없어. 그게 feature 야. 비용은 본인이 골라야 한다는 것 — 잘못 고르면 빠르게 썩어.
두 가지 조직 축
모든 React 프로젝트가 (의식하든 안 하든) 두 축에서 위치 골라:
- type 별 vs feature 별. type-first 는 hook 다
hooks/, 컴포넌트 다components/, 유틸 다lib/. feature-first 는 feature 의 hook + 컴포넌트 + 유틸을features/chat/,features/auth/에 같이. - flat vs deep. flat 은 nesting 1-2 단계; deep 은 5-6 단계까지.
초기엔 type-first 가 이김 (카테고리로 찾기 쉬움). 나중엔 feature-first 가 이김 (feature 통째로 삭제/추출 쉬움). 전환점은 컴포넌트 50개 즈음.
cwkPippa 프런트엔드 레이아웃
cwkPippa 프런트엔드는 feature-first, 2 단계 깊이:
src/
App.tsx # 상태 lifting + 라우트 마운트
main.tsx # 엔트리
index.css # Tailwind v4 + 테마 토큰
components/
chat/ # InputArea, MessageList, MessageItem
sidebar/ # ConversationList, FolderTree
council/ # Council UI surface
admin/ # admin 패널
settings/ # settings UI
hooks/ # useChat, useConversations, useHeartbeat
lib/ # api.ts, formatter, 상수
types/ # 공유 TS 타입
주목: 컴포넌트는 feature 별 (chat/, sidebar/, council/) 인데 hook + 타입은 공유라서 type-first 야. 그 hybrid 가 정상 — 디렉토리마다 올바른 축 골라.
State-lifting 질문
cwkPippa 의 App.tsx 는 state-lifting root: 최상위 state 가 거기 살고 자식 컴포넌트는 props + callback 받음. 어떤 프로젝트는 state 를 Zustand store / Context provider 로 미는 대신. 정답 없어 — 다만 일관성 중요. 하나 골라 유지.
Path alias
긴 상대 경로 import (../../../components/chat/MessageList) 는 smell. Vite 가 tsconfig.json 의 paths 필드로 path alias 지원. @/* 를 src/* 로 맞추면 import 가 @/components/chat/MessageList — 읽기 쉽고 리팩토링에 강해.
이 파일을 찾고 싶을 폴더. 새 파일 만들 때 물어봐: '3개월 뒤 이 파일 찾으러 어디 먼저 가 볼까?' 거기에 놔. 답이 'grep 할 거야' 면, 구조 이미 깨진 거야.