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