10편에서 배포 컨테이너를 살펴봤다면, 이번에는 개발용 컨테이너에서 소스 코드를 어떻게 다루는지 알아보겠습니다. Docker에서는 같은 디렉터리를 공유하더라도 "어떤 데이터는 호스트와 공유"하고 "어떤 데이터는 컨테이너 안에만" 두는 방식으로 나눌 수 있습니다. 바인드 마운트, 이름 있는(named) 볼륨, 익명(anonymous) 볼륨의 차이를 이해하면 이런 설계를 직접 할 수 있습니다.
이 글의 흐름
- 바인드 마운트·이름 있는 볼륨·익명 볼륨 개념 잡기
- 실습: Node.js 개발 컨테이너에서 세 가지를 함께 쓰기
node_modules를 분리해야 하는 대표 패턴- 문제를 예방하는 팁과 볼륨 관리법
- 실전에서 바로 써먹는 볼륨 전략 정리
읽기 카드
- 예상 소요 시간: 15분
- 사전 준비: Docker Compose 기초, npm 사용 경험
- 읽고 나면: 바인드 마운트와 익명 볼륨을 언제 분리해야 하는지 설명할 수 있습니다.
세 가지 볼륨 한눈에 보기
- 바인드 마운트(bind mount): 호스트의 특정 폴더를 컨테이너 경로에 그대로 연결합니다. 예)
.:/app - 이름 있는 볼륨(named volume):
docker volume create mydata처럼 이름을 붙여 Docker가 관리하는 데이터 공간을 씁니다. Compose에서는volumes: [mydata:/var/lib/mysql]처럼 참조합니다. - 익명 볼륨(anonymous volume): 이름을 지정하지 않고 경로만 적습니다. Compose가 임시 이름을 만들어 준 뒤 컨테이너가 사라지면 함께 정리하거나 직접 삭제할 수 있습니다. 예)
/app/node_modules
어떤 데이터를 어디에 둘지는 목적에 따라 다릅니다.
- 코드는 편집기에서 바로 수정해야 하므로 바인드 마운트.
- 데이터베이스처럼 지속되어야 하는 값은 이름 있는 볼륨.
node_modules처럼 OS별 차이가 크고 삭제해도 다시 설치할 수 있는 값은 익명 볼륨.
실습: Node.js 개발 컨테이너
아래 Compose 파일을 직접 실행해 보세요.
services:
dev:
image: node:20-alpine
working_dir: /app
volumes:
- .:/app # 바인드 마운트
- node-cache:/root/.npm # 이름 있는 볼륨
- /app/node_modules # 익명 볼륨
ports:
- "4321:4321"
command: sh -c "npm install && npm run dev"
volumes:
node-cache:
.:/app덕분에 로컬 코드가 컨테이너에 즉시 반영됩니다.node-cache는 npm 캐시를 저장해 다음 설치를 빠르게 합니다./app/node_modules는 익명 볼륨이라 컨테이너를 지우면 함께 사라져 깨끗한 상태로 다시 설치할 수 있습니다. 팀에 따라서는 익명 볼륨 대신 이름 있는 볼륨을 써서 더 눈에 잘 보이게 관리하기도 합니다.
docker compose up dev로 띄운 뒤 파일을 수정하면 브라우저에서 바로 반영되는지 확인해 보세요. docker volume ls를 보면 node-cache처럼 이름 있는 볼륨과 dev_app-node_modules 같은 익명 볼륨이 함께 생성된 것을 확인할 수 있습니다.
왜 node_modules를 분리해야 할까?
Node.js 프로젝트는 의존성 폴더에 OS별로 다른 바이너리가 들어 있습니다. 호스트와 컨테이너가 리눅스/맥/윈도처럼 섞이면 권한이나 파일 포맷 충돌이 자주 일어납니다. 대표적인 문제는 다음과 같습니다.
- macOS에서 생성된
node_modules가 리눅스 컨테이너에서 실행될 때EINVAL: invalid argument같은 오류 발생 - Windows 파일 경로 구분자가 하드코딩된 패키지가 컨테이너에서 깨짐
node-gyp가 빌드한 바이너리가 OS마다 달라 재설치가 필요함
그래서 실무에서는 보통 이렇게 나눕니다.
- 소스 폴더: 바인드 마운트로 호스트와 공유
node_modules: 익명 볼륨으로 컨테이너 내부에 생성- npm 캐시: 이름 있는 볼륨으로 저장해 재설치 속도 개선
익명 볼륨 대신 이름 있는 볼륨을 쓰고 싶다면 이렇게 바꿀 수도 있습니다.
services:
dev:
volumes:
- .:/app
- node-cache:/root/.npm
- node-modules:/app/node_modules
volumes:
node-cache:
node-modules:
이 방식은 node-modules라는 이름이 눈에 보여서 팀이 함께 관리하기 쉽다는 장점이 있습니다. 반대로 "컨테이너를 지우면 의존성도 같이 날려서 늘 깨끗하게 다시 설치하고 싶다"면 익명 볼륨이 더 단순할 수 있습니다.
필요할 때는 docker compose rm -f dev && docker volume prune으로 익명 볼륨을 정리하고 새로운 의존성을 설치하면 됩니다.
문제를 예방하는 팁
- 퍼미션 오류: 호스트 UID/GID와 컨테이너가 다르면 파일 소유권이 꼬입니다.
chown -R $(id -u):$(id -g) .를 실행하거나user: "1000:1000"같은 옵션으로 맞춰 주세요. - 볼륨 정리: 익명 볼륨은 이름이 없으니
docker volume ls에서 긴 해시처럼 보입니다. 주기적으로docker volume prune이나docker volume rm <ID>로 정리하세요. - 핫 리로드 속도: macOS에서 대형 프로젝트를 바인드 마운트하면 느릴 수 있습니다.
./src,./package.json등 필요한 경로만 선택적으로 마운트하거나, WSL2/리눅스 환경에서 실행하면 더 빠릅니다. Docker Desktop 환경에서는 마운트 성능 옵션을 추가로 조정하는 경우도 있습니다. - 팀 프로젝트: 모든 팀원이 같은 Compose 파일을 쓰면 npm 버전과 Node 버전을 자동으로 맞출 수 있습니다.
실전에서 바로 써먹는 볼륨 전략 정리
개발용 컨테이너에서는 보통 아래 규칙으로 시작하면 안전합니다.
- 소스 코드: 바인드 마운트
- 데이터베이스 데이터: 이름 있는 볼륨
node_modules같은 OS 의존 폴더: 익명 볼륨 또는 별도 이름 있는 볼륨
이 세 가지만 구분해도 대부분의 개발용 Compose 파일을 안정적으로 설계할 수 있습니다. 다음 12편에서는 이런 개발·운영 구성이 어디까지 커버 가능한지, 그다음에 어떤 인프라를 배워야 하는지 정리하며 시리즈를 마무리합니다.
💬 댓글
이 글에 대한 의견을 남겨주세요