Skip to content

프런트앤드 코딩 규칙 정리

프런트앤드에서 규칙 정의가 필요한 항목

Section titled “프런트앤드에서 규칙 정의가 필요한 항목”

dictionary-app(React 19 + TypeScript + Vite) 저장소의 CLAUDE.md/README.md에서 사용 중인 규칙을 기준으로 정리한다.

  1. 디렉토리 규칙
  2. 코딩 컨벤션
  3. 컴포넌트 작성 규칙
  4. 커스텀 훅 작성 규칙
  5. 백앤드 연동 규칙
  6. 외부 인터페이스 연동 규칙
  7. 예외 처리 규칙
  8. 로그인 사용자 조회 규칙
  9. 공통 컴포넌트 규칙
  10. 토스트 작성 규칙
  11. 알람/확인(Dialog) 작성 규칙
  12. 모달창 작성 규칙
  13. 그리드 작성 규칙
  14. 배포 규칙
  1. 디렉토리 이름은 소문자 케밥 케이스로 작성한다(언더바 _ 금지).
  2. index.ts 파일로 디렉토리 안의 컴포넌트/모듈을 배럴 export한다. export 대상 경로는 확장자 없이 작성한다.
  3. @/{디렉토리} 최상위 배럴은 그 디렉토리 바깥에서 가져올 때만 쓴다. 같은 배럴 디렉토리 내부에서는 상대 경로나 더 좁은 하위 배럴로 직접 가져와 순환 참조를 피한다.
  4. 디렉토리는 필요할 때만 생성한다. 빈 디렉토리는 만들지 않는다.
  • Directorysrc/
    • Directorycomponents/ 컴포넌트
    • Directoryhooks/ 커스텀 훅
    • Directorylayout/ 레이아웃
    • Directorypages/ 페이지
    • Directoryroutes/ 라우팅 설정
    • Directorytheme/ 테마
    • Directorytypes/ 공통 타입
    • Directoryutils/ 공통 함수
    • Directoryassets/ 이미지, 아이콘, 폰트 등
    • main.tsx 엔트리 포인트
    • App.tsx 루트 컴포넌트
    • App.css
    • index.css

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

  • Directorysrc/hooks/
    • Directoryapis/ 백앤드 연결을 위한 커스텀 훅
      • {apis-hook}.ts apis 훅
      • index.ts apis 훅을 익스포트
    • Directoryqueries/ react-query 조회(useQuery) 커스텀 훅
      • {queries-hook}.ts queries 훅
      • index.ts queries 훅을 익스포트
    • Directorymutations/ react-query 저장(useMutation) 커스텀 훅
      • {mutations-hook}.ts mutations 훅
      • index.ts mutations 훅을 익스포트
    • Directorystores/ zustand를 활용한 스토어 커스텀 훅
      • {stores-hook}.ts stores 훅
      • index.ts stores 훅을 익스포트
    • {hook-file}.ts 별도 디렉토리로 분류되지 않는 공통 커스텀 훅
    • index.ts 디렉토리 내부의 커스텀 훅을 익스포트

apis/queries/mutations/stores 디렉토리는 공통 훅뿐 아니라 특정 페이지에만 쓰는 훅에도 동일한 형식으로 적용된다(페이지 디렉토리 하위 구조 참고). 훅 이름에 붙는 접미사 규칙은 커스텀 훅 작성 규칙 참고.

  • Directorysrc/components/
    • Directoryatoms/ 더 이상 쪼갤 수 없는 최소 단위 컴포넌트(버튼, 인풋, 아이콘, 텍스트 등)
      • Directory{component-a}/ 컴포넌트 디렉토리
        • ComponentA.tsx 컴포넌트
        • StyledComponentA.ts 컴포넌트 스타일 파일(필요한 경우)
        • index.ts 컴포넌트 익스포트
      • index.ts 컴포넌트를 익스포트
    • Directorymolecules/ atom을 조합해 만든 재사용 가능한 단위 컴포넌트(검색창, 폼 필드 등)
      • Directory{component-b}/ 컴포넌트 디렉토리
        • ComponentB.tsx 컴포넌트
        • StyledComponentB.ts 컴포넌트 스타일 파일(필요한 경우)
        • index.ts 컴포넌트 익스포트
      • index.ts 컴포넌트를 익스포트
    • Directoryorganisms/ atom·molecule을 조합해 독립적인 의미를 가지는 UI 영역(헤더, 카드 리스트 등)
      • Directory{component-c}/ 컴포넌트 디렉토리
        • ComponentC.tsx 컴포넌트
        • StyledComponentC.ts 컴포넌트 스타일 파일(필요한 경우)
        • index.ts 컴포넌트 익스포트
      • index.ts 컴포넌트를 익스포트
    • index.ts atoms/molecules/organisms를 익스포트

컴포넌트를 Atomic Design 3단계로 분류하는 기준과 디렉토리/파일 네이밍 규칙은 공통 컴포넌트 규칙 참고.

  • Directorysrc/pages/{page-directory}/
    • Directorycomponents/ 페이지 전용 하위 컴포넌트
      • {sub-component}.tsx 하위 컴포넌트
      • Styled{sub-component}.ts 하위 컴포넌트 스타일 파일(필요한 경우)
      • index.ts 컴포넌트를 익스포트
    • Directoryapis/ 페이지 전용 백앤드 연결 커스텀 훅
      • {apis-hook}.ts apis 훅
      • index.ts apis 훅을 익스포트
    • Directoryqueries/ 페이지 전용 react-query 조회 커스텀 훅
      • {queries-hook}.ts queries 훅
      • index.ts queries 훅을 익스포트
    • Directorymutations/ 페이지 전용 react-query 저장 커스텀 훅
      • {mutations-hook}.ts mutations 훅
      • index.ts mutations 훅을 익스포트
    • Directorystores/ 페이지 전용 zustand 스토어 커스텀 훅
      • {stores-hook}.ts stores 훅
      • index.ts stores 훅을 익스포트
    • Directorymodels/ 페이지 전용 타입(모델)
      • {ModelName}.ts 타입 하나. 파일명은 타입 이름과 동일한 파스칼 케이스
      • index.ts 타입을 익스포트
    • {page-hook}.ts 페이지 전용 커스텀 훅
    • {page-component}.tsx 페이지 컴포넌트
    • {page-style}.ts 페이지 스타일 파일
    • index.ts 페이지 컴포넌트만 익스포트
  1. 페이지 경로는 메뉴와 동일한 구조를 가지며 중첩될 수 있다(예- /src/pages/community/animation).
  2. models 디렉토리는 최상위 src/types(생성된 API 스키마 등 인프라성 공용 타입)와 이 페이지에서만 쓰는 업무 모델을 위치만으로 구분하기 위해 types가 아닌 이름을 쓴다.
  3. index.ts는 페이지 컴포넌트만 export한다 — 페이지 아래에 위치한 컴포넌트/커스텀 훅/타입은 그 페이지에서만 쓰이므로 외부에 노출하지 않는다.
  • Directorysrc/layout/
    • Directorycontent/ 컨텐츠
      • Content.tsx Content 컴포넌트
      • index.ts 컴포넌트를 익스포트
    • Directoryfooter/ 푸터
      • Footer.tsx Footer 컴포넌트
      • index.ts 컴포넌트를 익스포트
    • Directoryapp-layout/ 레이아웃
      • AppLayout.tsx AppLayout 컴포넌트
      • index.ts 컴포넌트를 익스포트
    • Directoryheader/ 헤더
      • Header.tsx Header 컴포넌트
      • index.ts 컴포넌트를 익스포트
    • Directoryside-menu/ 사이드 메뉴
      • SideMenu.tsx SideMenu 컴포넌트
      • index.ts 컴포넌트를 익스포트
    • index.ts 하위 디렉토리의 컴포넌트를 익스포트
  1. app-layout 컴포넌트에서 전체 레이아웃을 정의한다.
  2. 전체 레이아웃 왼쪽에 side-menu 컴포넌트가 위치한다.
  3. 전체 레이아웃 오른쪽에는 headercontentfooter 컴포넌트가 세로 방향으로 순서대로 위치한다.
  4. content 컴포넌트에는 outlet이 위치해 동적으로 페이지가 변경될 수 있도록 한다.
  1. Prettier: 세미콜론 없음(semi: false), 싱글쿼트, trailing comma, printWidth: 90.
  2. oxlint: no-var, eqeqeq, react/rules-of-hooks.oxlintrc.json 규칙을 따른다.
  3. function 대신 화살표 함수를 사용한다. 단, 이름 붙여 정의하는 화살표 함수는 한 줄로 표현 가능해도 암묵적 반환 대신 중괄호 + return 구문을 쓴다(인라인 콜백은 예외).
  4. use client 구문은 사용하지 않는다.
  5. 모델을 알 수 있는 경우 제너릭으로 대상 모델을 명시하고, 사용할 수 있는 곳에서는 적극 활용한다.
  1. 컴포넌트 이름은 파스칼 케이스, 파일명은 컴포넌트 이름과 동일하게, 확장자는 tsx(HTML을 반환하지 않는 경우에 한해 ts 허용)로 작성한다.
  2. export default는 사용하지 않는다. 컴포넌트 정의 시 이름 왼쪽에 export를 붙인다.
  3. 프롭으로 주입하는 이벤트 핸들러 이름은 handle 접두사를 쓴다(onClick이 아니라 handleClick).
  4. 이벤트 핸들러는 JSX에 인라인 화살표 함수로 작성하지 않고, 컴포넌트 함수 바디에 이름 붙은 핸들러로 분리한 뒤 참조한다. 인자를 즉시 바인딩해야 하는 경우에도 로직은 핸들러 함수에 두고 JSX에서는 인자만 넘기는 최소한의 래핑만 허용한다.
  1. 모든 커스텀 훅 이름은 use로 시작한다(예- useDictionary).
  2. apis/queries/mutations/stores 디렉토리에 위치하는 훅은 소속을 나타내는 접미사를 붙인다: apisApi, queriesQuery, mutationsMutation, storesStore.
  3. 조회(useQuery)는 queries, 저장(useMutation)은 mutations 디렉토리로 분리한다.
  4. call/callRaw/callPage/callDownload를 감싸는 훅이 결과를 그대로 반환(pass-through) 하기만 한다면 async/await를 붙이지 않는다. resolve된 값을 가공/destructure해야 할 때만 async/await를 사용한다.
  1. 공용 API 훅 useApi(call/callRaw/callPage/callDownload)로 백엔드와 통신한다.
    • call: 응답 봉투(ResponseDto)에서 payload만 반환
    • callRaw: status/headers 등 응답 전체가 필요할 때
    • callPage: payload + pagination을 함께 반환(페이지네이션 목록 조회)
    • callDownload: 파일 다운로드(Blob)
  2. axios는 withCredentials: true + VITE_APP_API_URL(baseURL) 조합으로 세션 쿠키를 실어 호출한다(useAxiosInterceptor).
  3. 백엔드 API 응답 DTO(OpenAPI 스펙)가 바뀌면 pnpm gen:api-typessrc/types/schema.ts를 재생성해 타입을 동기화한다.
  4. dictionary-api(백엔드) 소스는 API 스펙 확인 등 참조용으로만 활용하고, 직접 수정하지 않는다.
  1. 프런트엔드는 외부 사전 API(표준국어대사전/한영사전)를 직접 호출하지 않는다. 백엔드의 /search 엔드포인트(find-stdict/find-krdict)를 통해서만 연동한다.
  2. 외부 인터페이스 호출도 백앤드 연동 규칙과 동일하게 useApi 훅으로 감싼다.
  1. axios 응답 인터셉터가 isUnauthorizedError로 401 여부를 판별해, 세션 제거 (removeSessionUser) + 쿼리 캐시 초기화(queryClient.clear()) + 로그인 페이지 이동을 한 곳에서 공통 처리한다.
  2. 그 외 API 에러는 호출부에서 try/catch로 잡아 토스트로 사용자에게 알린다.
  1. useSessionStore(zustand persist)가 로그인 사용자 정보(sessionUser)와 만료 시각 (expiresAt)을 localStorage에 영속화해서 관리한다.
  2. expiresAt은 로그인 세션이 있는 모든 API 응답 봉투에 함께 내려오며, 응답 인터셉터가 매 응답마다 최신화한다(서버가 슬라이딩 세션이라 호출할 때마다 만료 시각이 밀리기 때문).
  3. 만료 판정은 세 단계로 이뤄진다: 하이드레이션 시점의 로컬 근사치 체크(onRehydrateStorage) → 서버가 401로 확정 → 인증 가드(RequireAuth)가 주기적으로(5분) find-session으로 재확인.
  1. src/componentsAtomic Design(atoms/molecules/organisms)으로 구성한다.
  2. 컴포넌트마다 케밥 케이스 디렉토리를 만들고, 그 안에 컴포넌트 파일(ComponentName.tsx)과 index.ts를 둔다. index.ts는 해당 컴포넌트만 export한다.
  3. 별도 스타일이 필요하면 같은 디렉토리에 Styled{컴포넌트 이름}.ts를 두고, Chakra UI v3의 chakra() 팩토리로 작성한다(별도 CSS/styled-components/sx/인라인 style 대신).
  4. 특정 페이지에만 쓰는 컴포넌트는 공통 디렉토리가 아니라 해당 페이지 디렉토리 하위 components에 둔다.
  1. 전역 싱글턴 toaster(success/error/warning/info/loading)를 호출해서 띄운다. 렌더링 컴포넌트는 App.tsx에 한 번만 마운트한다.
  2. 기본 동작: 3초 후 자동으로 사라짐(duration으로 재정의 가능), 닫기 버튼 항상 표시, 화면 오른쪽 위(top-end)에 쌓임.
  3. toaster.dismiss()로 전체를, toaster.dismiss(id)로 특정 토스트만 닫을 수 있다.
  1. window.confirm 대신 confirmDialog(options)를 사용한다. 사용자가 확인을 누르면 true, 취소/바깥 클릭이면 false로 resolve되는 Promise<boolean>을 반환한다.
  2. 취소 버튼 없이 확인 버튼만 필요하면 alertDialog(options)를 사용한다 (Promise<void> 반환).
  3. 다이얼로그는 화면에 한 번에 하나만 뜬다. 순서대로 여러 번 확인받아야 하면 각 호출을 await로 이어서 사용한다.
  1. Modal 컴포넌트는 열림 상태(open/handleClose)를 부모가 useState로 직접 제어하는 선언형 컴포넌트다(선택 결과를 Promise로 돌려주는 Dialog와 구분).
  2. footer를 넘기지 않고 handleConfirm을 넘기면 확인/취소 버튼이 자동으로 만들어지고, footer를 넘기면 그 내용을 그대로 쓴다.
  3. 크기는 fullScreen > width/height > size 순으로 우선한다. 항상 placement="center"로 화면 가운데에 뜬다.
  1. ag-grid-react로 데이터 그리드를 그린다. 사용할 모듈은 앱 전체에서 한 번만 src/utils/agGridTheme.ts(진입 시점에 main.tsx에서 import)로 등록한다.
  2. 서버사이드 페이지네이션이 필요한 목록 화면은 DataGrid(그리드 + 페이지네이션 결합 컴포넌트)를 사용한다.
  1. GitHub Pages에 배포하며, master가 아니라 deploy 브랜치에 push될 때만 .github/workflows/static.yml이 실행된다. master 커밋마다 자동 배포되는 것을 막기 위한 구조다.
  2. 배포하고 싶은 시점에 사람이 직접 masterdeploy로 병합·푸시한다(정해진 주기 없음, 브랜치 역할과 병합 규칙 참고).
  3. 프로젝트 페이지 서브패스(/dictionary-app/) 배포이므로 vite.config.tsbase 설정이 필요하다. 누락하면 빈 페이지만 표시된다.
  4. VITE_APP_API_URL은 빌드 시점에 고정되는 값이라 GitHub Secrets에 등록해야 하며, 등록하지 않으면 배포된 페이지에서 API 호출이 전부 실패한다.