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>
115 lines
6.4 KiB
Markdown
115 lines
6.4 KiB
Markdown
# 프로젝트 개요
|
|
|
|
이 저장소는 **풀스택 모노레포 기본 템플릿**이다. 누구나 클론하여 바로 개발을 시작할 수 있도록 공통 규칙과 언어별 규칙을 정의한다.
|
|
|
|
| 디렉터리 | 스택 | 역할 | 개발 규칙 (슬래시 명령) |
|
|
| ----------- | -------------- | ------------------------ | ---------------------------------------- |
|
|
| `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>: <제목>` 형식 — 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
|