Skip to content

프로젝트 디렉토리 구성

dictionary-app 프로젝트의 실제 디렉토리 구조를 예시로 삼는다.

  1. 디렉토리 이름은 소문자로 쓰고, 단어 사이는 하이픈(-)으로 구분한다. 언더바(_)는 쓰지 않는다.
  2. 디렉토리마다 index.ts를 두어 안의 컴포넌트/하위 디렉토리를 export한다. export 경로에는 확장자를 붙이지 않는다(export * from './Domain').
  3. 외부에서 가져올 때는 @/utils처럼 최상위 디렉토리 별칭(배럴)으로만 참조한다.
  4. 단, 최상위 배럴은 그 디렉토리 바깥에서만 쓴다. hooks/utils/components 내부에서 같은 디렉토리의 다른 파일을 참조할 때는 상대 경로나 더 좁은 하위 배럴(../apis, ./stores)로 가져온다. 자기 배럴을 되돌아 가져오면 순환 참조가 생기고 번들 그래프도 불필요하게 커진다.
  5. 디렉토리는 필요할 때만 만든다. 빈 디렉토리는 만들지 않는다.
  • Directorysrc/
    • main.tsx 엔트리 포인트
    • App.tsx 루트 컴포넌트
    • Directorycomponents/ 컴포넌트(Atomic Design)
      • …
    • Directoryhooks/ 커스텀 훅
      • …
    • Directorylayout/ 레이아웃
      • …
    • Directorypages/ 페이지
      • …
    • Directoryroutes/ 라우팅 설정
      • …
    • Directorytheme/ 테마
      • …
    • Directorytypes/ 공통 타입
      • …
    • Directoryutils/ 공통 함수
      • …
    • index.css

routes/, theme/, types/, utils/는 내부 구성에 별도 규칙을 두지 않고 자유롭게 생성한다. 단, 위 디렉토리 공통 규칙(index.ts 배럴, @/ 최상위 import)은 동일하게 적용한다.

  • 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
  1. atoms/molecules/organisms 아래에는 컴포넌트마다 디렉토리를 만들고, 컴포넌트 파일(ComponentName.tsx)과 index.ts를 둔다.
  2. 컴포넌트 디렉토리 이름은 컴포넌트 이름을 케밥 케이스로 바꿔 짓는다(DataGrid → data-grid).
  3. 컴포넌트 디렉토리의 index.ts는 그 컴포넌트만 export한다.
  4. 별도 스타일이 필요하면 같은 디렉토리에 Styled{컴포넌트 이름}.ts를 둔다.
  • 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
  1. apis/queries/mutations/stores에는 여러 기능이 함께 쓰는 공통 훅을 둔다. 특정 페이지에서만 쓰는 훅은 그 페이지 디렉토리 아래에 같은 형식으로 둔다(아래 페이지 디렉토리 참고).
  2. 훅 함수명은 use로 시작한다.
  3. apis/queries/mutations/stores에 있는 훅은 소속 디렉토리를 나타내는 접미사를 붙인다. 공통 훅과 페이지 전용 훅 모두 같다.
    • apis: Api 접미사(useAuthApi, useMenuApi)
    • queries: Query 접미사(useMenuRootQuery, useMenuChildQuery)
    • mutations: Mutation 접미사(useDomainSaveMutation, useDictionaryDeleteMutation)
    • stores: Store 접미사(useSideMenuStore, useSpinnerStore)
  4. queries(조회)와 mutations(저장)를 분리하는 이유는 백엔드 API 사용 예제 참고.
  • 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
  1. 페이지 경로는 메뉴 구조와 같게 중첩할 수 있다(pages/word/domain).
  2. 페이지 전용 하위 컴포넌트와 apis/queries/mutations/stores는 페이지 디렉토리 아래 같은 이름의 디렉토리에 둔다. 공통 훅처럼 훅마다 파일을 나누고, index.ts가 다시 export한다.
  3. 페이지 전용 타입은 types가 아니라 models 디렉토리에 둔다. 최상위 @/types(생성된 API 스키마 등 공용 타입)와 이 페이지에서만 쓰는 업무 모델(검색 조건 타입 등)을 위치만으로 구분하기 위해서다.
  4. 페이지 전용 커스텀 훅(useDomain.ts)은 페이지 디렉토리 바로 아래에 두고, 상수·헬퍼 함수도 함께 정의한다. 단, 하위 컴포넌트 여러 개가 함께 쓰는 순수 헬퍼 함수는 같은 components 디렉토리에 별도 .ts 파일로 둔다(예: pages/word/column/components/columnName.ts). 컴포넌트 파일(.tsx)이 함수를 함께 export하면 oxlint react/only-export-components 경고가 생기기 때문이다. 이 파일은 컴포넌트 전용 내부 구현이므로 components/index.ts에서 재-export하지 않는다.
  5. 페이지 스타일 파일명은 Styled{페이지 컴포넌트 이름}.ts로 짓는다.
  6. 페이지 디렉토리의 index.ts는 페이지 컴포넌트만 export한다. 하위 컴포넌트와 훅은 페이지 안에서만 쓰므로 외부에 노출하지 않는다.
  • 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