93c8ae2a22
Docker 만 사용하므로 standalone 안내를 정리: - 프론트 env(.example): '단독 실행 시 주석 해제' 안내 + 주석 처리된 VITE 라인 제거 (VITE_ 값은 compose build arg 로만 주입됨) - CLAUDE.md: '단독 실행 시 주석 예시 활성화' 규칙 삭제 - README: 사전요구사항/cp 명령/‘단독 실행’ 섹션/env 표의 standalone 문구 제거 (모바일은 compose 비대상이라 별도 실행 안내만 한 줄 유지) - integration-progress: Redis 폴백 설명의 '단독 실행' 표현을 'REDIS_HOST 미설정 시'로 교정 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.4 KiB
6.4 KiB
프로젝트 개요
이 저장소는 풀스택 모노레포 기본 템플릿이다. 누구나 클론하여 바로 개발을 시작할 수 있도록 공통 규칙과 언어별 규칙을 정의한다.
| 디렉터리 | 스택 | 역할 | 개발 규칙 (슬래시 명령) |
|---|---|---|---|
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
@/, Dartpackage:), 깊은 상대경로(../../) 지양
표준 응답 포맷 (Global Response Wrapper)
모든 API 응답은 아래 구조로 통일한다.
(SSOT: backend TransformInterceptor + HttpExceptionFilter)
// 성공
{ "success": true, "data": { /* ... */ } }
// 실패
{ "success": false, "error": { "code": "VAL_001", "message": "..." } }
- 프론트/플러터 인터셉터는
success: true면data만 언래핑하여 반환한다. success: false이거나 HTTP 4xx/5xx 면error.code로 메시지를 매핑한다.
Git 커밋 컨벤션
<type>: <제목> 형식 — 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