블로그로 돌아가기
DevOps4 min read

Next.js 앱 Docker로 배포하기: 완벽 가이드

Next.js 애플리케이션을 Docker로 컨테이너화하고 프로덕션 환경에 배포하는 방법을 단계별로 알아봅니다.


Next.js 앱 Docker로 배포하기: 완벽 가이드


Next.js 애플리케이션을 Docker로 배포하면 환경 일관성, 쉬운 확장, 간편한 롤백이 가능합니다. 이 글에서는 실제 프로덕션 배포 과정을 다룹니다.


프로젝트 구조


my-app/
├── src/
├── public/
├── Dockerfile
├── docker-compose.yml
├── .dockerignore
├── next.config.ts
└── package.json

Dockerfile 작성


Multi-stage 빌드로 이미지 크기를 최소화합니다.


<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">1. Dependencies 설치</h1>
FROM node:22-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --production

<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">2. 빌드</h1>
FROM node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build


<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">3. 프로덕션 이미지</h1>
FROM node:22-alpine AS runner
WORKDIR /app


ENV NODE_ENV=production


<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">보안: non-root 유저 사용</h1>
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs


<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">standalone 출력 복사</h1>
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static


USER nextjs


EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"


CMD ["node", "server.js"]


next.config.ts 설정


standalone 출력을 활성화합니다.


import type { NextConfig } from 'next';

const nextConfig: NextC {
output: 'standalone',
};


export default nextConfig;


.dockerignore


불필요한 파일을 제외하여 빌드 속도를 높입니다.


node_modules
.next
.git
*.md
.env*.local

docker-compose.yml


version: '3.8'

services:
app:
build: .
container_name: my-nextjs-app
ports:
- "3000:3000"
environment:
- NODE_ENV=production
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://localhost:3000/').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"]
interval: 30s
timeout: 10s
retries: 3
networks:
- app-network


networks:
app-network:
driver: bridge


빌드 및 실행


로컬 테스트


<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">이미지 빌드</h1>
docker compose build

<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">컨테이너 실행</h1>
docker compose up -d


<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">로그 확인</h1>
docker compose logs -f


<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">상태 확인</h1>
docker compose ps


프로덕션 배포


<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">이미지 빌드 (캐시 무시)</h1>
docker compose build --no-cache

<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">컨테이너 재시작</h1>
docker compose up -d --force-recreate


<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">이전 이미지 정리</h1>
docker image prune -f


Nginx 리버스 프록시


SSL과 함께 사용할 Nginx 설정입니다.


server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}

server {
listen 443 ssl http2;
server_name example.com;


ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;


location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}


# 정적 파일 캐싱
location /_next/static {
proxy_pass http://localhost:3000;
add_header Cache-Control "public, max-age=31536000, immutable";
}
}


환경 변수 관리


.env 파일 사용


<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">docker-compose.yml</h1>
services:
app:
env_file:
- .env.production

Docker Secrets (민감한 정보)


services:
app:
secrets:
- db_password
environment:
- DB_PASSWORD_FILE=/run/secrets/db_password

secrets:
db_password:
file: ./secrets/db_password.txt


모니터링


Health Check 확인


docker inspect --format='{{.State.Health.Status}}' my-nextjs-app

리소스 사용량 모니터링


docker stats my-nextjs-app

트러블슈팅


1. 빌드 실패


<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">캐시 삭제 후 재빌드</h1>
docker builder prune -f
docker compose build --no-cache

2. 메모리 부족


<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">docker-compose.yml에 리소스 제한 추가</h1>
deploy:
resources:
limits:
memory: 1G

3. 권한 문제


<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">파일 권한 확인</h1>
ls -la .next/
<h1 class="text-3xl font-bold mt-8 mb-6 text-foreground">Dockerfile에서 chown 확인</h1>

마무리


Docker를 활용하면 Next.js 앱을 일관되게 배포할 수 있습니다. CI/CD와 결합하면 완전 자동화된 배포 파이프라인을 구축할 수 있습니다.


다음 글에서는 GitHub Actions를 활용한 자동 배포 설정을 다루겠습니다.