6편에서 Compose 파일을 만드는 방법을 익혔다면 이제 "같은 코드지만 다른 규칙으로 실행되는 두 컨테이너"를 비교할 차례입니다. Node.js 앱을 예로 들면 dev/prod 차이가 훨씬 또렷하게 보입니다. 이번 글은 일반적인 Node 앱을 기준으로 운영 컨테이너와 개발 컨테이너를 나눠 봅니다.
이 글의 흐름
- Node 앱 기준으로 운영 컨테이너를 설계하는 기본 규칙
- 개발 컨테이너에서 의도적으로 열어 두는 요소
- 명령·포트·볼륨을 비교하는 3단 체크리스트
- Mini Lab: 두 컨테이너를 번갈아 실행해 보기
- 초보자가 꼭 구분해야 할 핵심 한 줄
읽기 카드
- 예상 소요 시간: 18분
- 사전 준비: npm dev 서버 실행 경험, Compose 명령 기초
- 읽고 나면: 하나의 코드베이스에서 dev/prod 컨테이너를 구분해 운영할 수 있습니다.
운영 컨테이너: 빌드된 산출물만 포함하기
운영 환경에서는 "바뀌지 않는 것"이 장점입니다. 아래 두 파일을 기준으로 생각해 보세요.
# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm ci --omit=dev
CMD ["node", "dist/server.js"]
# docker-compose.prod.yml
services:
app:
build:
context: .
dockerfile: Dockerfile
target: runner
ports:
- "8080:3000"
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:3000/health"]
interval: 30s
timeout: 3s
retries: 3
- 이미지는 멀티 스테이지로 빌드되어
dist/결과물만 포함합니다. npm ci --omit=dev를 사용해 개발 의존성을 제거합니다.ports에서는 외부 8080 → 내부 3000만 열어 두고, 나머지 포트는 감춥니다.restart,healthcheck같은 정책을 추가해 자동 복구를 준비합니다.
핵심 문장은 하나입니다. 운영 컨테이너는 단단할수록 좋다. 파일을 수정하거나 npm install을 다시 할 수 없도록 만드는 것이 오히려 안전합니다.
개발 컨테이너: 빠르게 고칠 수 있어야 한다
개발 환경에서는 "즉시 반영"이 가장 중요합니다. 그래서 세 가지를 의도적으로 열어 둡니다.
services:
app-dev:
image: node:20-alpine
working_dir: /app
command: sh -c "npm install && npm run dev"
volumes:
- .:/app
- /app/node_modules
ports:
- "5173:5173"
environment:
- NODE_ENV=development
profiles: ["dev"]
volumes로 로컬 폴더를 그대로 올리고, 익명 볼륨으로node_modules만 컨테이너 내부에 유지합니다.command로 npm dev 서버를 실행해 핫 리로드를 지원합니다.profiles덕분에docker compose up기본 실행에서는 개발 컨테이너가 뜨지 않습니다.
이 상태를 프로덕션에 들고 가면 npm install이 매번 실행되고, 코드가 실시간으로 변하기 때문에 장애가 나도 재현하기 어렵습니다. 그래서 dev 컨테이너는 반드시 별도 프로필이나 별도 Compose 파일로 분리해야 합니다.
3단 체크리스트: 명령 · 포트 · 볼륨
| 항목 | 운영 컨테이너 | 개발 컨테이너 |
|---|---|---|
| 명령 | Dockerfile CMD 그대로 실행, 변경 금지 |
Compose command로 dev 서버 실행 |
| 포트 | 필요한 최소 포트만 노출(예: 8080:3000) |
개발 서버 기본 포트 그대로 노출(예: 5173:5173) |
| 볼륨 | 읽기 전용 자산, 혹은 아예 없음 | 소스 폴더 바인드 + 의존성 익명 볼륨 |
이 표를 빠르게 점검해 보면 "지금 띄운 컨테이너가 진짜 운영인지" 헷갈리지 않습니다.
Mini Lab: 두 컨테이너 번갈아 실행하기
- 위 dev/prod Compose 스니펫을
compose.prod.yml,compose.dev.yml두 파일에 나눠 적습니다. docker compose -f compose.prod.yml up -d로 운영 버전을 띄운 뒤curl localhost:8080으로 확인합니다.docker compose -f compose.dev.yml --profile dev up -d를 실행해 개발 서버도 띄우고,docker compose ps로 포트가 어떻게 다른지 비교합니다.- 코드 한 줄을 수정한 뒤 브라우저를 새로고침해 dev 컨테이너의 즉시 반영을 확인합니다.
- 마지막으로
docker compose -f compose.dev.yml --profile dev down으로 개발 컨테이너만 종료합니다.
이 실습을 반복하면 "운영 컨테이너는 이미지가 바뀌지 않는 한 상태가 변하지 않는다"는 사실이 서서히 체감됩니다.
초보자가 꼭 구분해야 할 핵심 한 줄
정리하면 아주 단순합니다.
- 운영 컨테이너는 바뀌지 않아야 합니다.
- 개발 컨테이너는 빨리 바뀌어야 합니다.
이 한 줄만 기억해도 dev/prod 설정이 왜 갈라지는지 대부분 설명할 수 있습니다. 다음 8편에서는 이 두 컨테이너가 실제로 어떤 포트와 네트워크를 통해 통신하는지, 그리고 호스트에서 어떻게 접근할 수 있는지를 더 자세히 다룹니다.
💬 댓글
이 글에 대한 의견을 남겨주세요