SW/Kubernetes, Docker

React 앱 Docker 프로덕션 배포: Multi-Stage Build 완벽 가이드

얇은생각 2025. 6. 28. 05:25
반응형

Local Hack에서 Production Hero로 – Docker로 React 프런트엔드 배포까지 공감 100% 가이드

React 앱을 개발용 Docker 컨테이너와 프로덕션용 컨테이너로 분리해야 하는 이유와 구체적인 방법을 한국어로 친절히 풀어냈습니다. Multi‑stage build, Nginx static serve, 실전 체크리스트까지 총정리!

 

Dockerizing React 프런트엔드, 두근두근 실전 가이드

 

Dockerizing React 프런트엔드, 두근두근 실전 가이드

1. 프롤로그 – 코드도, 사람도 살아있다

npm start를 누르는 순간, 화면이 깜박이며 가능성의 세계가 열리죠. Hot reload가 마치 친근한 드래곤처럼 숨 쉬고, 브라우저는 JSX를 마법처럼 렌더링합니다. 하지만 팀원이 묻습니다. “이제 배포할까?” 그때부터 미세한 공포가 엄습하죠.

  • “같은 이미지 그대로 프로덕션에 써도 괜찮을까?”
  • “왜 우리 VPS가 RAM을 과자 먹듯 씹어먹지…?”
  • “port 3000 그대로 열리고, 실시간 rebuild가 유저 앞에서 막… 이거 레알?”

 

새벽 2시에 식은땀 흘려본 적 있다면, 이 글은 당신을 위해 준비했습니다. Dev 컨테이너의 포근함에서 Production 컨테이너의 전장까지, 고생 없이 건너갈 수 있도록 감성 + 실전 팁 + 복붙 가능한 코드까지 풀코스로 준비했어요.

 


 

2. Development ≠ Production, 그래서 아름답다

 

2. Development ≠ Production, 그래서 아름답다

개발 단계에서 우리가 원하는 세 가지 슈퍼파워:

  1. 즉각 피드백 – 저장만 하면 화면이 새로고침.
  2. 친절한 에러 – minify 전, 딱 IDE로 링크되는 스택트레이스.
  3. 무마찰 – 번거로운 빌드 따위 X, 상상→코드 바로 실행.

 

npm start는 이 모든 걸 선물하죠. react-scripts dev server가:

  • JSX, ESNext 코드를 on‑the‑fly로 컴파일
  • Hot reload 주입
  • 메모리에서 파일 서빙 (속도 최우선)

 

하지만 프로덕션에선? 이 축복이 저주로 변합니다. 실시간 컴파일이 CPU를 폭식, source map이 민감 정보 누출, 번들 사이즈 뚱뚱. 유저가 느려터진 첫 화면에 짜증내기 전에 우린 다르게 가야 합니다.

 


 

3. 브라우저가 이해하는 언어 – 왜 우리는 컴파일할까?

 

3. 브라우저가 이해하는 언어 – 왜 우리는 컴파일할까?

소스에는 HTML 같은 JSX랑 최신 자바스크립트(예: optional chaining, top‑level await)가 섞여 있죠. 구형 브라우저… 심지어 최신 크롬도 JSX를 그대로는 못 씹어 먹어요. 그래서 Babel + webpack이 뒤에서:

  • 트리쉐이킹(tree‑shaking)
  • Minify
  • Code splitting

 

요걸 dev 시간엔 즉석 조리. 하지만 트래픽 받으면서 계속 요리하면 서버 과열은 뻔하죠.

 


 

4. Docker에겐 두 개의 성격이 있다

 

4. Docker에겐 두 개의 성격이 있다

Docker 이미지를 컨테이너 야적장이라고 생각해봅시다. 한 컨테이너엔 낡은 붓, 스케치 중 캔버스, LO‑FI 비트가 울려 퍼집니다—dev 컨테이너죠. 다른 컨테이너엔 작품이 액자에 곱게 포장되어 목록화—prod 컨테이너.

 

4‑1. Development Dockerfile 예시

# dev stage
FROM node:20-bookworm
WORKDIR /app
COPY package*.json ./
RUN npm install --legacy-peer-deps
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

 

Why it rocks in dev

  • Node 풀패키지로 react-scripts OK
  • Hot reload, source map, 친절한 오류
  • port 3000 → 브라우저 직결

 

4‑2. Production Dockerfile (Multi‑Stage) 예시

# 1) build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
RUN npm run build

# 2) serve stage
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

 

포인트

  • Alpine 이미지는 다이어트 성공 ➜ 용량↓ 속도↑
  • npm run build 결과물만 /app/build에
  • 최종 이미지엔 Node가 아예 없음 → attack surface 최소화

 


 

5. Multi‑Stage Build가 필요한 세 가지 이유

 

5. Multi‑Stage Build가 필요한 세 가지 이유

  1. Security – 소스/비밀 키 없는 클린 이미지
  2. Efficiency – 1 GB → 50 MB 미만
  3. Speed – 배포/롤링 업데이트 time saving

 


 

6. 실전 썰 – Hot Reload가 서버를 녹인 날

 

6. 실전 썰 – Hot Reload가 서버를 녹인 날

배포 5분 만에 CPU 400 %… Chrome DevTools 확인 → 모든 셀션이 아직도 webpack watch 돌고 있더라구요. Production Dockerfile로 다시 빌드 후 배포했더니 CPU 3 %, First Paint 6 s → 1.2 s!

같은 삽질, 다들 합니다. 중요한 건 깨닫고 고치는 거죠 😊

 


 

7. 배포 체크리스트 ✅

  1. Dockerfile.dev, Dockerfile.prod 두 개 마련
  2. docker-compose.yml dev용 볼륨 매핑 + port 3000
  3. CI/CD 스텝
    • Test 돌리기
    • docker build -f Dockerfile.prod -t myapp:$(git rev-parse --short HEAD) .
    • Registry push
  4. 클러스터 배포(Kubernetes, ECS…)
  5. Lighthouse로 퍼포먼스 모니터링

 


 

8. 자주 묻는 질문

Q. Next.js로 넘어가면 prod에도 Node 필요?
A. 서버사이드 렌더링 쓴다면 yes. Pure static export라면 여전히 Nginx OK.

Q. Express로 build 결과물 서빙 가능?
A. 가능하지만 gzip/brotli, 캐싱 헤더를 직접 세팅해야 속도 면에선 Nginx보다 불리.

Q. 환경 변수는?
A. 빌드 타임(REACT_APP_*)은 front‑end에 bake‑in, 런타임 시크릿은 Nginx/Express 레벨에서 주입.

 


 

9. 에필로그 – Joy를 배포하라

코드 배포는 기술 이상의 약속입니다. Dev와 Prod를 분리하면 사용자에게 빠른 경험을, 팀에게 평화를 선물할 수 있어요.

  • Dev 컨테이너 → 창의력 폭발, 개발자 행복 😊
  • Prod 컨테이너 → 압축된 자바스크립트, 유저 행복 😍

이제 두 컨테이너 전략으로 당당히 배포해보세요. 미래의 당신이, 그리고 당신의 사용자들이 박수 칠 겁니다.

반응형