feat(backend): DB 마이그레이션 워크플로 추가
엔티티를 추가하면 개발에서는 동작하지만 운영 배포 시 테이블이 생성되지 않는 구조적 결함이 있었다. app.module 은 운영에서 migrationsRun 을 켜두었지만 마이그레이션을 만들 수단(DataSource·CLI 스크립트·디렉터리)이 전혀 없었고, 규칙 문서도 "운영에서는 마이그레이션이 강제된다"는 원칙만 있고 절차가 없었다. - CLI 전용 DataSource(src/data-source.ts)와 migration:* 스크립트 추가. ts-node 에 tsconfig-paths 를 등록해 엔티티가 @/ alias 를 써도 동작한다. - DB 접속 설정을 database.config.ts 로 모아 앱과 CLI 가 공유하게 함. 두 곳에 나눠 적으면 반드시 어긋난다. - synchronize 기본값을 false 로 변경 (DB_SYNCHRONIZE=true 로 개발 한정 opt-in). 켜져 있으면 개발 DB 가 항상 최신이라 migration:generate 가 "변경 없음"만 내놓아 마이그레이션을 만들 수 없다 — 이것이 위 결함의 직접 원인이었다. 운영에서는 이 값과 무관하게 항상 비활성화된다. - 엔티티 추가 절차와 주의사항(생성 SQL 검토, 커밋 후 수정 금지)을 문서화하고 커밋 전 체크리스트에 스키마 변경 항목 추가. generate → run → revert 전 과정을 임시 엔티티로 검증했다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -46,7 +46,15 @@
|
||||
|
||||
> `AUTH_002`(세션 만료)는 **클라이언트 전용** 코드로 서버 응답에는 나타나지 않는다. 자세한 내용은 [[error-codes]].
|
||||
|
||||
## 4. 공유 계약을 바꿀 때 (응답 포맷 · 페이지네이션 · API 규약)
|
||||
## 4. 엔티티(DB 스키마)를 바꿀 때
|
||||
|
||||
- [ ] 마이그레이션 생성 — `dc exec backend npm run migration:generate -- src/migrations/<이름>` ([[nestjs]])
|
||||
- [ ] 생성된 SQL 검토 — 의도치 않은 `DROP` 이 없는지 **반드시 눈으로 확인**
|
||||
- [ ] 적용 확인 — `migration:run` 후 의도한 스키마가 되었는지
|
||||
- [ ] 엔티티와 마이그레이션 파일을 **함께 커밋** (하나만 올라가면 배포 시 깨진다)
|
||||
- [ ] `DB_SYNCHRONIZE=true` 로 실험했다면 **끄고** 마이그레이션을 생성했는지 확인
|
||||
|
||||
## 5. 공유 계약을 바꿀 때 (응답 포맷 · 페이지네이션 · API 규약)
|
||||
|
||||
- [ ] 백엔드 구현 수정 ([[response-format]] · [[api-design]])
|
||||
- [ ] 세 클라이언트의 인터셉터/타입 동기화 (`useApi.ts` · `dio_client.dart` · 공유 타입 파일)
|
||||
@@ -55,7 +63,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 5. 기능 확장 시 따르는 순서
|
||||
## 6. 기능 확장 시 따르는 순서
|
||||
|
||||
새 기능은 만들기 전에, 해당 스킬의 **"템플릿 기본 제공 골격"** 에서 재사용할 패턴을 먼저 찾는다(같은 역할을 새로 만들지 말 것).
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ description: NestJS 백엔드(backend/) 개발 규칙 — 컨트롤러/서비스
|
||||
| Rate Limiting | `app.module.ts` 의 `ThrottlerGuard`(APP_GUARD) | 초과 시 429 → `REQ_001`. 루트 `.env` 의 `THROTTLE_*` 로 조정 |
|
||||
| 로깅 | `shared/logger/winston.config.ts` | `console.log` 대신 사용 ([[logging]]) |
|
||||
| env 검증 | `shared/config/env.validation.ts` | 잘못된 설정이면 **기동 실패**(fail-fast) ([[env-structure]]) |
|
||||
| DB 설정 | `shared/config/database.config.ts` | 앱·CLI 공유 SSOT. `synchronize` 기본 off |
|
||||
| 마이그레이션 | `src/data-source.ts` + `src/migrations/` | `npm run migration:*` (아래 "DB 마이그레이션") |
|
||||
| Graceful shutdown | `main.ts` 의 `enableShutdownHooks()` + `shared/logger/shutdown.logger.ts` | SIGTERM 시 요청/커넥션 정리 후 종료 |
|
||||
| 예제 모듈(참조용) | `modules/user`(DTO+Swagger+페이지네이션) | 새 모듈 패턴 참고. in-memory 데모 저장소 |
|
||||
| 헬스체크 | `modules/health` | `@SkipThrottle`+`@SkipTransform` — 기계가 읽는 엔드포인트 패턴 ([[response-format]]) |
|
||||
@@ -36,7 +38,8 @@ description: NestJS 백엔드(backend/) 개발 규칙 — 컨트롤러/서비스
|
||||
> **확장 시 따르는 규칙** (기능이 필요해지면 규칙대로 구현):
|
||||
> - **인증(JWT)**: `@nestjs/jwt`·`@nestjs/passport` 설치 → `common/guards/` 생성 → AUTH_001/AUTH_003 흐름 검증
|
||||
> - **DB 영속화**: TypeORM 엔티티/리포지토리 추가 (예제의 in-memory 저장 대체).
|
||||
> `app.module.ts` 는 이미 `TypeOrmModule.forRootAsync` 로 연결되어 있으며 **운영에서는 `synchronize:false` + 마이그레이션**이 강제된다 — 이 설정을 바꾸지 말 것
|
||||
> `app.module.ts` 는 이미 `TypeOrmModule.forRootAsync` 로 연결되어 있다.
|
||||
> 스키마는 **마이그레이션으로 관리**하며 `synchronize` 는 기본 off 다 (아래 "DB 마이그레이션" 참조)
|
||||
> - **도메인 에러 코드**: `error-codes.ts` 의 `DOMAIN_ERROR_CODES` 에 등록 → `npm run sync:error-codes`
|
||||
> - `common/{guards,pipes}/` 는 해당 관심사 등장 시 생성 (아래 "모듈 구조"는 목표 구조)
|
||||
|
||||
@@ -224,6 +227,53 @@ SwaggerModule.setup('api-docs', app, document);
|
||||
|
||||
---
|
||||
|
||||
## DB 마이그레이션
|
||||
|
||||
스키마 변경은 **마이그레이션으로 관리**한다. `synchronize` 는 기본적으로 꺼져 있다.
|
||||
|
||||
> **왜 끄는가**: `synchronize: true` 는 엔티티를 DB 에 자동 반영하므로 개발 DB 가 항상 최신 상태가 된다.
|
||||
> 그러면 `migration:generate` 가 "변경 없음"만 내놓고, 그 코드를 운영에 배포하면
|
||||
> (운영은 synchronize 가 꺼져 있고 실행할 마이그레이션도 없어) **테이블이 만들어지지 않는다.**
|
||||
> 개발과 운영이 같은 경로를 쓰도록 마이그레이션으로 통일한다.
|
||||
|
||||
### 엔티티 추가/변경 절차
|
||||
|
||||
```bash
|
||||
# 0) 개발 스택이 떠 있어야 한다 (DB 접속 정보는 루트 .env → compose 주입)
|
||||
# dc = docker compose -f docker-compose.yml -f docker-compose.dev.yml --env-file .env.development
|
||||
|
||||
# 1) 엔티티 작성 (src/**/*.entity.ts)
|
||||
|
||||
# 2) 마이그레이션 생성 — 엔티티와 실제 DB 스키마의 차이를 자동으로 계산한다
|
||||
dc exec backend npm run migration:generate -- src/migrations/AddUser
|
||||
|
||||
# 3) 생성된 SQL 을 반드시 눈으로 확인한다 (의도치 않은 DROP 이 없는지)
|
||||
# 4) 적용
|
||||
dc exec backend npm run migration:run
|
||||
|
||||
# 5) 엔티티와 마이그레이션 파일을 함께 커밋
|
||||
```
|
||||
|
||||
| 명령 | 용도 |
|
||||
| --- | --- |
|
||||
| `npm run migration:generate -- src/migrations/<이름>` | 엔티티 ↔ DB 차이로 자동 생성 |
|
||||
| `npm run migration:create -- src/migrations/<이름>` | 빈 마이그레이션 (데이터 이관 등 수동 SQL) |
|
||||
| `npm run migration:run` | 미적용 마이그레이션 실행 |
|
||||
| `npm run migration:revert` | 마지막 마이그레이션 롤백 |
|
||||
| `npm run migration:show` | 적용 상태 확인 |
|
||||
|
||||
규칙:
|
||||
|
||||
- **명령은 컨테이너 안에서 실행**한다. DB 접속 정보가 compose 로 주입되기 때문이다.
|
||||
- 생성된 SQL 을 **검토 없이 커밋하지 않는다.** 컬럼 rename 이 DROP+ADD 로 생성되면 데이터가 날아간다.
|
||||
- 마이그레이션 파일은 **한번 커밋되면 수정하지 않는다.** 잘못됐으면 새 마이그레이션으로 고친다.
|
||||
- 운영 배포 시 `migrationsRun: true` 로 자동 실행된다 — 별도 명령이 필요 없다.
|
||||
- 빠른 실험이 필요하면 개발 환경에서만 `DB_SYNCHRONIZE=true` 로 임시 활성화하되,
|
||||
**커밋 전에는 끄고 마이그레이션을 생성**한다. 운영에서는 이 값과 무관하게 항상 비활성화된다.
|
||||
- 접속 설정은 `shared/config/database.config.ts` 하나만 수정한다 (앱과 CLI 가 공유).
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 의존성 추가/제거 (Windows 개발 환경 주의)
|
||||
|
||||
**Windows 에서 `npm install` / `npm uninstall` 을 실행하면 lock 파일에서 Linux 전용 optional 의존성이 잘려나간다.**
|
||||
|
||||
Reference in New Issue
Block a user