한우의 개발일기

[Next.js]Nextjs 프로젝트 도커로 빌드하기 본문

NextJS

[Next.js]Nextjs 프로젝트 도커로 빌드하기

한우코딩 2024. 11. 11. 15:25

도커 적용하기

먼저 도커 홈페이지로 이동후 각각 OS 환경에 맞는 도커 데스크탑을 설치해준다. 편한 개발환경을 위해 vscode 익스텐션도 설치해주자!!

설치를 한뒤 프로젝트 파일로 이동한 뒤

루트디랙토리에 Dokerfile, doker-compose.yml 파일을 생성해준다
.dokerignore 파일도 만들어주면 좋다!!(뒤에서 설명할 예정)

# Dockerfile
FROM node:18-alpine AS base

# Dependencies
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app

COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile

# Builder
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

RUN yarn build

# Runner
FROM base AS runner
WORKDIR /app

ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public

COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT 3000
ENV HOSTNAME "0.0.0.0"

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

현재 나는 yarn 패키지 매니저를 이용중이므로 yarn을 이용했다

현재 나는 멀티스테이지 빌드 방식을 이용하고 있다

멀티스테이지 빌드란?

멀티 스테이지 빌드는 하나의 Dockerfile 안에서 여러 단계(stage)로 나누어 빌드하는 방식입니다.

주요목적은

  • 최종 이미지 크기 감소
  • 보안 강화
  • 빌드 프로세스 최적화
  1. base 스테이지
FROM node:18-alpine AS base
  • 기본 이미지를 정의
  • 다른 스테이지들의 베이스로 사용됨
  1. deps 스테이지
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
  • 의존성 패키지들을 설치하는 단계
  • package.json과 yarn.lock만 복사해서 의존성 설치
  • 캐시 레이어를 효율적으로 사용하기 위함
  1. builder 스테이지
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn build
  • 실제 애플리케이션을 빌드하는 단계
  • deps 스테이지에서 설치된 node_modules를 가져옴
  • 소스 코드를 복사하고 빌드 실행
  1. runner 스테이지 (최종 스테이지)
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
...
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
  • 실제 운영환경에서 실행될 최종 이미지
  • 필요한 파일만 builder 스테이지에서 가져옴
  • 보안 설정 (nextjs 사용자 추가)
  • 실행 환경 설정

멀티스테이지 빌드의 장점

  1. 이미지 크기 최소화

    • 빌드 도구와 중간 파일들이 최종 이미지에 포함되지 않음
    • 필요한 파일만 최종 스테이지에 복사
  2. 보안 강화

    • 빌드 도구와 소스 코드가 최종 이미지에 노출되지 않음
    • 취약점 노출 가능성 감소
  3. 캐시 최적화

    • 각 스테이지별로 캐시가 따로 동작
    • 소스 코드가 변경되어도 의존성 설치 단계는 캐시 사용
  4. 빌드 속도 향상

    • 변경된 레이어만 다시 빌드
    • 의존성이 변경되지 않으면 해당 레이어는 캐시 사용

이러한 방식은 특히 Next.js와 같은 프론트엔드 애플리케이션에서 매우 효율적이라고 한다!

여기서 .dockerignore 파일에 넣어줄 수 있는 파일들이 생기는데

node_modules
.next
.git
*.md
.env*

이렇게 있다 위 파일들을 넣어주는 이유는

  1. node_modules
  • 이유:
    • 로컬의 node_modules는 도커 빌드에 필요하지 않음
    • Dockerfile 내에서 yarn install로 새로 설치됨
    • OS나 환경에 따라 다르게 컴파일된 의존성이 있을 수 있음
  • 포함시키면 생기는 문제:
    • 빌드 시간 증가
    • 이미지 크기 증가
    • 호환성 문제 발생 가능
  1. .next
  • 이유:
    • 로컬에서 빌드된 결과물
    • Dockerfile 내에서 yarn build로 새로 빌드됨
    • 환경별로 다르게 빌드되어야 함
  • 포함시키면 생기는 문제:
    • 개발환경과 운영환경의 빌드가 섞일 수 있음
    • 불필요한 파일 복사로 빌드 시간 증가
  1. .git
  • 이유:
    • 버전 관리 정보는 도커 이미지에 불필요
    • 이미지 크기만 키움
  • 포함시키면 생기는 문제:
    • 이미지 크기 증가
    • 보안 위험 (git 히스토리 노출)
  1. *.md (README.md 등)
  • 이유:
    • 문서 파일은 애플리케이션 실행에 불필요
    • 개발자를 위한 문서일 뿐
  • 포함시키면 생기는 문제:
    • 불필요한 파일로 이미지 크기 증가
  1. .env*
  • 이유:
    • 환경변수 파일은 환경별로 다르게 관리되어야 함
    • 보안 정보 포함 가능성
    • Docker의 환경변수 주입 기능 사용 권장
  • 포함시키면 생기는 문제:
    • 보안 위험 (민감한 정보 노출)
    • 환경별 설정 관리가 어려워짐

결론적으로

  1. 성능 최적화

    • 불필요한 파일 제외로 빌드 속도 향상
    • 이미지 크기 최소화
  2. 보안

    • 민감한 정보 제외
    • 불필요한 정보 노출 방지
  3. 환경 독립성

    • 각 환경에 맞는 빌드와 설정 사용
    • 환경 간 충돌 방지

이러한 설정으로 더 효율적이고 안전한 도커 이미지를 만들 수 있다고 한다

nextjs 프로젝트는 평균적으로 5~10분정도 빌드하는데 걸린다고 한다..

이제

# 도커 컴포즈 버전 명시
version: "3"

# 실행할 서비스들을 정의
services:
  # 'web'이라는 서비스 정의
  web:
    # 빌드 설정
    build:
      # 빌드 컨텍스트 (Dockerfile이 있는 위치)
      context: .
      # 사용할 Dockerfile 지정
      dockerfile: Dockerfile

    # 포트 매핑 (호스트:컨테이너)
    ports:
      - "3000:3000"

    # 환경 변수 설정
    environment:
      - NODE_ENV=production

    # 컨테이너 재시작 정책
    restart: always

이런식으로 설정을해주고

docker build -t projectname . 를 해주면 빌드가 시작된다.

빌드가 완료 되면 도커 데스크탑 혹은 cli 명령어인 docker images를 입력하면 빌드된 이미지가 보일것이다.

다음에는 빌드한 이미지를 aws 를 통해 배포하는것 까지 해보겠습니다