Use Cases (업데이트: 2026. 6. 1.)

Claude Code Docker Compose 실전 가이드: 로컬 App, Postgres, Redis, Worker

Claude Code와 Docker Compose로 app, Postgres, Redis, worker 로컬 개발 환경을 구성하는 실전 가이드.

Claude Code Docker Compose 실전 가이드: 로컬 App, Postgres, Redis, Worker

Docker Compose는 Web app, PostgreSQL, Redis, background worker를 한 번에 띄우기 위한 로컬 개발 설계도입니다. Docker의 Compose 공식 문서는 Compose를 여러 컨테이너 애플리케이션을 정의하고 실행하며, 서비스와 네트워크, 볼륨을 하나의 YAML 파일로 관리하는 도구로 설명합니다.

그래서 Claude Code와 궁합이 좋습니다. “Docker 환경 만들어줘”라고 막연히 부탁하는 대신 compose.yaml, Dockerfile, .env.example, Makefile을 두고 Claude Code에게 검토하게 하면 결과가 훨씬 재현 가능해집니다.

Masa가 작은 Next.js + queue worker 검증 프로젝트에서 이 구성을 시험했을 때 효과가 컸던 부분은 프롬프트가 아니라 세부 설계였습니다. Postgres 준비 대기, Redis 지속성, node_modules 볼륨, migration/test 명령, CI 차이를 먼저 정한 것이 안정성을 만들었습니다.

이 글은 app + PostgreSQL + Redis + worker 구성을 그대로 복사해 시작할 수 있게 정리합니다. Compose는 로컬 개발, 통합 테스트, 온보딩에는 뛰어나지만, 운영 환경은 별도입니다. 오케스트레이션, secret, 모니터링, 백업, 보안 경계, 비용을 따로 검토해야 합니다.

구조

flowchart LR
  Dev["Developer"] --> App["app: web server"]
  App --> Pg["postgres: database"]
  App --> Redis["redis: cache and queue"]
  Worker["worker: background jobs"] --> Pg
  Worker --> Redis
  App --> Volume["named volume: node_modules"]
  Pg --> PgVol["named volume: postgres_data"]
  Redis --> RedisVol["named volume: redis_data"]

app은 웹 애플리케이션을 띄우고, worker는 메일, webhook, 이미지 처리, queue job 같은 백그라운드 작업을 처리합니다. postgresredis에는 healthcheck를 넣어 컨테이너가 “켜진 상태”가 아니라 실제로 연결 가능한 상태를 확인합니다.

문법은 Compose file referenceservices reference를 기준으로 보는 것이 안전합니다. Claude Code에 리뷰를 맡길 때도 이 공식 문서를 기준으로 하라고 적어두면 오래된 docker-compose 예시를 덜 가져옵니다.

Compose가 맞는 곳

용도Compose가 좋은 이유주의할 점
로컬 개발app, DB, Redis, worker를 한 명령으로 재현OS별 파일 mount 성능 차이
통합 테스트테스트용 DB와 Redis를 필요할 때 생성CI 포트와 cache를 명시
신규 멤버 온보딩.env.examplemake setup으로 절차 고정예시 파일에 진짜 secret 금지
운영 환경작은 내부 도구에서는 후보가 될 수 있음보안, 복구, 스케일, 비용 검토 필수

운영 경계를 분리하는 것이 중요합니다. Compose는 개발자 컴퓨터에 같은 작업대를 만드는 데 강합니다. 운영 배포는 ECS, Kubernetes, Cloud Run, Fly.io, Render 또는 기존 플랫폼과 비교해야 합니다. Claude Code에는 로컬 파일을 배포 계획으로 확정하지 말고 마이그레이션 제약을 목록화하라고 지시하는 편이 안전합니다.

복사해서 쓰는 compose.yaml

아래 예시는 Node.js 또는 Next.js 프로젝트를 가정합니다. npm run dev, npm run worker:dev, migration 명령은 실제 package.json에 맞게 바꾸세요.

# compose.yaml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: dev
    command: npm run dev -- --hostname 0.0.0.0
    ports:
      - "3000:3000"
    env_file:
      - .env
    environment:
      DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?schema=public
      REDIS_URL: redis://redis:6379
    volumes:
      - .:/workspace
      - node_modules:/workspace/node_modules
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "node -e \"fetch('http://127.0.0.1:3000/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""]
      interval: 10s
      timeout: 5s
      retries: 12
      start_period: 20s

  worker:
    build:
      context: .
      dockerfile: Dockerfile
      target: dev
    command: npm run worker:dev
    env_file:
      - .env
    environment:
      DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?schema=public
      REDIS_URL: redis://redis:6379
    volumes:
      - .:/workspace
      - node_modules:/workspace/node_modules
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy

  postgres:
    image: postgres:16-alpine
    ports:
      - "5432:5432"
    env_file:
      - .env
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./docker/postgres/init:/docker-entrypoint-initdb.d:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
      interval: 5s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7-alpine
    command: ["redis-server", "--appendonly", "yes"]
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 10

volumes:
  node_modules:
  postgres_data:
  redis_data:

핵심은 세 가지입니다. 컨테이너끼리는 서비스 이름으로 통신하므로 localhost가 아니라 postgres:5432, redis:6379를 씁니다. node_modules는 named volume으로 분리해 bind mount가 컨테이너 의존성을 덮어쓰지 않게 합니다. Postgres healthcheck의 이중 달러 기호는 Compose가 아니라 컨테이너 shell에서 변수를 읽게 하기 위한 것입니다.

Dockerfile

개발과 운영 이미지를 stage로 나눕니다. 초보 팀에서는 여러 Dockerfile보다 하나의 Dockerfile에 stage를 나누는 편이 관리하기 쉽습니다.

# Dockerfile
# syntax=docker/dockerfile:1

FROM node:22-alpine AS base
WORKDIR /workspace
ENV NEXT_TELEMETRY_DISABLED=1
COPY package*.json ./
RUN npm ci

FROM base AS dev
EXPOSE 3000
CMD ["npm", "run", "dev", "--", "--hostname", "0.0.0.0"]

FROM base AS build
COPY . .
RUN npm run build

FROM node:22-alpine AS production
WORKDIR /workspace
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=build /workspace/.next ./.next
COPY --from=build /workspace/public ./public
USER node
EXPOSE 3000
CMD ["npm", "start"]

dev stage는 소스 코드를 복사하지 않습니다. Compose volume으로 호스트 파일을 보여주기 때문입니다. 반대로 build stage는 전체 저장소를 복사해 CI와 운영 이미지에서도 같은 방식으로 빌드합니다.

.env.example

.env는 커밋하지 않습니다. 대신 .env.example을 커밋해 필요한 변수 이름과 예시 값을 공유합니다.

# .env.example
POSTGRES_USER=app
POSTGRES_PASSWORD=app_password
POSTGRES_DB=app_development

DATABASE_URL=postgresql://app:app_password@postgres:5432/app_development?schema=public
REDIS_URL=redis://redis:6379
NODE_ENV=development
PORT=3000

env_file은 컨테이너에 전달할 환경 변수입니다. Compose 파일 안의 변수 치환도 프로젝트의 .env를 읽습니다. 둘을 혼동하지 않도록 값과 이름을 맞춰두는 것이 좋습니다.

Makefile과 일회성 명령

반복 명령은 Makefile에 고정합니다. make를 쓰지 않는 팀이라면 같은 내용을 package.json scripts로 옮기면 됩니다.

COMPOSE = docker compose --env-file .env -f compose.yaml

.PHONY: setup up up-d down restart logs ps app-shell db-shell redis-cli migrate seed test lint clean

setup:
	cp .env.example .env
	$(COMPOSE) build

up:
	$(COMPOSE) up --build

up-d:
	$(COMPOSE) up -d --build

down:
	$(COMPOSE) down

restart:
	$(COMPOSE) restart app worker

logs:
	$(COMPOSE) logs -f app worker postgres redis

ps:
	$(COMPOSE) ps

app-shell:
	$(COMPOSE) exec app sh

db-shell:
	$(COMPOSE) exec postgres psql -U app -d app_development

redis-cli:
	$(COMPOSE) exec redis redis-cli

migrate:
	$(COMPOSE) run --rm app npm run db:migrate

seed:
	$(COMPOSE) run --rm app npm run db:seed

test:
	$(COMPOSE) run --rm app npm test

lint:
	$(COMPOSE) run --rm app npm run lint

clean:
	$(COMPOSE) down --volumes --remove-orphans

exec는 이미 실행 중인 컨테이너에 들어갈 때 사용합니다. run --rm은 lint, test, migration, seed처럼 새 임시 컨테이너에서 한 번 실행할 때 사용합니다.

Claude Code 리뷰 프롬프트

Claude Code common workflows는 코드 이해, 수정, 테스트, 리뷰를 작은 작업으로 나누는 방식을 권장합니다. Compose 리뷰도 같은 방식이 좋습니다.

이 저장소의 Docker Compose 구성을 리뷰해 주세요.

대상:
- compose.yaml
- Dockerfile
- .env.example
- package.json
- CI workflow 파일이 있으면 포함

검토 항목:
1. app + postgres + redis + worker가 로컬에서 실행 가능한가
2. healthcheck와 depends_on 사용이 적절한가
3. named volume과 bind mount 구분이 안전한가
4. .env.example에 실제 secret이 없는가
5. migrate, seed, test, lint 일회성 명령이 있는가
6. CI에서 포트, cache, 권한, 시작 대기로 실패할 위험이 있는가
7. 운영 환경에 쓰기 전에 별도 검토할 항목은 무엇인가

제약:
- 기존 framework와 package manager를 따른다
- 큰 리팩터링은 제안만 한다
- 파일을 수정하면 이유와 검증 명령을 함께 쓴다

이 프롬프트를 CLAUDE.md 근처에 두면 팀 리뷰 기준으로 재사용할 수 있습니다. 함께 읽을 글로는 Dev Container 가이드CI/CD 설정 가이드가 있습니다.

실무 유스케이스

첫 번째는 신규 멤버의 첫날 환경 설정입니다. .env.example을 복사하고 make up만 실행해 app, DB, Redis, worker가 뜨면 README가 짧아지고 질문도 더 구체적이 됩니다.

두 번째는 queue 처리 검증입니다. 메일 발송, 이미지 변환, 결제 webhook 이후 작업은 web server만으로 검증하기 어렵습니다. Compose에 worker를 넣으면 Redis queue의 job을 손쉽게 재현할 수 있습니다.

세 번째는 통합 테스트 안정화입니다. SQLite 대체 테스트에서는 통과하지만 Postgres에서 실패하는 문제는 흔합니다. Compose로 실제 DB에 가까운 환경을 세우면 SQL 방언과 migration 문제를 빨리 찾을 수 있습니다.

네 번째는 Claude Code 기반 인프라 리뷰입니다. 사람은 localhost, 오래된 volume, healthcheck 누락, 예시 secret을 놓치기 쉽습니다. 리뷰 관점을 템플릿화하면 매번 같은 기준으로 확인할 수 있습니다.

흔한 함정

가장 흔한 실수는 app 컨테이너에서 localhost로 DB에 접속하는 것입니다. Compose 네트워크에서는 서비스 이름이 DNS 이름입니다. postgresredis를 사용하세요.

두 번째는 depends_on을 완전한 준비 보장으로 보는 것입니다. condition: service_healthy는 도움이 되지만 app에는 재시도 로직이 필요하고, worker가 migration보다 먼저 중요한 작업을 실행하지 않게 해야 합니다.

오래된 named volume도 문제를 만듭니다. schema가 바뀌었는데 postgres_data가 남아 있으면 로컬 상태가 저장소와 달라집니다. 새로 검증할 때는 make clean을 쓰되 로컬 데이터가 삭제된다는 점을 확인하세요.

.env.example에 실제 API key를 넣지 마세요. 예시는 app_passwordreplace_me로 두고, 운영 secret은 Docker secrets, cloud secret manager, CI secret store에서 관리합니다.

마지막으로 로컬 Compose 파일을 그대로 운영 설계서로 삼지 마세요. 운영에서는 TLS, 네트워크 공개 범위, DB 백업, Redis persistence, 모니터링, 취약점 스캔, 이미지 출처, 권한, 비용을 따로 봐야 합니다.

ClaudeCodeLab 교육과 템플릿

이 구성을 자신의 저장소에 맞게 정리하려면 ClaudeCodeLab의 products and templatesCLAUDE.md, 리뷰 프롬프트, setup runbook을 먼저 표준화하는 것이 빠릅니다. 팀 도입, Docker Desktop 차이, CI/CD, 권한 경계, 운영 전환 판단까지 같이 설계해야 한다면 training and consultation을 이용하세요.

공식 참고 자료는 Docker Compose, Compose file reference, services reference, Claude Code common workflows입니다.

이 글의 구성을 실제로 적용해 본 결과, Masa의 테스트 프로젝트에서는 make up으로 app, Postgres, Redis, worker가 함께 올라왔고 healthcheck 덕분에 DB 준비 전에 앱이 실패하는 경합이 줄었습니다. 다만 오래된 named volume이 남아 있으면 migration 결과가 달라질 수 있었습니다. 결론은 명확합니다. Compose는 로컬 개발 재현성을 높이는 강한 기반이지만, 운영 결정은 보안, 관측, 복구, 비용을 별도로 리뷰해야 합니다.

#Claude Code #Docker #Docker Compose #로컬 개발 #PostgreSQL #Redis
무료

무료 PDF: Claude Code 치트시트

이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.

개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.

Masa

작성자 소개

Masa

Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.