refactor(docker): Redis 를 기본 비활성으로 전환 (Compose profile)
CI / Backend (lint · test · e2e · build) (push) Has been cancelled
CI / Frontend (lint · type-check · test) (push) Has been cancelled
CI / Flutter (analyze · test) (push) Has been cancelled
CI / 에러 코드 SSOT 동기화 검사 (push) Has been cancelled

Redis 컨테이너가 항상 떠 있었지만 애플리케이션은 사용하지 않았다.
쓰지 않는 컨테이너는 메모리를 차지하고 "이게 왜 떠 있지" 하는 혼란을 준다.

- redis 서비스를 profiles: ["redis"] 로 숨겨 기본 기동에서 제외
- backend 의 redis 의존성을 required: false 로 두어 없어도 정상 기동
- 루트 .env 의 COMPOSE_PROFILES 한 줄로 켤 수 있게 함
  (데이터 볼륨과 REDIS_* 는 유지되어 켜면 이전 상태가 그대로 이어진다)

profile 미지정 시 서비스 목록에서 제외되는 것, redis 없이 앱이 정상 동작하는 것,
COMPOSE_PROFILES=redis 로 다시 기동되는 것을 모두 확인했다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-17 18:48:02 +09:00
parent a217d13483
commit e4262b2816
7 changed files with 58 additions and 5 deletions
+1
View File
@@ -14,6 +14,7 @@
- [ ] 프로젝트 식별 값 교체 (`PROJECT_NAME` 등 루트 env 키)
- [ ] 의존성 설치: `cd backend && npm ci`, `cd frontend && npm ci`, `cd flutter && flutter pub get`
- [ ] Docker 개발 환경 기동으로 동작 확인 ([[docker]])
(Redis 는 기본 비활성이다 — 필요하면 루트 `.env` 에 `COMPOSE_PROFILES=redis`)
## 2. 커밋 전 공통 점검 (매 작업)
+22
View File
@@ -76,6 +76,28 @@ dc exec redis redis-cli -a <REDIS_PASSWORD>
---
## 선택적 서비스 (Redis)
**Redis 는 기본적으로 기동하지 않는다.** 단일 인스턴스에서는 in-memory 캐시로 충분하고,
쓰지 않는 컨테이너는 메모리만 차지하며 혼란을 준다.
```bash
# 켜기 — 루트 .env.{APP_ENV} 한 줄 수정 (이후 평소 명령 그대로)
COMPOSE_PROFILES=redis
# 또는 일회성
dc --profile redis up -d
```
- compose `profiles: ["redis"]` 로 숨겨져 있고, backend 의 의존성은 `required: false` 라
Redis 가 없어도 정상 기동한다.
- 데이터 볼륨(`${REDIS_DATA_DIR}`)과 `REDIS_*` 환경변수는 그대로 유지되므로,
켜면 이전 데이터와 설정이 그대로 이어진다.
- **애플리케이션 코드는 아직 Redis 를 사용하지 않는다.** 실제 연결 절차는 [[nestjs]] 참조.
- 끝까지 쓰지 않을 프로젝트라면 compose 에서 redis 서비스와 관련 env 를 지우는 편이 낫다.
---
## 규칙
- **빌드 최소화**: 의존성(package.json / pubspec 등) 변경이 없으면 `--build` 없이 기동한다.
+4 -3
View File
@@ -202,16 +202,17 @@ SwaggerModule.setup('api-docs', app, document);
## 성능 & 최신 트렌드 추가 규칙
- **캐싱**: `@nestjs/cache-manager` 가 전역 등록되어 있으나 **현재 저장소는 in-memory** 다.
> 📐 **Redis 는 확장 지점이다.** compose 에 redis 컨테이너가 뜨고 `REDIS_HOST`/`REDIS_PORT`/`REDIS_PASSWORD` 가
> 백엔드로 주입되지만, **아직 이를 사용하는 코드는 없다.** 단일 인스턴스에서는 in-memory 로 충분하다.
> 📐 **Redis 는 확장 지점이다.** compose 에서 **기본 비활성**(profile)이며 애플리케이션도 사용하지 않는다.
> 단일 인스턴스에서는 in-memory 로 충분하다.
>
> Redis 가 실제로 필요해지는 시점(다중 인스턴스 운영, 세션·캐시 공유, 분산 락)에 연결한다:
> 0. 컨테이너 활성화 — 루트 `.env` 에 `COMPOSE_PROFILES=redis` ([[docker]])
> 1. Redis store 패키지 설치 (**컨테이너 안에서** — 위 의존성 주의 항목 참조)
> 2. `app.module.ts` 의 `CacheModule.register` 를 `registerAsync` + Redis store 로 교체
> 3. `modules/health` 에 Redis 인디케이터 추가 (지금은 DB 만 검사한다)
> 4. 연결 실패 시 앱이 죽지 않도록 폴백 전략을 정한다
>
> 끝까지 쓰지 않을 프로젝트라면 compose 에서 redis 서비스를 빼는 편이 낫다 — 쓰지 않는 컨테이너는 혼란만 준다.
> 끝까지 쓰지 않을 프로젝트라면 compose 에서 redis 서비스와 관련 env 를 지우는 편이 낫다.
- **Rate Limiting**: `@nestjs/throttler` 로 일원화 (`app.module.ts` 의 `APP_GUARD`). 초과 시 429 → `REQ_001`.
라우트 단위 조정은 `@Throttle({ default: { ttl, limit } })`, 제외는 `@SkipThrottle()` (헬스체크에 적용되어 있다).
**rate limiter 를 중복으로 추가하지 말 것** ([[security]])
+10
View File
@@ -53,3 +53,13 @@ THROTTLE_LIMIT=100
# (운영 환경에서는 이 값과 무관하게 항상 비활성화된다)
# ==========================================
DB_SYNCHRONIZE=false
# ==========================================
# 선택적 서비스 (Compose profiles)
#
# 기본값(비어 있음) = redis 를 기동하지 않는다.
# 캐시·세션 공유가 필요해지면 아래를 redis 로 바꾸면 컨테이너가 함께 뜬다.
# COMPOSE_PROFILES=redis
# 일회성으로 켜려면: docker compose --profile redis ... up -d
# ==========================================
COMPOSE_PROFILES=
+10
View File
@@ -53,3 +53,13 @@ THROTTLE_LIMIT=100
# (운영 환경에서는 이 값과 무관하게 항상 비활성화된다)
# ==========================================
DB_SYNCHRONIZE=false
# ==========================================
# 선택적 서비스 (Compose profiles)
#
# 기본값(비어 있음) = redis 를 기동하지 않는다.
# 캐시·세션 공유가 필요해지면 아래를 redis 로 바꾸면 컨테이너가 함께 뜬다.
# COMPOSE_PROFILES=redis
# 일회성으로 켜려면: docker compose --profile redis ... up -d
# ==========================================
COMPOSE_PROFILES=
+3 -1
View File
@@ -11,7 +11,7 @@ NestJS(백엔드) · Vue 3(웹) · Flutter(모바일)를 하나의 저장소에
| `frontend/` | Vue 3 + Vite | 웹 프론트엔드 | 5173 |
| `flutter/` | Flutter + Riverpod | 모바일 앱 | - |
| `db` | PostgreSQL | 데이터베이스 | 5432 |
| `redis` | Redis | 캐시 / 세션 | 6379 |
| `redis` | Redis | 캐시 / 세션 (**기본 비활성**) | 6379 |
## 사전 요구사항
@@ -41,6 +41,8 @@ docker compose -f docker-compose.yml -f docker-compose.dev.yml --env-file .env.d
- API: <http://localhost:3000/api>
- Swagger 문서: <http://localhost:3000/api-docs>
> Redis 는 기본적으로 뜨지 않습니다. 필요해지면 루트 `.env.{env}` 에 `COMPOSE_PROFILES=redis` 를 설정하세요.
전체 실행/배포 명령어는 [`.claude/skills/docker/SKILL.md`](./.claude/skills/docker/SKILL.md) 참조.
## 모바일 앱 실행 (Flutter)
+8 -1
View File
@@ -73,8 +73,10 @@ services:
depends_on:
db:
condition: service_healthy
# redis 는 profile 로 비활성화될 수 있으므로 선택적 의존으로 둔다
redis:
condition: service_healthy
required: false
healthcheck:
# localhost 는 컨테이너 안에서 ::1 로 먼저 해석되는데 Node 는 0.0.0.0(IPv4) 바인딩이므로 127.0.0.1 을 명시한다
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:${BACKEND_PORT}/ || exit 1"]
@@ -120,9 +122,14 @@ services:
- "com.centurylinklabs.watchtower.enable=true"
# ----------------------------------------
# 4. Redis — 내부 네트워크 전용
# 4. Redis — 내부 네트워크 전용 · 기본 비활성
#
# 기본적으로 기동하지 않는다. 캐시/세션 공유가 실제로 필요해질 때 켠다.
# 루트 .env.{APP_ENV} 에서 COMPOSE_PROFILES=redis
# 또는 일회성으로 docker compose --profile redis up -d
# ----------------------------------------
redis:
profiles: ["redis"]
container_name: ${PROJECT_NAME}-redis
image: ${REDIS_IMAGE}
command: redis-server --requirepass ${REDIS_PASSWORD}