본문 바로가기

Node.js 컨테이너 보안과 최적화: Docker 이미지 불필요한 의존성 제거

728x90

아래처럼 빌드 과정에서만 node_modules가 필요하고, 최종 실행은 다른 산출물만으로 가능한 경우입니다.

  • React / Vue / Next.js 일부 정적 빌드 결과물만 실행
  • TypeScript를 JS로 컴파일한 뒤, 런타임에는 컴파일된 결과만 실행
  • 번들러(Webpack, Vite, esbuild 등)로 의존성을 묶어서 배포

이 경우에는 보통 최종 이미지에 node_modules를 넣지 않거나, production dependency만 남기는 방식으로 줄입니다.

언제 제거하면 안 되나

아래 경우는 실행 시점에 node_modules가 필요합니다.

  • Node.js 서버가 require() / import로 패키지를 직접 사용
  • NestJS, Express, Fastify 같은 서버 앱
  • ORM, DB 드라이버, 템플릿 엔진, 로그 라이브러리 등을 런타임에 쓰는 경우

이때 node_modules를 지우면 컨테이너는 올라가도 앱이 바로 에러 납니다.
즉, “빌드용 의존성”과 “실행용 의존성”을 분리해야 합니다.

가장 권장하는 방식: 멀티 스테이지 빌드

보통은 이렇게 합니다.

# 1) build stage
FROM node:20-alpine AS builder
WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# 2) runtime stage
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production

COPY package*.json ./
RUN npm ci --omit=dev

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

CMD ["node", "dist/index.js"]

이 방식의 장점은 다음과 같습니다.

  • 빌드용 툴체인과 소스코드가 최종 이미지에 남지 않음
  • 최종 이미지가 작아짐
  • 보안상 공격면이 줄어듦
  • 캐시 효율이 좋아짐

node_modules를 그냥 지우는 것보다 더 좋은 방법

같은 stage에서 빌드 후 rm -rf node_modules를 하는 것도 가능하지만, 보통은 권장하지 않습니다.

  • 빌드 후 런타임에 필요한 패키지까지 같이 지워질 수 있음
  • Docker 레이어 특성상 “지웠다” 해도 중간 레이어에 흔적이 남음
  • 최종 이미지 최적화 효과가 멀티 스테이지보다 떨어짐

즉, 삭제보다 분리가 더 좋습니다.

이미지 최적화에 같이 쓰면 좋은 방법

.dockerignore

불필요한 파일이 빌드 컨텍스트에 들어가지 않게 합니다.

node_modules
dist
.git
Dockerfile
npm-debug.log

npm ci --omit=dev

운영 이미지에는 devDependencies를 제외합니다.

npm ci --omit=dev

Alpine 또는 slim 이미지 사용

  • node:20-alpine
  • node:20-slim
300x250

다만 native module이 많으면 Alpine(musl)에서 추가 빌드 이슈가 생길 수 있습니다.

npm prune --omit=dev

이미 설치된 상태에서 devDependencies만 제거할 때 사용합니다.

npm prune --omit=dev

판단 기준을 한 줄로 정리하면

  • 빌드 결과물만 실행하면 된다node_modules를 최종 이미지에서 제거 가능
  • Node 앱이 직접 실행되며 패키지를 import 한다node_modules는 남겨야 함
  • 가장 좋은 방법 → 멀티 스테이지 빌드 + production dependency만 설치

실무에서 많이 쓰는 예시

TypeScript 서버

  • builder에서 전체 설치
  • npm run build
  • runner에서 npm ci --omit=dev
  • dist만 복사

React/Vite 정적 웹

  • builder에서 npm ci 후 build
  • 최종 이미지는 nginx 같은 정적 서버만 사용
  • node_modules는 최종 이미지에 불필요
728x90
그리드형(광고전용)

댓글