Hot Reloading이란 개발자가 코드를 변경하였을 때 해당 변경사항이 실시간으로 반영되어 앱을 재시작할 필요없이 앱에 반영되게 하는 기술입니다.
Docker에서 Hot Reloading하는 법은 간단합니다.
컨테이너가 Hot Reloading될 수 있도록 변경사항을 감지해서 반영해주게 하고 컨테이너에 로컬 변경사항을 바로 반영하고 싶은 부분을 바인드 마운트를 합니다.
여기서는 실제로 React앱을 컨테이너 환경에서 개발하면서 로컬 변경사항이 바로 컨테이너에 반영되고 컨테이너의 변경사항이 바로 앱에 반영되게 만들 것입니다.
개발 편의를 위해서는 node.js버전을 로컬과 컨테이너를 같게 만들어주면 좋습니다.
먼저 로컬 nodejs버전을 확인해봅시다.

저는 nodejs 22.8.0버전을 쓰고 있습니다.
그리고 dockerhub에서 22.8.0버전을 지원하는지 확인해 봅시다.

22.8.0을 지원합니다.
이대로 사용하면 됩니다.
이제 vite를 이용해 리액트 샘플을 얻어봅시다.
아래 명령어를 통해 기본 react 템플릿을 가져옵니다.
npm create vite@latest
그리고 실행해 봅시다.

기본적으로 vite로 가져온 React템플릿은 Hot Reloading을 해주는 HMR을 가지고 있습니다.
실제로 로컬 파일이 변경되면 바로 반영됩니다.
하지만 이 상태로 바로 컨테이너에 바인드 마운트를 하면 로컬 파일 변경사항을 반영하지 않습니다.
그렇기 때문에 추가적인 설정을 해줘야 합니다.
vite.config파일을 아래와 같이 바꿉시다.
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
server: {
watch: {
usePolling: true, // 폴링 방식으로 변경 감지
},
},
})
usePolling: true는 주기적으로 파일의 변경사항을 찾아내고 반영하는 방식입니다.
컨테이너에서는 로컬에서 데이터를 변경하고 저장한다 하여도 이를 감지하지 못하기 때문에 Polling방식으로 주기적으로 변경사항을 확인하게 만드는 것입니다.
그리고 vite의 경우 기본적으로 localhost에서만 접속하게 해주기 때문에 외부에서 접속하게 하고 싶다면 package.json파일을 조금 수정 해야합니다.
아래와 같습니다.
"scripts": {
"dev": "vite --host", #바뀐 부분
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
}
vite --host를 통해 외부에서 접근할 수 있게 호스트 하게 만들어줍니다.
이제 컨테이너를 만들고 바인드 마운트를 하면 컨테이너에서 Hot Reloading이 되어 컨테이너 환경에서 개발하면서 로컬 변경사항이 반영되게 만들 수 있습니다.
컨테이너를 만들기 전에 .dockerignore파일에 무시할 파일을 명시해둡시다.
node_modules
dist
build
.env
tests
__tests__
.vscode
.idea
.git
.gitignore
*.log
npm-cache
이제 컨테이너를 만들기 위해 dockerfile을 만들고 이미지를 만듭시다.
FROM node:22.8.0
WORKDIR /usr/src/app
COPY package*.json ./
RUN --mount=type=cache,target=/usr/src/app/.npm \
npm set cache /usr/src/app/.npm && \
npm install
COPY . .
EXPOSE 5173
CMD ["npm", "run", "dev"]
docker build -t my-node-app .
이제 해당 이미지를 바인드 마운트를 통해 핫 리로딩 합시다.
docker run -d -p 5173:5173 `
--mount type=bind,source="$(pwd)\.",target=/usr/src/app `
--mount type=volume,source=node_modules,target=/usr/src/app/node_modules `
my-node-app
로컬에서 react 개발 파일과 폴더를 모두 바인드 마운트를 통해 동기화하였습니다.
그리고 node_module은 익명 볼륨으로 마운트하였는데 이유는 node_module이 로컬과 동기화 되었을때 컨테이너의 호환성과 맞지 않는 패키지가 node_module에 있어 문제가 생기는 것을 막기 위해서 입니다.
아래는 실행결과입니다.

로컬에서 파일을 수정해보겠습니다.
바로 반영됩니다.

위에서 node_module에서 로컬과 동기화되어서 충돌이 날 수 있기 때문에 따로 익명 볼륨으로 마운트를 하였는데 이렇게 되었을 경우 어떻게 패키지를 추가해야 할지 고민될 수 있습니다.
이때는 아래와 같이 컨테이너의 터미널에 들어가 컨테이너에서 패키지를 설치하고 동기화된 package.json파일을 이용해 로컬에서도 설치해주면 됩니다.
docker exec -it <container_id> /bin/sh
npm install <package_name>

이렇게 컨테이너 내에서 패키지를 설치함으로써 컨테이너에 호환되는 패키지가 설치되며 동기화된 package.json을 이용해 로컬에서 패키지를 설치하면 개발을 계속 이어갈 수 있습니다.
아래는 로컬에서 동기화된 package.json과 터미널을 통해 패키지를 설치하는 것을 보여주는 것입니다.
