NestJS 핫 리로드가 Docker에서 작동하지 않는 이유와 올바른 해결 방법
I’m happy to translate the article for you, but I don’t have access to the content at the linked URL. Could you please paste the text you’d like translated here? Once you provide the article’s content, I’ll translate it into Korean while preserving the original formatting, markdown syntax, and code blocks.
핵심 오해
Docker는 파일을 감시하지 않습니다. 컨테이너 내부에 존재하는 것만 볼 수 있습니다. 변경 사항이 반영되는지는 전적으로 다음에 달려 있습니다:
- 파일이 컨테이너에 어떻게 들어가는지
- NestJS가 어떻게 시작되는지
- 파일 시스템 이벤트가 올바르게 전파되는지
개발 모드 기대와 프로덕션 모드 설정을 혼합하면 핫 리로드가 작동하지 않게 됩니다.
Docker 없이 NestJS 핫 리로드가 작동하는 방식
NestJS를 로컬에서 실행할 때:
npm run start:dev
Nest CLI는 파일 감시자를 사용합니다. 파일이 변경되면 프로세스가 자동으로 재시작됩니다.
- Webpack이 사용되지 않습니다.
- HMR(Hot Module Replacement)이 없습니다.
- 단순히 프로세스가 재시작되며, 네이티브 파일 시스템 이벤트에 의해 동작합니다.
Docker를 도입하면 무엇이 바뀔까
컨테이너 내부에서 파일 감시는 세 가지에 따라 달라집니다:
- 파일 주입 – 소스 코드를 마운트하고 있나요, 아니면 스냅샷을 복사하고 있나요?
- 시작 명령 – Nest를 워치 모드로 실행하고 있나요?
- 이벤트 전파 – 파일 시스템 이벤트가 컨테이너에 도달하고 있나요?
이 중 하나라도 잘못 설정되면 핫 리로드가 깨집니다.
개발에 신뢰할 수 있는 유일한 설정
다음 모든 조건을 만족해야 합니다:
- 소스 코드를 볼륨으로 마운트
- NestJS를 워치 모드(
start:dev)로 실행 - Docker 내부에서 파일 감시가 정상 작동
볼륨은 절대 타협할 수 없습니다
코드를 단순히 복사하는 Dockerfile:
COPY . .
은 정적인 스냅샷을 만들며—프로덕션에는 적합하지만 개발에는 사용할 수 없습니다. Docker는 로컬 편집을 전혀 감지하지 못하기 때문입니다.
대신 프로젝트 디렉터리를 마운트하는 docker‑compose.yml(또는 docker run)을 사용하세요:
services:
app:
build: .
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules # 호스트 node_modules 제외
.:/usr/src/app 라인이 핫 리로드의 핵심입니다.
node_modules 제외
호스트의 node_modules를 마운트하면 네이티브 모듈 불일치가 발생할 수 있습니다. 위와 같이 제외하면 컨테이너가 자체 의존성을 사용하게 됩니다.
Nest를 워치 모드로 실행
npm run start:dev # ✅ 올바른 방법
# ❌ 잘못된 대안:
node dist/main.js
nest start
컴파일된 JavaScript를 실행하면 워처가 비활성화되어 핫 리로드가 불가능합니다.
macOS 및 Windows 파일‑watching 문제
Docker Desktop은 Linux 컨테이너를 VM 안에서 실행합니다. 기본 파일 시스템 이벤트가 macOS/Windows 호스트에서 컨테이너로 전파되지 않는 경우가 많아 NestJS가 변경 사항을 놓칩니다.
해결 방법: 폴링 활성화
Node‑기반 감시자가 이벤트에 의존하지 않고 변경 사항을 폴링하도록 다음 환경 변수를 설정합니다:
CHOKIDAR_USEPOLLING=true
WATCHPACK_POLLING=true
docker‑compose.yml(또는 Dockerfile)의 environment: 아래에 추가하세요. 폴링은 널리 알려진 신뢰할 수 있는 우회 방법입니다.
Webpack 혼란 설명
NestJS는 두 가지 재로드 전략을 지원합니다:
- 프로세스 재시작 – 기본 Nest CLI 워처(
start:dev사용 시). - 핫 모듈 교체(HMR) – Webpack이 필요합니다.
Webpack은 HMR을 명시적으로 활성화한 경우에만 필요합니다. HMR이 활성화되었음을 나타내는 지표:
// main.ts
if (module.hot) {
module.hot.accept();
}
// nest-cli.json
{
"compilerOptions": { ... },
"webpack": true // <-- enables HMR
}
HMR을 사용하려고 하지 않았다면 Webpack이 전혀 필요 없습니다. Docker 재로드 문제를 “해결”하기 위해 설치하는 것은 보통 헛된 시도이며, 누락된 부분은 위에서 설명한 폴링 설정인 경우가 많습니다.
Webpack HMR이 의미가 있을 때
- 애플리케이션 시작 시간이 오래 걸릴 때.
- Node 프로세스를 재시작하지 않고 즉시 재로드하고 싶을 때.
- Nest 설정에서 명시적으로 HMR을 활성화했을 때.
대부분의 백엔드 API에서는 파일 변경 시 간단한 프로세스 재시작만으로 충분하며 훨씬 덜 복잡합니다.
개발 vs. 프로덕션 사고 모델
| 항목 | 개발 | 프로덕션 |
|---|---|---|
| 볼륨 | 호스트‑컨테이너 마운트 사용 | 볼륨 없음; 빌드 시 파일 복사 |
| 시작 명령 | npm run start:dev (워치 모드) | 컴파일된 JS 실행 (node dist/main.js) |
| 파일 감시 | macOS/Windows 환경에서는 폴링 활성화 | 필요 없음 |
| 재빌드 | 코드 변경 시 이미지를 재빌드하지 않음 | 변경 시마다 이미지 재빌드 |
| Webpack | 선택 사항, HMR 전용 | 필요 없음 |
이 두 환경을 혼합하면(예: 프로덕션에서 볼륨을 사용하거나 모든 변경마다 이미지를 재빌드하는 경우) 워크플로가 깨집니다.
Final thoughts
- Docker isn’t broken.
- NestJS isn’t broken.
- Hot reload isn’t magic; it’s just file‑watching.
Understanding how files enter the container and how NestJS watches them makes the behavior predictable. If you find yourself rebuilding Docker images just to see a console‑log update, revisit the volume, start‑command, and polling settings. And if you installed Webpack solely to make file watching work, you probably didn’t need it.
If this article saved you time, it will likely save someone else hours of frustration too.