vite든, CRA든 번들링 도구를 사용하면 익숙하게 보게 될 package.json의 코드이다.
vite 실행 시 Vite 개발 서버를 로컬에서 실행하여 해당 브라우저에서 JS 파일을 요청하고, 우리는 그 결과물을 localhost 상의 포트에서 확인할 수 있다.
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"test": "jest",
"prettier": "prettier --write --config ./.prettierrc ./src"
},
그렇다면, 배포를 위해 build할 때는 어떻게 동작하는가? Vercel에 레포지토리를 연결하기만 해도 동작하는 이유가 무엇일까?
Vercel에서 배포할 때 찍히는 로그들을 분석해보자.
// 프로덕션 빌드가 시작
// 설정 파일(vite.config.js)을 읽어 빌드 프로세스를 초기화
vite v5.4.11 building for production...
// ESBuild를 사용해 소스 파일과 종속성을 빠르게 분석하고 변환.
// TypeScript, JSX, Vue/Svelte 등의 비표준 파일이 표준 JavaScript로 컴파일
transforming...
node_modules/lottie-web/build/player/lottie.js (17010:32): Use of eval in "node_modules/lottie-web/build/player/lottie.js" is strongly discouraged as it poses security risks and may cause issues with minification.
✓ 3202 modules transformed.
// Rollup을 사용해 코드 스플리팅과 번들링을 수행
// 코드를 여러 청크(chunk)로 분리. 번들링 결과는 dist/assets에 저장
rendering chunks...
// 번들 파일의 압축 전, 압축 후 크기를 계산해 표시
computing gzip size...
dist/index.html 0.95 kB │ gzip: 0.52 kB
dist/assets/index-CbGtfgdF.css 4.96 kB │ gzip: 1.26 kB
dist/assets/index-CGoU96VL.css 232.44 kB │ gzip: 31.00 kB
dist/assets/index--tBCjIru.js 0.83 kB │ gzip: 0.52 kB
dist/assets/index-C-ukZz5N.js 0.88 kB │ gzip: 0.55 kB
dist/assets/index-DRr-MHVL.js 1.25 kB │ gzip: 0.60 kB
dist/assets/index-c_RGvYKk.js 288.30 kB │ gzip: 81.85 kB
dist/assets/index-L_1_6mqk.js 446.61 kB │ gzip: 102.42 kB
dist/assets/index-Bvohxkgz.js 811.24 kB │ gzip: 226.20 kB
✓ built in 12.14s
// 일부 청크가 크다며 경고
(!) Some chunks are larger than 500 kB after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
// 빌드 완료, dist에 결과물 생성
Done in 19.49s.
Build Completed in /vercel/output [50s]
// 빌드된 파일을 Vercel 서버에 업로드
Deploying outputs...
Deployment completed
Uploading build cache [66.54 MB]...
Build cache uploaded: 881.425ms
Vercel을 쓰면 아주 편리하지만, 이런 호스팅 배포 사이트에 모든 서비스를 맡길 수는 없다. Google이나 Naver 까지가 아니더라도 일반적인 서비스라면 AWS 같은 유료 서비스, 정확히는 S3와 같은 서버를 하나 장만해서 배포를 하게 될 것이다.
그렇다면 배포를 할 때마다 vite build
명령을 해서 나온 폴더를 AWS 서버에 매번 옮겨줄 것인가? 그것은 너무나도 불편하고 비효율적이다. 게다가 개발할 때는 잘 되던 기능이 배포된 환경에서는 동작하지 않을 위험도 있다.
배포 과정과 실행 환경을 표준화할 수 있는 기술이 있다면 일관된 실행 환경을 보장할 수도 있고, 배포 과정 역시 자동화할 수 있다. 이를 가능케 하는 것이 Docker이다.
Docker는 애플리케이션을 실행하는 데 필요한 모든 환경(코드, 라이브러리, 설정 파일 등)을 하나의 패키지(컨테이너)로 묶어주는 컨테이너 기반 가상화 플랫폼이다.
docker 의 용도는 애플리케이션을 환경에 구애받지 않고 실행하기 위해서라고 할 수 있다. 윈도우와 맥 그리고 리눅스, 리눅스에서도 데비안 알파인... 명령어에서 부터 시작해서 별의 별것이 다르다. 하지만 도커는 이 모든걸 한번 작성해 놓으면 어디서든지 동일한 코드로 빌드할 수 있다.
이 컨테이너는 애플리케이션과 그에 필요한 라이브러리, 종속성들을 하나의 패키지로 묶어 다른 환경에서도 일관되게 동작하도록 보장해준다. 이로 인해 개발 환경과 실제 배포 환경 간의 차이로 인해 발생하는 문제를 줄일 수 있다.
그럼 가상 머신과 다를게 없지 않나? 하고 생각할 수 있을 것이다. 가상화 플랫폼을 제공한다는 것은 동일하니 말이다.
하지만 다르다. 명백히 다르다. Docker가 제공할 수 있는 장점들이 있다.
Docker는 여러 구성 요소로 이루어져 있으며, 이들 각각이 협력하여 컨테이너 기반 가상화 환경을 제공한다.
docker
명령어를 입력해 Docker 데몬에게 명령을 전달하고, 데몬은 이를 실행한다.docker run
, docker build
, docker pull
, docker push
등.dockerd
라는 프로세스를 통해 실행되며, Docker API 요청을 처리하고 컨테이너를 실행, 중지, 삭제하는 등의 작업을 수행한다.docker-compose.yml
)을 사용해 여러 서비스를 정의하고, 한 번에 실행할 수 있다.builder
):npm install
로 의존성을 설치하고 npm run build
로 빌드 파일을 생성.nginx
):build/
폴더)을 서빙.COPY --from=builder
를 통해 빌드 결과를 Nginx의 기본 경로로 복사.# 1. Node.js 이미지로 애플리케이션 빌드
FROM node:18 AS builder
# 2. 작업 디렉토리 설정
WORKDIR /app
# 3. 패키지 파일 복사 및 의존성 설치
COPY package.json package-lock.json ./
RUN npm install
# 4. 소스 코드 복사 및 빌드
COPY . .
RUN npm run build
# 5. Nginx를 사용해 정적 파일 서빙
FROM nginx:alpine
# 6. Nginx 설정 파일 덮어쓰기 (옵션)
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 7. React 빌드 결과를 Nginx의 기본 경로로 복사
COPY --from=builder /app/dist /usr/share/nginx/html
# 8. 컨테이너 포트 설정
EXPOSE 80
# 9. Nginx 시작
CMD ["nginx", "-g", "daemon off;"]
// server 블록 - 서버 설정을 정의합니다.
server {
// Nginx가 요청을 수신할 포트를 지정
listen 80;
// 이 서버 블록에서 처리할 요청의 도메인 지정
server_name localhost;
// 정적 파일(HTML, CSS, JS 등)이 저장된 루트 디렉터리를 설정
// 일반적으로 Nginx 컨테이너에서 기본적으로 사용하는 디렉터리
root /usr/share/nginx/html;
index index.html;
// 특정 URL 경로에 대해 별도의 동작을 정의.
// 기본 경로를 의미하며, 모든 요청이 이 블록에 매칭
location / {
try_files $uri /index.html;
}
}
일반적으로 여러 개의 컨테이너를 한 번에 관리할 때 사용되지만, 하나의 컨테이너 올릴 때도 compose로 동작하면 편리하다.
version: '3.8'
services:
react-app:
build:
context: .
ports:
- "3000:80"
컨테이너 상에서 nginx의 80 포트에 프로젝트의 build된 index.html이 잘 라우팅 되는 것을 확인할 수 있다.
3000포트와 연결해서 docker network 외부에서도 잘 동작하는 것을 확인할 수 있다.
레포지토리를 클론해서 docker compose 명령을 실행하면 윈도우 상에서 컨테이너를 돌렸을 때와 동일한 동작을 하는 것을 확인할 수 있다.
.env 파일이 .gitignore에 포함되어 따로 넣어주어야 한다는 점, vite 환경에서는 localhost:5173, 5174으로 포트를 열었기에 kakao API에서 도메인에 3000번 포트로 열려있어야 한다는 점에 유의하자.