# 프로젝트 개요 이 저장소는 **풀스택 모노레포 기본 템플릿**이다. 누구나 클론하여 바로 개발을 시작할 수 있도록 공통 규칙과 언어별 규칙을 정의한다. | 디렉터리 | 스택 | 역할 | 개발 규칙 (슬래시 명령) | | ----------- | -------------- | ------------------------ | ---------------------------------------- | | `backend/` | NestJS (TS) | REST API 서버 | `/nestjs` → @.claude/commands/nestjs.md | | `frontend/` | Vue 3 (TS) | 웹 프론트엔드 | `/vue3` → @.claude/commands/vue3.md | | `flutter/` | Flutter (Dart) | 모바일 앱 | `/flutter` → @.claude/commands/flutter.md | | 루트 | Docker Compose | 로컬/운영 오케스트레이션 | @command.md | 세 클라이언트는 동일한 **표준 응답 포맷**과 **에러 코드 맵**을 공유한다 (아래 참조). --- ## 코딩 규칙 ### 공통 - 모든 코드 주석은 **한국어**로 작성 - 웹(`frontend`)·서버(`backend`)는 100% TypeScript, 모바일(`flutter`)은 Dart Sound Null Safety 준수 - 컴파일러/린터 **Warning 방치 금지** — 커밋 전 각 프로젝트 `lint` / `analyze` 통과 - 유틸 함수는 순수 함수로 분리 — TS는 `shared/utils/`, Dart는 `core/utils/` - 매직 넘버·하드코딩 문자열 지양 → 상수 또는 환경변수로 분리 - 절대경로 alias 사용 (TS `@/`, Dart `package:`), 깊은 상대경로(`../../`) 지양 ### 표준 응답 포맷 (Global Response Wrapper) 모든 API 응답은 아래 구조로 통일한다. (SSOT: backend `TransformInterceptor` + `HttpExceptionFilter`) ```jsonc // 성공 { "success": true, "data": { /* ... */ } } // 실패 { "success": false, "error": { "code": "VAL_001", "message": "..." } } ``` - 프론트/플러터 인터셉터는 `success: true` 면 `data` 만 언래핑하여 반환한다. - `success: false` 이거나 HTTP 4xx/5xx 면 `error.code` 로 메시지를 매핑한다. ### Git 커밋 컨벤션 `: <제목>` 형식 — type: **feat / fix / refactor / docs / chore / perf / test / style** - 예: `feat: 사용자 목록 페이지네이션 추가` - 제목은 한국어, 명령형 현재 시제로 작성 - 한 커밋은 하나의 논리적 변경만 포함 ### 에러 코드 맵핑 세 플랫폼이 공유하는 **단일 진실 공급원(SSOT)**. 메시지를 수정할 때는 아래 세 파일을 함께 갱신한다. - backend: `src/common/filters/http-exception.filter.ts` - frontend: `src/composables/useApi.ts` (`ErrorCodeLexicon`) - flutter: `lib/core/api/error_lexicon.dart` (`ErrorLexicon`) | 코드 | HTTP / 상황 | 메시지 | | -------- | -------------------- | ------------------------------------------------------- | | SYS_001 | 500 / 미정의 예외 | "일시적인 시스템 오류가 발생했습니다. 잠시 후 다시 시도해 주세요." | | AUTH_001 | 401 | "로그인이 필요한 서비스입니다. 로그인 후 이용해 주세요." | | AUTH_002 | 세션 만료 (클라이언트) | "안전을 위해 로그아웃 되었습니다. 다시 로그인해 주세요." | | AUTH_003 | 403 | "해당 메뉴나 기능에 접근할 수 있는 권한이 없습니다." | | VAL_001 | 400 / 422 (Validation) | "입력하신 정보를 다시 확인해 주세요. (필수값 누락 또는 형식 오류)" | | RES_001 | 404 | "요청하신 정보나 페이지를 찾을 수 없습니다." | | BIZ_001 | 200(비즈니스 실패) / 기타 | "요청하신 작업을 완료하지 못했습니다. 다시 시도해 주세요." | > 비즈니스 예외는 `HttpException` 응답의 `message` 로 사용자 노출 문구를 재정의할 수 있다. ### 보안 - `.env*` 등 민감정보 Git 커밋 금지 (`.gitignore` 확인) — 예시는 `*.example`(예: `.env.development.example`, `.env.example`) 로만 공유 - JWT는 HttpOnly·Secure 쿠키 사용 (웹 `localStorage` 금지) / 모바일은 `flutter_secure_storage` 사용 - TypeORM ORM 쿼리 사용으로 SQL Injection 차단 (Raw 쿼리 시 파라미터 바인딩 필수) - 입력값은 DTO(`class-validator`) 검증을 거친 뒤 서비스 계층에 전달 - 보안 헤더(`helmet`)·CORS·Rate Limiting(`@nestjs/throttler`) 적용 - Docker 컨테이너 내부 root 실행 금지 (비-root 사용자로 구동) --- ## 환경변수 구조 (단일 진실 공급원: 루트 `.env`) 이 템플릿은 **루트 `.env.{APP_ENV}` 가 모든 설정의 단일 진실 공급원(SSOT)** 이다. 값을 한 곳에서만 관리하기 위해, 서비스(backend/frontend)는 자체 env에 값을 중복 작성하지 않고 **루트 → docker-compose 가 주입한 값을 그대로 사용**한다. ``` 루트 .env.{APP_ENV} │ (docker compose --env-file 로 로드) ├─▶ backend : env_file + environment 로 DB/Redis/CORS/포트 주입 │ (backend/.env.{APP_ENV} 는 "존재"만 하면 됨, 내용 최소) ├─▶ frontend : BACKEND_API_URL → build arg(VITE_API_BASE_URL) 로 주입 ├─▶ db : DB_USER/DB_PASSWORD/DB_NAME, DB_DATA_DIR 볼륨 └─▶ redis : REDIS_PASSWORD, REDIS_DATA_DIR 볼륨 ``` 규칙: - **새 설정값은 루트 `.env.{APP_ENV}` 에 먼저 추가**하고, 필요한 서비스에 `docker-compose.yml` 의 `environment` / `args` 로 전달한다. - 서비스 자체 env 파일에는 **루트에서 내려주지 않는 고유 값만** 작성한다. (예: 백엔드 `JWT_SECRET`) - `docker-compose.yml` 의 `env_file` 지시자 때문에 `backend/.env.{APP_ENV}` 파일은 비어 있어도 **존재해야 한다.** - **Flutter 는 예외** — docker-compose 대상이 아니므로 `flutter/.env` 에서 `API_URL` 을 독립적으로 관리한다. - 모든 env 파일은 커밋 금지, `*.example` 만 커밋한다. (`.gitignore` 의 `!*.example` 재포함 규칙) > 루트 env의 대표 키: `PROJECT_NAME`, `APP_ENV`, `BUILD_MODE`, 각종 `*_PORT`, `BACKEND_API_URL`, `CORS_ORIGIN`, `DB_*`/`DB_DATA_DIR`, `REDIS_*`/`REDIS_DATA_DIR`. 실제 목록은 `.env.development.example` 참조. --- ## 개발 규칙 참조 - 백엔드(NestJS): @.claude/commands/nestjs.md - 프론트엔드(Vue 3): @.claude/commands/vue3.md - 모바일(Flutter): @.claude/commands/flutter.md - 실행/배포 명령어: @command.md