posts

Next.js Dockerfile과 Docker Compose 배포 메모

May 11, 2025 updated May 11, 2025 dockernextjsnginxnode

이 글은 예전에 Dockerfile이랑 Compose 설정을 비교하면서 적어둔 메모를 정리한 버전입니다. 개발용/프로덕션용 이미지 구성, 빌드 방식, EC2 배포 방식까지 한 번에 이어지는 흐름이라 나중에 다시 보기 좋게 남겨둡니다.

1. Dockerfile 설명

1-1. 단일 스테이지 Dockerfile

# syntax=docker.io/docker/dockerfile:1

FROM node:18-alpine
WORKDIR /app

# 의존성 파일(package.json, lock 파일 등)을 복사한 후, 사용 중인 패키지 매니저에 따라 의존성 설치
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i; \
else echo "Warning: Lockfile not found. It is recommended to commit lockfiles to version control." && yarn install; \
fi

# 소스 코드 및 설정 파일들을 복사
COPY src ./src
COPY public ./public
COPY next.config.js .
COPY tsconfig.json .

# CMD에서 조건에 따라 dev 모드를 실행
CMD \
if [ -f yarn.lock ]; then yarn dev; \
elif [ -f package-lock.json ]; then npm run dev; \
elif [ -f pnpm-lock.yaml ]; then pnpm dev; \
else npm run dev; \
fi
•	용도: 주로 개발 환경에서 사용하도록 구성된 Dockerfile입니다.
•	주요 기능:
•	의존성 설치: lock 파일을 확인하여 적절한 패키지 매니저(yarn/npm/pnpm)로 의존성을 설치합니다.
•	파일 복사: src, public, next.config.js, tsconfig.json 등을 컨테이너로 복사합니다.
•	실행 명령: 개발 서버(예: yarn dev 또는 npm run dev)를 시작합니다.

1-2. 멀티 스테이지 Dockerfile (dev.dockerfile / prod.dockerfile)

이 Dockerfile은 두 단계로 구성되어 있으며, 빌드 및 프로덕션 실행을 위한 최적화된 이미지를 만듭니다.

빌더 단계 (builder)

FROM node:18-alpine AS base
FROM base AS builder
WORKDIR /app

# 의존성 설치
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
RUN ... (위와 동일한 조건문으로 의존성 설치)

# 소스 코드 복사 및 환경 변수 설정(빌드 타임 ARG 사용)
COPY src ./src
COPY public ./public
COPY next.config.js .
COPY tsconfig.json .
ARG ENV_VARIABLE
ENV ENV_VARIABLE=${ENV_VARIABLE}
ARG NEXT_PUBLIC_ENV_VARIABLE
ENV NEXT_PUBLIC_ENV_VARIABLE=${NEXT_PUBLIC_ENV_VARIABLE}

# Next.js 빌드 실행
RUN \
if [ -f yarn.lock ]; then yarn build; \
elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then pnpm build; \
else npm run build; \
fi
•	목적: 프로젝트를 빌드하여 프로덕션용 최적화된 산출물을 생성합니다.
•	특징:
•	빌드 시점에 필요한 환경 변수(예: API 키, 모드 등)를 ARG와 ENV를 통해 주입합니다.
•	Next.js의 빌드를 실행하여 .next 폴더 내에 최적화된 산출물을 생성합니다.

런너 단계 (runner)

FROM base AS runner
WORKDIR /app

# 비 root 사용자 생성 및 전환 (보안 강화)
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
USER nextjs

# 프로덕션 실행에 필요한 파일 복사
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

# 런타임 환경 변수 재설정 (빌드 때와 별도)
ARG ENV_VARIABLE
ENV ENV_VARIABLE=${ENV_VARIABLE}
ARG NEXT_PUBLIC_ENV_VARIABLE
ENV NEXT_PUBLIC_ENV_VARIABLE=${NEXT_PUBLIC_ENV_VARIABLE}

# 서버 시작
CMD ["node", "server.js"]
•	목적: 빌더 단계에서 생성된 프로덕션 빌드 산출물을 기반으로 실행 가능한 이미지를 생성합니다.
•	특징:
•	비 root 사용자로 실행하여 보안을 강화합니다.
•	Next.js의 Output Standalone을 활용하여 필요한 파일만 복사해 이미지 크기를 줄입니다.
•	실행 커맨드: node server.js를 통해 Next.js 서버를 구동합니다. (빌드 시 생성된 standalone 파일에 server.js가 포함되어 있어야 함)
  1. Docker Compose 파일 설명

2-1. 개발용 Compose (예시에서 volumes 사용)

services: next-app: container_name: next-app build: context: ./next-app dockerfile: dev.Dockerfile environment: ENV_VARIABLE: ${ENV_VARIABLE} NEXT_PUBLIC_ENV_VARIABLE: ${NEXT_PUBLIC_ENV_VARIABLE} env_file:

  • .env volumes:
  • ./next-app/src:/app/src
  • ./next-app/public:/app/public restart: always ports:
  • 3000:3000 networks:
  • my_network

networks: my_network: external: true

•	용도: 개발 환경에서 사용.
•	주요 기능:
•	볼륨 마운트: 로컬 src 및 public 폴더를 컨테이너와 동기화하여 코드 변경 시 자동으로 반영됩니다.
•	환경 변수 설정: Compose 파일 내에서 직접 또는 .env 파일을 통해 환경 변수를 주입합니다.
•	네트워크: 외부에 미리 생성된 my_network 네트워크를 사용하여 다른 컨테이너(예: Nginx, DB)와 통신 가능.

2-2. 프로덕션용 Compose (볼륨 마운트 없음)

services: next-app: container_name: next-app build: context: ./next-app dockerfile: prod.Dockerfile args: ENV_VARIABLE: ${ENV_VARIABLE} NEXT_PUBLIC_ENV_VARIABLE: ${NEXT_PUBLIC_ENV_VARIABLE} restart: always ports:

  • 3000:3000 networks:
  • my_network

networks: my_network: external: true

•	용도: 프로덕션 환경에서 사용.
•	주요 기능:
•	빌드 인자 전달: 프로덕션 빌드에 필요한 환경 변수를 빌드 시점에 전달합니다.
•	볼륨 미사용: 빌드된 산출물을 이미지에 포함시키므로 외부 볼륨 마운트 없이 독립적으로 실행됩니다.
•	네트워크 설정: 동일하게 외부 네트워크(my_network)를 사용하여 다른 서비스와 통신할 수 있습니다.
  1. 현재 프로젝트에 적용할 변경 사항

현재 프로젝트 디렉터리 구조를 보면, src 대신 app, components, configs 등 여러 디렉터리가 존재합니다. 따라서 아래와 같이 변경할 필요가 있습니다: • 파일 복사 경로 수정: • Dockerfile 내에서 COPY src ./src 대신 프로젝트 구조에 맞게 전체 소스(예: app, components, pages 등)를 복사해야 합니다. • 예시:

COPY app ./app COPY components ./components COPY configs ./configs COPY public ./public COPY next.config.ts . # 파일명이 ts 확장자인 경우 COPY tsconfig.json .

•	빌드 및 실행 명령어 확인:
•	Next.js 13의 App Router를 사용 중이라면, 빌드 산출물이 standalone 모드로 생성되는지 확인하세요.
•	server.js 파일가 생성되지 않을 경우, Next.js 공식 Output Standalone 가이드를 참고하여 Dockerfile을 수정해야 합니다.
•	환경 변수 처리:
•	프로젝트에서 사용하는 환경 변수 이름과 값이 Dockerfile과 Compose 파일에 맞게 설정되어 있는지 확인합니다.
•	패키지 매니저:
•	현재 사용하는 패키지 매니저에 따라 lock 파일 이름이 올바른지 점검합니다.
•	네트워크:
•	EC2에서 실행 시, Compose 파일에 정의된 외부 네트워크(my_network)가 이미 생성되어 있는지 확인하거나, Compose에서 내부 네트워크로 변경할 수 있습니다.
  1. EC2에 소스 올리는 방식: Git Clone vs 파일 직접 전송

Git Clone 방식 • 장점: • 버전 관리: Git 리포지토리의 히스토리와 태그를 그대로 유지할 수 있어, 나중에 롤백이나 업데이트가 용이합니다. • 자동화: CI/CD 파이프라인 구축 시 Git 기반 배포가 일반적이며, 여러 개발자와의 협업 시에도 편리합니다. • 투명성: 소스 코드 변경 사항을 추적하기 쉽고, 외주 프로젝트의 경우 코드 관리 및 유지보수가 명확해집니다. • 단점: • 리포지토리 접근 권한: 외주 프로젝트의 경우, 고객과의 권한 관리(예: private 리포지토리)가 필요할 수 있습니다.

파일 직접 전송 방식 (SCP, SFTP 등) • 장점: • 단순성: 한 번의 파일 전송으로 전체 소스 코드를 배포할 수 있어, 설정이 간단합니다. • 보안: Git 사용에 익숙하지 않은 고객이라면, 단순히 압축 파일로 전달 후 EC2에 업로드할 수 있습니다. • 단점: • 버전 관리 부재: 파일 전송 후에는 Git의 버전 관리 혜택을 누리기 어렵습니다. 이후 유지보수나 업데이트 시 혼선이 생길 수 있습니다. • 자동화 어려움: 수동 전송은 반복 배포나 CI/CD 환경 구축 시 불리합니다.

외주 프로젝트에서 추천하는 방식 • 권장 방식: Git Clone (즉, Git 기반 배포) • 외주 프로젝트라도 소스 코드의 버전 관리와 협업, 추후 업데이트를 고려하면 Git 리포지토리(예: GitHub, GitLab, Bitbucket 등)를 통한 배포가 훨씬 관리하기 쉽습니다. • 고객과 사전에 리포지토리 접근 권한(Private Repository 권한 등)을 조율하면, 안정적이고 투명한 소스 코드 인계가 가능합니다. • 단, 고객의 요구사항이나 보안 정책에 따라 파일 직접 전송이 필요하다면, 압축 파일로 전달 후 EC2에 업로드하는 방법도 고려할 수 있습니다.

결론

  1. Dockerfile 및 Compose 파일 • 개발용과 프로덕션용으로 분리되어 있으며, 각각 의존성 설치, 환경 변수 주입, 파일 복사, 빌드, 실행 방식이 다릅니다. • 현재 프로젝트의 디렉터리 구조에 맞게 COPY 명령어 경로와 관련 설정을 수정해야 합니다. • Next.js 13 (App Router) 사용 시 standalone 출력 관련 설정(예: server.js 생성 여부)을 확인하여 필요한 경우 Dockerfile을 업데이트합니다.
  2. 소스 배포 방식 • 외주 프로젝트에서는 Git 기반 배포 (Git Clone) 방식을 권장합니다. → 버전 관리, 업데이트, 협업 측면에서 유리하며, 고객에게도 투명한 인수인계를 할 수 있습니다. • 단, 고객 요구나 보안 정책에 따라 파일 직접 전송 방식도 고려할 수 있으므로, 사전 협의가 필요합니다.

이러한 내용을 토대로 현재 프로젝트의 Docker 및 배포 설정을 점검하고, 필요에 맞게 수정하시길 바랍니다.