# 09. 코딩 컨벤션 > **대상**: 프론트엔드 개발자 > **버전**: 1.0.0 > **최종 수정**: 2026-03-09 --- ## 1. 네이밍 규칙 ### 파일/폴더 | 대상 | 규칙 | 예시 | |------|------|------| | 컴포넌트 파일 | PascalCase | `BillManagement.tsx` | | 컴포넌트 폴더 | PascalCase | `BillManagement/` | | 유틸리티 | camelCase | `query-params.ts`, `amount.ts` | | 타입 파일 | camelCase | `types.ts` | | Server Action | camelCase | `actions.ts` | | 훅 | camelCase (use 접두사) | `useCEODashboard.ts` | | 페이지 라우트 | kebab-case | `general-journal-entry/page.tsx` | ### 변수/함수 | 대상 | 규칙 | 예시 | |------|------|------| | 컴포넌트 | PascalCase | `function BillManagement()` | | 함수 | camelCase | `handleSave`, `loadData` | | 이벤트 핸들러 | handle 접두사 | `handleClick`, `handleSubmit` | | 콜백 prop | on 접두사 | `onSave`, `onChange` | | boolean | is/has/show 접두사 | `isLoading`, `hasError`, `showModal` | | 상수 | UPPER_SNAKE_CASE | `PERIOD_BUTTONS`, `TYPE_WELFARE` | | 타입/인터페이스 | PascalCase | `BillRecord`, `SearchParams` | --- ## 2. 파일 배치 규칙 ### 도메인 컴포넌트 구조 ``` src/components/accounting/BillManagement/ ├── index.tsx # 메인 컴포넌트 (export) ├── actions.ts # Server Actions ('use server') ├── types.ts # 타입 정의 ├── BillDetail.tsx # 하위 컴포넌트 ├── BillForm.tsx # 폼 컴포넌트 └── schema.ts # Zod 스키마 (선택) ``` ### 배치 원칙 | 파일 | 위치 | |------|------| | 비즈니스 로직 컴포넌트 | `src/components/{domain}/{ComponentName}/` | | 공통 UI 컴포넌트 | `src/components/ui/` (shadcn/ui) | | 공통 조합 컴포넌트 | `src/components/organisms/` | | 도메인 공통 | `src/components/{domain}/common/` | | 전체 공통 | `src/components/common/` | | API 유틸 | `src/lib/api/` | | 범용 유틸 | `src/lib/utils/` | | 훅 | `src/hooks/` | | 전역 타입 | `src/types/` | --- ## 3. Import 규칙 ### 순서 ```typescript // 1. React/Next.js import { useState, useCallback } from 'react'; import { useRouter } from 'next/navigation'; // 2. 외부 라이브러리 import { toast } from 'sonner'; import { z } from 'zod'; // 3. UI 컴포넌트 (@/components/ui) import { Button } from '@/components/ui/button'; import { Card } from '@/components/ui/card'; // 4. 공통 컴포넌트 (@/components/organisms, molecules, atoms) import { PageLayout } from '@/components/organisms/PageLayout'; import { FormField } from '@/components/molecules/FormField'; // 5. 도메인 컴포넌트/액션 import { getBills } from './actions'; import { BillDetail } from './BillDetail'; // 6. 유틸/훅/타입 import { formatNumber } from '@/lib/utils/amount'; import type { BillRecord } from './types'; ``` ### 경로 별칭 - `@/` = `src/` (tsconfig paths) - 항상 `@/` 별칭 사용, 상대 경로(`../../`)는 같은 폴더 내에서만 --- ## 4. 컴포넌트 패턴 ### 페이지 컴포넌트 (page.tsx) ```typescript // 얇은 껍데기만 — 비즈니스 로직은 도메인 컴포넌트에 'use client'; import { BillManagement } from '@/components/accounting/BillManagement'; export default function BillsPage() { return ; } ``` ### 도메인 컴포넌트 (index.tsx) ```typescript 'use client'; import { useState, useCallback, useEffect } from 'react'; // ... imports export function BillManagement() { // 1. 상태 선언 const [data, setData] = useState([]); const [isLoading, setIsLoading] = useState(true); // 2. 데이터 로드 const loadData = useCallback(async () => { ... }, [deps]); useEffect(() => { loadData(); }, [loadData]); // 3. 이벤트 핸들러 const handleSave = useCallback(async () => { ... }, [deps]); const handleDelete = useCallback(async () => { ... }, [deps]); // 4. 계산값 (useMemo) const config = useMemo(() => ({ ... }), [deps]); // 5. 렌더링 return ; } ``` --- ## 5. TypeScript 규칙 ### 타입 정의 ```typescript // ✅ 컴포넌트 props는 interface (확장 가능) interface BillDetailProps { record: BillRecord; onSave: (data: BillFormData) => Promise; } // ✅ 데이터 모델은 type (유니온/인터섹션 활용) type BillStatus = 'draft' | 'confirmed' | 'paid'; // ✅ API 응답 변환 interface BillApiResponse { ... } // 백엔드 스네이크_케이스 interface BillRecord { ... } // 프론트 camelCase function transformBillApiToFrontend(api: BillApiResponse): BillRecord { ... } ``` ### 금지 패턴 ```typescript // ❌ any 사용 금지 const data: any = response; // ❌ as 캐스트 지양 (Zod 사용 시 불필요) const value = input as string; // ❌ non-null assertion 지양 const name = user!.name; ``` --- ## 6. 상태 관리 규칙 | 범위 | 방법 | |------|------| | 컴포넌트 내부 | useState, useReducer | | 형제 간 공유 | 부모에서 prop 전달 | | 전역 인증 | useAuthGuard (Context) | | 서버 데이터 | Server Action + useState | | 대시보드 갱신 | dashboard-invalidation (CustomEvent) | **사용하지 않는 것**: Redux, Zustand, Recoil 등 전역 상태 라이브러리 --- ## 7. 에러 처리 규칙 ```typescript // Server Action 결과 처리 const result = await saveItem(formData); if (result.success) { toast.success('저장되었습니다.'); loadData(); } else { toast.error(result.error || '저장에 실패했습니다.'); } // try-catch (Server Action 호출 자체의 에러) try { const result = await getItems(); if (result.success) setData(result.data); } catch { toast.error('서버 오류가 발생했습니다.'); } ``` --- ## 8. 성능 규칙 | 규칙 | 이유 | |------|------| | `useMemo`로 config 객체 감싸기 | 불필요한 리렌더 방지 | | `useCallback`으로 핸들러 감싸기 | 자식 컴포넌트 리렌더 방지 | | 무거운 컴포넌트 `React.memo` | 부모 리렌더 시 불필요한 재계산 방지 | | 대시보드 섹션 LazySection | Intersection Observer 기반 지연 로딩 | | 이미지 `next/image` 사용 | 자동 최적화 | --- ## 9. Git 커밋 메시지 ``` [타입]: 작업내용 (한글) 타입: - feat: 신규 기능 - fix: 버그 수정 - refactor: 리팩토링 - chore: 설정/빌드 - style: 포맷팅 - docs: 문서 ``` 예시: ``` feat: CEO 대시보드 접대비 섹션 API 연동 fix: 어음관리 날짜 필터 오류 수정 refactor: 계정과목 설정 모달 공통화 ``` --- ## 10. 빠른 참조 | 상황 | 해야 할 것 | |------|-----------| | 새 리스트 페이지 | UniversalListPage + actions.ts + types.ts | | 새 폼 | Zod 스키마 + FormField + react-hook-form | | 모달 필요 | SearchableSelectionModal 먼저 확인 | | API 호출 | Server Action → buildApiUrl → executeServerAction | | 토스트 알림 | `toast.success()` / `toast.error()` | | 날짜 입력 | DatePicker (input type="date" 금지) | | 대시보드 갱신 | `invalidateDashboard('domain')` | | 금액 표시 | `formatNumber()` |