프런트엔드 코딩 규칙 정리
프런트엔드에서 규칙 정의가 필요한 항목
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/ 공통 함수
- …
- main.tsx 엔트리 포인트
- App.tsx 루트 컴포넌트
- index.css
routes//theme//types//utils/는 내부 파일·하위 디렉토리 구성에 별도 규칙을 두지 않고
자유롭게 생성한다. 단, 위 배럴 export와 @/ 최상위 import 규칙은 동일하게 적용한다.
하위 디렉토리 구조
Section titled “하위 디렉토리 구조”hooks/는 소속별 하위 디렉토리(apis/queries/mutations/stores)로 나누고, 어디에도 속하지 않는 공통 훅은hooks/바로 아래에 둔다. 훅 이름 접미사는 커스텀 훅 작성 규칙 참고.components/는 Atomic Design 3단계(atoms/molecules/organisms)로 나누고, 컴포넌트마다 케밥 케이스 디렉토리를 만든다(공통 컴포넌트 규칙 참고).pages/는 메뉴와 같은 구조로 중첩한다(예:pages/word/domain). 페이지 전용 컴포넌트·훅·타입은 페이지 디렉토리 아래의components/apis/queries/mutations/stores/models에 두고, 페이지의index.ts는 페이지 컴포넌트만 export한다.- 페이지 전용 타입 디렉토리는
types가 아니라models로 이름 붙여, 최상위src/types(생성된 API 스키마 등 공용 타입)와 위치만으로 구분한다. layout/은app-layout이 전체 틀을 잡고, 왼쪽에side-menu, 오른쪽에header→content(Outlet) →footer를 세로로 배치한다.
디렉토리별 실제 파일 구성과 예시는 프로젝트 디렉토리 구성 참고.
코딩 컨벤션
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으로 재확인.
flowchart TD
load["페이지 로드(하이드레이션)"] --> c1{"① onRehydrateStorage<br/>expiresAt이 지났는가?"}
c1 -- "예" --> out["세션 제거 → 로그인 페이지"]
c1 -- "아니오" --> use["화면 사용"]
use -- "API 호출" --> c2{"② 응답이 401인가?"}
c2 -- "예 (응답 인터셉터, 쿼리 캐시도 초기화)" --> out
c2 -- "아니오" --> renew["응답의 expiresAt으로<br/>만료 시각 갱신"] --> use
use -- "5분마다 (RequireAuth)" --> c3{"③ find-session 응답이<br/>401인가?"}
c3 -- "예" --> out
c3 -- "아니오" --> use
공통 컴포넌트 규칙
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로 병합·푸시하는 실행은 직접 하거나/git-deploy스킬로 AI에게 맡긴다(브랜치 역할과 병합 규칙 참고). - 프로젝트 페이지 서브패스(
/dictionary-app/) 배포이므로vite.config.ts의base설정이 필요하다. 누락하면 빈 페이지만 표시된다. VITE_APP_API_URL은 빌드 시점에 고정되는 값이라 GitHub Secrets에 등록해야 하며, 등록하지 않으면 배포된 페이지에서 API 호출이 전부 실패한다.