이 글은 누구를 위한 것인가
- "내 로컬에서는 됐는데" 문제를 없애고 싶은 팀
- Next.js 앱과 PostgreSQL/Redis를 Docker로 묶어서 관리하려는 개발자
- 개발과 프로덕션 Docker 환경을 분리해서 관리하려는 DevOps 담당자
들어가며
팀원 5명이 각자 다른 Node.js 버전, 다른 PostgreSQL 버전을 쓰면 "내 로컬에서는 됐는데" 문제가 생긴다. Docker Compose는 이 문제를 완전히 해결한다. docker compose up 한 번으로 모든 팀원이 동일한 환경에서 개발한다.
이 글은 bluefoxdev.kr의 Docker 개발 환경 가이드 를 참고하여 작성했습니다.
1. 개발 환경 구성 전략
[Docker Compose 서비스 구성]
app: Next.js 개발 서버 (핫 리로드)
db: PostgreSQL 15
redis: Redis 7
adminer: DB 관리 UI (개발용)
[볼륨 전략]
소스 코드: 바인드 마운트 (./:/app)
→ 코드 변경 즉시 반영 (핫 리로드)
node_modules: 익명 볼륨 (/app/node_modules)
→ 컨테이너 내부 것 보존 (호스트 것 무시)
DB 데이터: 명명된 볼륨 (postgres-data)
→ 컨테이너 재시작 후에도 데이터 유지
[환경 분리]
docker-compose.yml → 공통 (개발)
docker-compose.prod.yml → 프로덕션 오버라이드
docker-compose.test.yml → 테스트 환경
.env → 로컬 환경변수 (git 제외)
.env.example → 팀 공유용 예시
2. Docker Compose 풀 구성
# Dockerfile.dev - 개발용
FROM node:20-alpine
# pnpm 설치
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
# 의존성 레이어 (캐시 최적화)
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
# 소스 코드는 볼륨으로 마운트 (COPY 하지 않음)
EXPOSE 3000
CMD ["pnpm", "dev"]
# Dockerfile - 프로덕션용 멀티스테이지 빌드
FROM node:20-alpine AS base
RUN corepack enable && corepack prepare pnpm@latest --activate
# 1단계: 의존성 설치
FROM base AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --prod
# 2단계: 빌드
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN pnpm build
# 3단계: 실행 (최소 이미지)
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
# Next.js 스탠드얼론 출력 사용
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]
# docker-compose.yml
version: '3.9'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- .:/app # 소스 코드 마운트
- /app/node_modules # node_modules 격리
- /app/.next # Next.js 빌드 캐시
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
- REDIS_URL=redis://redis:6379
env_file:
- .env.local # 로컬 비밀 값 (git 제외)
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- app-network
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
volumes:
- postgres-data:/var/lib/postgresql/data
- ./docker/init.sql:/docker-entrypoint-initdb.d/init.sql # 초기화 SQL
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
networks:
- app-network
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis-data:/data
ports:
- "6379:6379"
networks:
- app-network
adminer:
image: adminer
ports:
- "8080:8080"
depends_on:
- db
networks:
- app-network
profiles:
- tools # docker compose --profile tools up 으로만 실행
volumes:
postgres-data:
redis-data:
networks:
app-network:
driver: bridge
# docker-compose.prod.yml - 프로덕션 오버라이드
version: '3.9'
services:
app:
build:
dockerfile: Dockerfile
environment:
- NODE_ENV=production
restart: unless-stopped
# 핫 리로드 볼륨 제거 (프로덕션에서는 필요 없음)
volumes: []
db:
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} # .env.prod 필요
volumes:
- postgres-data:/var/lib/postgresql/data
# 초기화 SQL 제거 (이미 마이그레이션 완료)
ports: [] # 외부 노출 제거
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- app
# Makefile - 자주 쓰는 명령어 단축키
.PHONY: dev prod down logs shell db-shell migrate seed
dev:
docker compose up --build
dev-bg:
docker compose up -d --build
prod:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up --build
down:
docker compose down
down-all:
docker compose down -v # 볼륨까지 삭제
logs:
docker compose logs -f app
shell:
docker compose exec app sh
db-shell:
docker compose exec db psql -U postgres myapp
migrate:
docker compose exec app pnpm prisma migrate dev
seed:
docker compose exec app pnpm prisma db seed
# 처음 세팅
setup:
cp .env.example .env.local
docker compose up -d --build
docker compose exec app pnpm prisma migrate dev
docker compose exec app pnpm prisma db seed
@echo "개발 환경 준비 완료: http://localhost:3000"
마무리
Docker Compose 개발 환경의 핵심은 볼륨 전략이다. 소스 코드는 바인드 마운트로 핫 리로드를 유지하고, node_modules는 익명 볼륨으로 컨테이너 내부 것을 사용한다. Makefile로 자주 쓰는 명령어를 단축키로 제공하면 팀 진입 장벽이 크게 낮아진다. 신규 팀원은 git clone → make setup 하나로 개발 환경을 갖춘다.