프로젝트 디렉토리 구성
dictionary-app 프로젝트의 실제 디렉토리 구조를 예시로 삼는다.
디렉토리 공통 규칙
Section titled “디렉토리 공통 규칙”- 디렉토리 이름은 소문자로 쓰고, 단어 사이는 하이픈(
-)으로 구분한다. 언더바(_)는 쓰지 않는다. - 디렉토리마다
index.ts를 두어 안의 컴포넌트/하위 디렉토리를 export한다. export 경로에는 확장자를 붙이지 않는다(export * from './Domain'). - 외부에서 가져올 때는
@/utils처럼 최상위 디렉토리 별칭(배럴)으로만 참조한다. - 단, 최상위 배럴은 그 디렉토리 바깥에서만 쓴다.
hooks/utils/components내부에서 같은 디렉토리의 다른 파일을 참조할 때는 상대 경로나 더 좁은 하위 배럴(../apis,./stores)로 가져온다. 자기 배럴을 되돌아 가져오면 순환 참조가 생기고 번들 그래프도 불필요하게 커진다. - 디렉토리는 필요할 때만 만든다. 빈 디렉토리는 만들지 않는다.
최상위 디렉토리 구조
Section titled “최상위 디렉토리 구조”Directorysrc/
- main.tsx 엔트리 포인트
- App.tsx 루트 컴포넌트
Directorycomponents/ 컴포넌트(Atomic Design)
- …
Directoryhooks/ 커스텀 훅
- …
Directorylayout/ 레이아웃
- …
Directorypages/ 페이지
- …
Directoryroutes/ 라우팅 설정
- …
Directorytheme/ 테마
- …
Directorytypes/ 공통 타입
- …
Directoryutils/ 공통 함수
- …
- index.css
routes/, theme/, types/, utils/는 내부 구성에 별도 규칙을 두지 않고 자유롭게 생성한다. 단, 위
디렉토리 공통 규칙(index.ts 배럴, @/ 최상위 import)은 동일하게 적용한다.
컴포넌트 디렉토리 (Atomic Design)
Section titled “컴포넌트 디렉토리 (Atomic Design)”Directorycomponents/
Directoryatoms/ 최소 단위 컴포넌트(버튼, 인풋, 아이콘 등)
Directorybutton/
- Button.tsx
- StyledButton.ts
- index.ts
- index.ts
Directorymolecules/ atom을 조합한 재사용 단위(검색창, 폼 필드, 모달 등)
Directorymodal/
- Modal.tsx
- index.ts
- index.ts
Directoryorganisms/ atom·molecule을 조합한 독립 UI 영역(헤더, 그리드, 토스터 등)
Directorydata-grid/
- DataGrid.tsx
- index.ts
- index.ts
- index.ts
- atoms/molecules/organisms 아래에는 컴포넌트마다 디렉토리를 만들고, 컴포넌트 파일(
ComponentName.tsx)과index.ts를 둔다. - 컴포넌트 디렉토리 이름은 컴포넌트 이름을 케밥 케이스로 바꿔 짓는다(
DataGrid→data-grid). - 컴포넌트 디렉토리의
index.ts는 그 컴포넌트만 export한다. - 별도 스타일이 필요하면 같은 디렉토리에
Styled{컴포넌트 이름}.ts를 둔다.
커스텀 훅 디렉토리
Section titled “커스텀 훅 디렉토리”Directoryhooks/
Directoryapis/ 백엔드 연결 훅 (
Api접미사, 예: useMenuApi)- index.ts
Directoryqueries/ react-query 조회(useQuery) 훅 (
Query접미사, 예: useMenuRootQuery)- index.ts
Directorymutations/ react-query 저장(useMutation) 훅 (
Mutation접미사, 예: useDomainSaveMutation)- index.ts
Directorystores/ zustand 스토어 훅 (
Store접미사, 예: useSpinnerStore)- index.ts
- useApi.ts 별도 디렉토리로 분류되지 않는 공통 훅
- index.ts
apis/queries/mutations/stores에는 여러 기능이 함께 쓰는 공통 훅을 둔다. 특정 페이지에서만 쓰는 훅은 그 페이지 디렉토리 아래에 같은 형식으로 둔다(아래 페이지 디렉토리 참고).- 훅 함수명은
use로 시작한다. apis/queries/mutations/stores에 있는 훅은 소속 디렉토리를 나타내는 접미사를 붙인다. 공통 훅과 페이지 전용 훅 모두 같다.apis:Api접미사(useAuthApi,useMenuApi)queries:Query접미사(useMenuRootQuery,useMenuChildQuery)mutations:Mutation접미사(useDomainSaveMutation,useDictionaryDeleteMutation)stores:Store접미사(useSideMenuStore,useSpinnerStore)
queries(조회)와mutations(저장)를 분리하는 이유는 백엔드 API 사용 예제 참고.
페이지 디렉토리
Section titled “페이지 디렉토리”Directorypages/word/domain/
Directorycomponents/ 페이지 전용 하위 컴포넌트
- DomainFormModal.tsx
- index.ts
Directoryapis/ 페이지 전용 백엔드 연결 훅
- useDomainApi.ts
- index.ts
Directoryqueries/ 페이지 전용 조회 훅
- useDomainListQuery.ts
- index.ts
Directorymutations/ 페이지 전용 저장 훅
- useDomainSaveMutation.ts
- index.ts
Directorymodels/ 페이지 전용 타입
- DomainFilter.ts
- index.ts
- useDomain.ts 페이지 전용 커스텀 훅(상수·헬퍼 포함)
- Domain.tsx 페이지 컴포넌트
- StyledDomain.ts
- index.ts 페이지 컴포넌트만 export
- 페이지 경로는 메뉴 구조와 같게 중첩할 수 있다(
pages/word/domain). - 페이지 전용 하위 컴포넌트와
apis/queries/mutations/stores는 페이지 디렉토리 아래 같은 이름의 디렉토리에 둔다. 공통 훅처럼 훅마다 파일을 나누고,index.ts가 다시 export한다. - 페이지 전용 타입은
types가 아니라models디렉토리에 둔다. 최상위@/types(생성된 API 스키마 등 공용 타입)와 이 페이지에서만 쓰는 업무 모델(검색 조건 타입 등)을 위치만으로 구분하기 위해서다. - 페이지 전용 커스텀 훅(
useDomain.ts)은 페이지 디렉토리 바로 아래에 두고, 상수·헬퍼 함수도 함께 정의한다. 단, 하위 컴포넌트 여러 개가 함께 쓰는 순수 헬퍼 함수는 같은components디렉토리에 별도.ts파일로 둔다(예:pages/word/column/components/columnName.ts). 컴포넌트 파일(.tsx)이 함수를 함께 export하면 oxlintreact/only-export-components경고가 생기기 때문이다. 이 파일은 컴포넌트 전용 내부 구현이므로components/index.ts에서 재-export하지 않는다. - 페이지 스타일 파일명은
Styled{페이지 컴포넌트 이름}.ts로 짓는다. - 페이지 디렉토리의
index.ts는 페이지 컴포넌트만 export한다. 하위 컴포넌트와 훅은 페이지 안에서만 쓰므로 외부에 노출하지 않는다.
layout 디렉토리
Section titled “layout 디렉토리”Directorylayout/
Directoryapp-layout/
- AppLayout.tsx
- index.ts
Directoryheader/
- Header.tsx
- index.ts
Directoryside-menu/
- SideMenu.tsx
- index.ts
Directorycontent/
- Content.tsx
- index.ts
Directoryfooter/
- Footer.tsx
- index.ts
- index.ts
app-layout이 전체 레이아웃을 정의한다 — 왼쪽에 side-menu, 오른쪽에 header → content → footer가
세로로 순서대로 위치한다.
flowchart LR
subgraph app["app-layout"]
direction LR
side["side-menu"]
subgraph main[" "]
direction TB
header["header"] ~~~ content["content<br/>(Outlet)"] ~~~ footer["footer"]
end
end