FROM node:24-alpine AS build-stage WORKDIR /app # 호스트의 npm 11 이 작성한 package-lock.json 형식과 호환되도록 npm 을 11 로 고정 # (베이스 이미지의 번들 npm 이 바뀌어도 lock 형식이 어긋나지 않게 한다. 전역 설치는 root 권한이 필요하므로 USER 전환 전에 수행) RUN npm install -g npm@11 # 개발 환경(docker-compose.dev.yml)은 이 스테이지에서 vite dev 서버를 구동하므로 비-root 로 전환한다. # /app 소유권을 넘겨야 익명 볼륨(node_modules)이 node 소유로 생성되어 vite 캐시(.vite) 쓰기가 가능하다. RUN chown node:node /app USER node COPY --chown=node:node package*.json ./ RUN npm ci # 호스트의 node_modules·dist·.env 는 .dockerignore 로 제외된다 COPY --chown=node:node . . ARG BUILD_MODE=production # 브라우저가 호출할 API 경로 — 기본은 같은 도메인의 /api (리버스 프록시가 백엔드로 넘긴다). # compose 가 루트 BACKEND_API_URL 을 넘긴다. API 를 별도 도메인에 둘 때만 절대 URL 이 들어온다. ARG VITE_API_BASE_URL=/api ENV VITE_API_BASE_URL=$VITE_API_BASE_URL RUN npm run build-only -- --mode ${BUILD_MODE} # 비-root 전용 nginx 이미지 (보안 규칙: 컨테이너 root 실행 금지) # 1024 미만 포트는 바인드할 수 없으므로 8080 을 사용한다 — nginx.conf 의 listen 과 일치해야 한다. FROM nginxinc/nginx-unprivileged:stable-alpine AS production-stage # 보안 헤더 포함된 nginx 설정 적용 COPY nginx.conf /etc/nginx/conf.d/default.conf COPY --from=build-stage /app/dist /usr/share/nginx/html # 이미지 기본 사용자(nginx, uid 101)로 구동 — root 로 되돌리지 말 것 USER nginx EXPOSE 8080 CMD ["nginx", "-g", "daemon off;"]