Files
sam-docs/frontend/v1/09-conventions.md
유병철 8f939d3609 docs: [frontend] 프론트엔드 아키텍처/가이드 문서 v1 작성
- _index.md: 문서 목록 및 버전 관리
- 01~09: 아키텍처, API패턴, 컴포넌트, 폼, 스타일, 인증, 대시보드, 컨벤션
- 10: 문서 API 연동 스펙 (api-specs에서 이관)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 10:24:25 +09:00

7.0 KiB

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 규칙

순서

// 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)

// 얇은 껍데기만 — 비즈니스 로직은 도메인 컴포넌트에
'use client';
import { BillManagement } from '@/components/accounting/BillManagement';

export default function BillsPage() {
  return <BillManagement />;
}

도메인 컴포넌트 (index.tsx)

'use client';

import { useState, useCallback, useEffect } from 'react';
// ... imports

export function BillManagement() {
  // 1. 상태 선언
  const [data, setData] = useState<BillRecord[]>([]);
  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 <UniversalListPage config={config} />;
}

5. TypeScript 규칙

타입 정의

// ✅ 컴포넌트 props는 interface (확장 가능)
interface BillDetailProps {
  record: BillRecord;
  onSave: (data: BillFormData) => Promise<void>;
}

// ✅ 데이터 모델은 type (유니온/인터섹션 활용)
type BillStatus = 'draft' | 'confirmed' | 'paid';

// ✅ API 응답 변환
interface BillApiResponse { ... }  // 백엔드 스네이크_케이스
interface BillRecord { ... }       // 프론트 camelCase
function transformBillApiToFrontend(api: BillApiResponse): BillRecord { ... }

금지 패턴

// ❌ 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. 에러 처리 규칙

// 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()