infra
Docker Compose로 Node.js 개발 환경을 구성하는 방법 — 앱과 DB를 한 번에 올리는 방법
Node.js 앱과 PostgreSQL을 docker-compose.yml 하나로 묶어서 실행하면 팀원 누구나 동일한 개발 환경을 docker compose up 한 줄로 구성할 수 있다. 볼륨, 핫 리로드, 환경변수 설정까지 정리했다.

- ·docker-compose.yml: 여러 컨테이너 서비스를 하나의 파일로 선언적으로 관리
- ·depends_on: 서비스 시작 순서를 지정하지만 앱 준비 완료는 보장하지 않음
- ·볼륨 마운트: 소스 코드를 컨테이너와 공유해 핫 리로드 구현
- ·docker compose down -v: 컨테이너와 볼륨을 함께 삭제해 DB 초기화
팀에 합류한 신규 개발자가 로컬 환경을 셋업하는 데 반나절이 걸리는 걸 보고 Docker Compose로 개발 환경을 구성했다. 그 이후로는 docker compose up 한 줄이면 Node.js 앱과 DB가 동시에 뜨고, 코드 변경도 볼륨 마운트 덕분에 컨테이너를 재시작하지 않아도 바로 반영됐다. 처음에 depends_on만 설정하고 앱이 DB보다 먼저 시작되는 문제가 있었는데, healthcheck와 condition: service_healthy를 조합하면 DB가 실제로 준비된 후 앱이 시작되도록 만들 수 있다는 걸 알게 됐다.
Docker Compose란 무엇인가
Docker Compose가 Node.js 개발 환경 구성에 필요한 이유
Node.js 앱을 개발할 때 백엔드 서버 외에 데이터베이스, 캐시 서버, 메시지 큐 같은 의존 서비스들이 필요한 경우가 많다. 팀원마다 로컬에 PostgreSQL을 직접 설치하고 버전을 맞추고 데이터베이스를 생성하는 과정이 있으면 환경 셋업에 시간이 많이 걸리고 버전 불일치 문제도 생긴다. Docker Compose는 이 문제를 해결한다. docker-compose.yml 파일 하나에 Node.js 앱과 DB를 모두 선언해두면 팀원 누구나 docker compose up 명령 하나로 동일한 환경을 즉시 실행할 수 있다. 각 서비스는 독립된 컨테이너로 격리되기 때문에 호스트 시스템 환경에 영향받지 않는다. 개발 PC에 이미 다른 버전의 PostgreSQL이 설치되어 있어도 상관없다. Compose 파일 자체가 프로젝트 저장소에 포함되기 때문에 새로운 서비스가 추가되거나 버전이 바뀌면 파일을 업데이트하고 커밋하면 된다. 팀원들은 git pull 후 docker compose up --build 한 번으로 변경된 환경을 그대로 받을 수 있다. 직접 도입해보면 새 팀원의 온보딩 시간이 눈에 띄게 줄어드는 효과를 바로 체감할 수 있다.
docker-compose.yml로 Node.js 앱 설정하기
docker-compose.yml에 Node.js 앱 서비스를 정의하는 방법
docker-compose.yml은 services 키 아래에 각 컨테이너 서비스를 정의한다. Node.js 앱 서비스는 build, ports, volumes, environment 같은 항목으로 구성한다. build: . 는 현재 디렉토리의 Dockerfile을 기준으로 이미지를 빌드하라는 의미다. ports는 호스트 포트와 컨테이너 포트를 '3000:3000' 형태로 매핑한다. volumes에서 소스 코드 경로를 컨테이너 내부 경로와 연결하면 코드를 수정했을 때 컨테이너를 재시작하지 않아도 변경 사항이 컨테이너 안에 즉시 반영된다. 이 방식으로 nodemon과 조합하면 코드 변경 시 서버가 자동으로 재시작되는 핫 리로드 환경이 만들어진다. node_modules는 호스트와 공유하지 않도록 익명 볼륨으로 마운트해서 분리하는 게 좋다. 호스트의 node_modules와 컨테이너 안의 node_modules가 충돌하는 문제를 예방할 수 있다. environment 항목에서 컨테이너에 주입할 환경변수를 설정한다. DB 연결 문자열, 포트 번호 같은 값을 여기서 관리한다.
services:
app:
build: .
ports:
- '3000:3000'
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://user:password@db:5432/mydb
depends_on:
db:
condition: service_healthy
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=mydb
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U user']
interval: 5s
retries: 5
volumes:
postgres_data:Docker Compose에서 Node.js 앱과 PostgreSQL을 함께 실행하는 방법
docker-compose.yml에 두 서비스를 정의했으면 docker compose up 명령으로 모두 함께 실행한다. -d 옵션을 추가하면 백그라운드로 실행된다. 처음 실행할 때는 이미지 빌드와 PostgreSQL 이미지 다운로드가 함께 이루어지므로 시간이 좀 걸린다. 두 번째 실행부터는 캐시된 이미지를 사용해서 빠르게 시작된다. depends_on에 condition: service_healthy를 설정하면 PostgreSQL의 healthcheck가 통과된 이후에 Node.js 앱이 시작된다. healthcheck 없이 depends_on만 쓰면 컨테이너가 시작됐는지만 확인하고 앱을 띄우기 때문에 DB가 실제로 연결 가능한 상태가 되기 전에 앱이 먼저 뜨면서 연결 오류가 날 수 있다. docker compose logs -f로 모든 서비스의 로그를 실시간으로 볼 수 있고, docker compose logs -f app처럼 특정 서비스만 볼 수도 있다. 개발 중에 DB를 완전히 초기화하고 싶을 때는 docker compose down -v 명령을 쓴다. -v 옵션이 볼륨까지 삭제하기 때문에 DB 데이터가 모두 지워지고 다음 실행 시 빈 상태로 시작된다. 실수로 프로덕션 환경에 down -v를 실행하지 않도록 주의해야 한다.
Docker Compose 개발 환경 실용 설정
Docker Compose에서 Node.js 개발 환경에 볼륨과 핫 리로드를 설정하는 방법
개발 환경에서 가장 중요한 편의 기능은 코드를 수정하면 서버가 자동으로 재시작되는 핫 리로드다. Docker Compose에서 이를 구현하려면 두 가지가 필요하다. 첫 번째는 볼륨 마운트로 호스트의 소스 코드를 컨테이너 내부와 연결하는 것이다. .:/app 처럼 설정하면 호스트에서 파일을 수정하는 즉시 컨테이너 안에도 반영된다. 두 번째는 nodemon 같은 파일 변경 감지 도구를 개발 서버 시작 명령에 사용하는 것이다. Dockerfile이나 docker-compose.yml의 command에 nodemon app.js 또는 npm run dev를 설정하면 파일이 바뀔 때마다 서버가 재시작된다. Windows 환경에서는 파일 시스템 이벤트가 컨테이너로 전달되지 않아 nodemon의 --legacy-watch 옵션이 필요한 경우가 있다. 폴링 방식으로 파일 변경을 감지하기 때문에 약간의 CPU 사용이 있지만 개발 환경에서는 문제가 없다. node_modules는 반드시 익명 볼륨으로 분리해서 호스트 디렉토리가 컨테이너 안을 덮어쓰지 않도록 해야 한다. 이 설정을 빠뜨리면 컨테이너 안에서 설치한 npm 패키지가 사라지거나 플랫폼 차이로 네이티브 모듈이 동작하지 않는 문제가 생긴다.
Docker Compose 환경변수를 .env 파일로 관리하는 방법
docker-compose.yml에 환경변수를 직접 하드코딩하면 DB 비밀번호 같은 민감한 값이 Git에 올라갈 수 있다. Compose는 프로젝트 루트의 .env 파일을 자동으로 읽어서 파일 안의 변수를 docker-compose.yml에서 ${변수명} 형태로 참조할 수 있다. .env 파일에 POSTGRES_PASSWORD=mypassword를 정의하고 docker-compose.yml에서 POSTGRES_PASSWORD=${POSTGRES_PASSWORD}로 참조하면 된다. .env 파일은 .gitignore에 추가해서 저장소에 올라가지 않도록 하고, .env.example 파일에 어떤 변수가 필요한지 키만 남겨두면 팀원이 파일을 복사해서 값을 채울 수 있다. 서비스별로 다른 env 파일을 쓰고 싶다면 services의 env_file 옵션으로 경로를 지정할 수 있다. env_file: .env.db처럼 서비스마다 분리된 env 파일을 쓰면 설정이 명확하게 나뉜다. 개발, 스테이징, 프로덕션 환경을 분리하고 싶다면 docker-compose.override.yml 파일을 활용하는 방법도 있다. docker compose up을 실행하면 docker-compose.yml과 override 파일이 자동으로 합쳐져서 실행된다.
자주 묻는 질문
docker compose up 후 Node.js 앱이 DB에 연결하지 못하고 오류가 나는 이유가 무엇인가요?+
depends_on만 설정한 경우 컨테이너 시작 여부만 확인하고 DB가 실제로 연결 가능한 상태인지는 기다리지 않습니다. healthcheck와 condition: service_healthy를 함께 사용하면 PostgreSQL이 준비된 후 앱이 시작됩니다. 또는 앱 코드에서 DB 연결 재시도 로직을 추가하는 방법도 있습니다.
컨테이너 안에서 node_modules가 비어 있거나 모듈을 찾지 못하는 오류가 나면 어떻게 하나요?+
볼륨 마운트 설정에서 node_modules를 익명 볼륨으로 분리했는지 확인하세요. volumes에 - /app/node_modules 항목을 추가하면 호스트의 node_modules가 컨테이너 안을 덮어쓰지 않습니다. 이미 잘못된 상태라면 docker compose down --volumes 후 docker compose up --build로 재시작하면 됩니다.
docker compose up과 docker compose up --build의 차이는 무엇인가요?+
docker compose up은 기존 이미지가 있으면 그것을 사용합니다. --build를 추가하면 항상 이미지를 새로 빌드합니다. Dockerfile이나 package.json이 바뀌었다면 --build를 써야 변경 사항이 반영됩니다.
개발 환경 Compose 파일과 프로덕션 Compose 파일을 어떻게 분리하나요?+
docker-compose.yml에 공통 설정을 두고, docker-compose.dev.yml에 개발 전용 설정을 추가하는 방식이 일반적입니다. docker compose -f docker-compose.yml -f docker-compose.dev.yml up 형태로 실행하면 두 파일이 합쳐져서 적용됩니다.
관련 글
Docker로 Jenkins 설치하고 초기 설정까지 완료하는 방법
WAR 파일 설치 대신 Docker로 Jenkins를 올리면 Java 의존성 문제가 사라지고 버전 관리가 쉬워진다. 컨테이너 실행부터 볼륨 마운트, 초기 설정까지 순서대로 정리했다.
Nginx 리버스 프록시로 Node.js 앱을 도메인에 연결하는 방법
Node.js 앱을 직접 80 포트로 띄우는 대신 Nginx를 리버스 프록시로 앞에 세우면 도메인 연결과 HTTPS 적용이 깔끔해진다. 설정 파일 작성부터 Certbot SSL 적용까지 정리했다.
PM2로 Node.js 앱을 서버에서 관리하는 방법 — 프로세스 자동 재시작과 startup 설정
nohup으로 Node.js를 띄우다가 PM2로 전환하면 프로세스 자동 재시작, 서버 재부팅 후 자동 실행, 로그 관리까지 한 번에 해결된다. 설치부터 ecosystem.config.js 설정까지 정리했다.