docker-compose.dev.yml
Rama main · réplica de lectura del repositorio original.
# 로컬 개발용 Docker Compose — R-23 통합 dev stack (Sprint 3-4).
#
# 사용법
# docker compose -f docker-compose.dev.yml up -d
# docker compose -f docker-compose.dev.yml down -v # -v로 volume까지 정리
#
# 무엇을 포함하나 / 무엇을 포함 안 하나
#
# 포함:
# - server (apps/server, Hono, tsx-watch)
# - db (Postgres 17 — production과 메이저 버전 매칭, gotcha #16)
# - mock-openai (R-3 E2E용 mock LLM, scripts/mock-openai-server.ts)
#
# 포함 안 함 (의도적):
# - GoTrue + Kong + Supabase Studio — 로컬은 `supabase start` CLI가
# 표준 (CLAUDE.md "supabase start" 권장). dev compose에서 이 셋을
# 관리하면 supabase CLI와 두 가지 진실의 출처가 됨. CI에서도
# `supabase start`를 sidecar로 띄우는 게 supabase 팀이 유지하는
# 세팅 그대로라 깨질 일 적음.
# - web (apps/web) — `pnpm --filter web dev`로 직접 실행.
# Next.js dev server는 hot reload + RSC 변경 감지가 컨테이너 외부
# 실행이 훨씬 빠름.
#
# 환경 변수 (.env 또는 export)
# 필수: SUPABASE_URL / SUPABASE_ANON_KEY / SUPABASE_SERVICE_ROLE_KEY /
# ENCRYPTION_KEY
# ClickHouse(컨테이너): 아래 service 안에서 자동 설정 (외부 .env 불필요)
# Mock OpenAI: 자동 (port 4000)
version: '3.9'
services:
server:
build:
context: ./apps/server
dockerfile: Dockerfile.dev
ports:
- '3001:3001'
environment:
PORT: 3001
SUPABASE_URL: ${SUPABASE_URL:-http://supabase-kong:8000}
SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
SUPABASE_SERVICE_ROLE_KEY: ${SUPABASE_SERVICE_ROLE_KEY}
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
# 로컬 Postgres 직결. Supavisor는 로컬 스택에 없으므로 pooler 대신
# db service를 그대로 가리킨다 — 프로덕션은 포트 6543 transaction
# 모드를 쓴다 (.env.example 참고).
SUPABASE_DB_POOLER_URL: postgresql://postgres:postgres@db:5432/postgres
# E2E 모드에서 mock OpenAI를 LLM upstream으로 사용. proxy가 실제
# OpenAI API에 도달하지 않게 하려면 OPENAI_API_BASE를 mock-openai
# service URL로 override. 평상시 dev에선 unset 두면 진짜 OpenAI.
OPENAI_API_BASE: ${OPENAI_API_BASE:-}
volumes:
- ./apps/server/src:/app/src
restart: unless-stopped
depends_on:
db:
condition: service_healthy
# R-22: readiness probe — same endpoint Vercel preview / production
# docker invocations use. /health/ready pings Postgres (both routes) +
# Upstash (Upstash 'skipped' is OK), so a half-broken container shows
# up as "unhealthy" before Docker routes traffic to it. start_period
# gives node + tsx-watch time to compile on cold container start.
#
# Why `node -e` instead of curl/wget: node:22-alpine ships neither by
# default, and adding an apk install just for healthcheck inflates the
# image. node 22 has fetch built in, so this stays zero-dep.
healthcheck:
test:
- 'CMD-SHELL'
- "node -e \"fetch('http://localhost:3001/health/ready').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
db:
# R-23: production은 Postgres 17 (gotcha #16). dev도 17로 맞춰서
# major-version skew로 인한 SQL/feature 차이 없게. supabase/postgres
# 이미지는 extension까지 포함이라 supabase CLI 환경과 동일하지만
# 별도 dependency가 무거우므로 plain alpine으로 시작 — extension
# 필요해지면 image만 supabase/postgres:17.x로 swap.
image: postgres:17-alpine
ports:
- '54322:5432'
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres']
interval: 5s
timeout: 5s
retries: 5
mock-openai:
# R-3 E2E용 OpenAI-compatible mock. server의 OPENAI_API_BASE를
# http://mock-openai:4000/v1로 override하면 실제 OpenAI API에 비용
# 없이 E2E를 돌릴 수 있다. 평상시 dev에선 OPENAI_API_BASE를 비워두면
# 진짜 OpenAI로 갑니다 (이 컨테이너는 그래도 띄우지만 traffic이 안 옴).
build:
context: ./apps/server
dockerfile: Dockerfile.dev
command: ['pnpm', 'exec', 'tsx', 'scripts/mock-openai-server.ts']
ports:
- '4000:4000'
environment:
MOCK_OPENAI_PORT: 4000
volumes:
- ./apps/server/scripts:/app/scripts
healthcheck:
test:
- 'CMD-SHELL'
- "node -e \"fetch('http://localhost:4000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
volumes:
postgres_data: