코드 작성 시 따르는 코딩 규칙과 패턴. 컴포넌트, 훅, API, 타입 작성 시 자동 적용.
app/ Pages (App Router), API Routes
components/
ui/ Button, Badge, Modal, Card, Table, LoadingSpinner
features/ Domain components (process-status/, resource-table/, admin/)
hooks/ useModal, useApiMutation, useAsync
lib/
theme.ts Design tokens (colors, component styles, helpers)
api.ts API call functions
types/ Shared types
constants/ labels.ts
utils/ date.ts, credentials.ts
StepIndicator.tsx)useModal.ts)@/ 절대 경로만 (상대 경로 금지)@/) → Types(import type)@/app/components/ui/ → UI 컴포넌트 (Button, Badge, Modal, Card, Table, LoadingSpinner)
@/app/components/features/ → 도메인 컴포넌트 (process-status/, resource-table/, admin/)
@/hooks/ → 커스텀 훅 (useModal, useApiMutation, useAsync)
@/lib/ → theme.ts, api.ts, types/, constants/, adapters/
@/utils/ → 유틸리티 (date.ts, credentials.ts)
@/components/ui/❌ →@/app/components/ui/✅ (app 디렉토리 내 위치)
any 금지 — 구체적 타입 또는 unknown + 타입 가드useModal() (useState 직접 관리 금지)useApiMutation() (try-catch 직접 작성 금지)bg-blue-600, text-red-500 등)theme.ts 토큰 경유:
statusColors.{success|error|info|warning|pending}getButtonClass(variant, size) 또는 <Button> 컴포넌트cardStyles 토큰getInputClass(state)✅ 레이아웃 직접 사용: flex, grid, gap-*, p-*, m-*, w-*, h-*, text-{sm|base|lg}
✅ theme.ts 헬퍼: cn(), getButtonClass(), getInputClass()
✅ 구조 클래스: rounded-*, border, shadow-*, overflow-*
❌ 색상 직접 사용: bg-{color}-*, text-{color}-*, border-{color}-*
❌ 컴포넌트 스타일 하드코딩: 버튼/카드/모달/뱃지/테이블
// ❌ Bad
<button className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700">
// ✅ Good
<button className={getButtonClass('primary', 'md')}>
// ❌ Bad
<span className="bg-green-100 text-green-800 px-2 py-1 rounded-full">완료</span>
// ✅ Good
<span className={cn(statusColors.success.bg, statusColors.success.textDark, 'px-2 py-1 rounded-full')}>완료</span>
&& 또는 삼항연산자 (if/else 블록 지양)// ❌ Verbose
const items = data.filter(item => item.active);
const sortedItems = items.sort((a, b) => a.name.localeCompare(b.name));
return (
<div>
{sortedItems.map(item => (
<Item key={item.id} data={item} />
))}
</div>
);
// ✅ Compact
return (
<div>
{data
.filter(item => item.active)
.sort((a, b) => a.name.localeCompare(b.name))
.map(item => <Item key={item.id} data={item} />)}
</div>
);
ComponentName/index.ts)docs/swagger/*.yaml) 준수lib/mock-*.ts 예외)app/api/route.ts는 client.method() 디스패치만 수행 (ADR-007)lib/api-client/mock/*.ts에 위치lib/adapters/는 삭제됨 (ADR-005 → Superseded by ADR-007)x-expected-duration)를 반드시 선언x-expected-duration 누락 발견 시 즉시 경고 후 보완매 엔드포인트마다 구현 후 아래 6항목을 교차 검증:
□ swagger response envelope 키 == client 반환 키 == FE fetchJson 타입
□ swagger request schema 필드명 == FE 전송 필드명
□ swagger response schema 필드명 == mock 데이터 필드명
□ route handler가 client.method() 디스패치만 수행 (response.json() 파싱 금지)
□ envelope 변환이 필요하면 client 계층(mock/*.ts)에서 수행
□ 모든 endpoint를 개별 검증 (일괄 구현 후 일괄 검증 금지)
swagger ≠ 코드일 때: 코드를 swagger에 맞춤 (swagger를 코드에 맞추지 않음)
// ❌ Bad — mock 직접 import
import { getProjectById } from '@/lib/mock-data';
const project = getProjectById(id);
// ✅ Good — client 디스패치
import { client } from '@/lib/api-client';
const project = await client.projects.get(id);
fetchJson 사용 (lib/fetch-json.ts)AppError로 정규화됨 — err.code로 분기 (lib/errors.ts)if (!res.ok) throw new Error(...) 패턴 금지 — fetchJson이 대체app/components/errors/ 경로에서 관리