Docker Compose로 프론트엔드 개발 환경 완전 통일하기

프론트엔드

DockerDocker Compose개발 환경Next.jsDevOps

이 글은 누구를 위한 것인가

  • "내 로컬에서는 됐는데" 문제를 없애고 싶은 팀
  • 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 clonemake setup 하나로 개발 환경을 갖춘다.