
docker compose up을 실행했는데 API 컨테이너가 데이터베이스 연결 오류를 내고 재시작되는 경우가 있다. depends_on을 이미 적었는데도 문제가 생기면 컨테이너가 실행된 시점과 내부 서비스가 요청을 받을 준비를 마친 시점을 구분해야 한다.
Compose는 의존성 순서에 따라 컨테이너를 시작하지만, 짧은 문법의 depends_on만으로 데이터베이스 준비 완료까지 기다리지는 않는다. 이때 데이터베이스의 healthcheck와 condition: service_healthy를 함께 사용하면 API를 시작할 조건을 명확하게 만들 수 있다.

depends_on만으로 부족한 이유
데이터베이스 컨테이너 프로세스가 시작돼도 초기화, 복구, 테이블 준비 때문에 실제 연결을 받기까지 시간이 더 필요할 수 있다. 따라서 다음 두 상태는 같지 않다.
| 상태 | 의미 |
|---|---|
| 컨테이너가 실행 중 | 컨테이너의 주 프로세스가 시작됨 |
| 서비스가 준비됨 | 애플리케이션 요청을 정상 처리할 수 있음 |
짧은 문법은 생성 순서를 정하는 데는 유용하지만 준비 상태를 검사하지 않는다.
services:
api:
image: my-api:latest
depends_on:
- db
db:
image: postgres:18
이 구성에서는 db 컨테이너가 먼저 시작된다. 하지만 PostgreSQL이 연결을 받을 수 있는지는 별도로 확인하지 않으므로 API가 너무 빨리 접속하면 첫 요청이 실패할 수 있다.
healthcheck와 service_healthy 구성
PostgreSQL의 준비 상태를 pg_isready로 검사하고, API가 그 결과를 기다리게 만들면 다음과 같이 구성할 수 있다.
services:
db:
image: postgres:18
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
api:
image: my-api:latest
depends_on:
db:
condition: service_healthy
${POSTGRES_PASSWORD}는 Compose를 실행하는 환경에서 전달하고 실제 비밀번호를 YAML에 직접 적지 않는다. healthcheck.test 안의 $${POSTGRES_USER}처럼 달러 기호를 두 번 쓴 부분은 호스트의 Compose 보간을 피하고 컨테이너 안에서 환경변수를 사용하기 위한 표현이다.
healthcheck 옵션은 어떻게 볼까?
각 값은 무조건 짧게 만드는 것보다 서비스가 실제로 준비되는 시간에 맞춰야 한다.
| 옵션 | 역할 | 확인할 점 |
|---|---|---|
test |
준비 상태를 판정할 명령 | 성공 시 종료 코드 0을 반환하는지 |
interval |
검사 사이의 간격 | 너무 짧아 불필요한 부하를 만들지 않는지 |
timeout |
한 번의 검사 제한 시간 | 정상 응답 시간보다 지나치게 짧지 않은지 |
retries |
연속 실패 허용 횟수 | 초기화 지연을 감당할 수 있는지 |
start_period |
초기 준비 유예 시간 | 마이그레이션이나 복구 시간을 반영했는지 |
service_healthy는 healthcheck가 성공해야 의존 서비스의 생성·시작 단계로 넘어가게 한다. healthcheck 자체가 없으면 기다릴 기준도 없으므로 두 설정을 한 쌍으로 봐야 한다.
시작 순서와 재시작 정책은 다르다
여기서 자주 헷갈리는 부분은 depends_on이 애플리케이션의 모든 재연결 문제를 해결해 주는 것은 아니라는 점이다.
- 시작 시점의 순서는 Compose가 관리한다.
- 실행 중 데이터베이스 연결이 끊어졌을 때의 재시도는 애플리케이션이 처리해야 한다.
- 컨테이너가 비정상 종료됐을 때 다시 실행할지는 서비스 수준의
restart정책이 담당한다. depends_on아래의restart: true는 명시적인 Compose 작업으로 의존 서비스를 갱신하거나 재시작할 때 종속 서비스도 다시 시작하도록 만드는 별도 동작이다.
따라서 API에는 연결 재시도와 적절한 타임아웃을 남겨 두는 편이 안전하다. healthcheck는 시작 조건을 개선하지만 운영 중 장애 복구 로직을 대신하지 않는다.
설정이 적용됐는지 확인하기
먼저 Compose 파일의 문법과 변수 보간 결과를 확인한다.
POSTGRES_PASSWORD=example docker compose config
실행한 뒤에는 서비스 상태를 확인한다.
docker compose up -d
docker compose ps
docker compose ps에서 데이터베이스 상태가 healthy로 바뀐 뒤 API가 실행되는지 확인한다. 상태가 계속 starting 또는 unhealthy라면 API보다 healthcheck 명령을 먼저 점검해야 한다.
docker inspect --format='{{json .State.Health}}' <db-container-name>
이 글의 Compose 예시는 Docker Compose v5.3.0에서 docker compose config 검사를 통과했다. 현재 환경에서는 Docker 데몬 접근이 제한돼 실제 컨테이너 시작 순서까지 재현하지 않았으므로, 적용할 때는 사용 중인 이미지의 healthcheck 명령과 준비 시간을 함께 확인해야 한다.
결국 핵심은 depends_on을 적는 것보다 무엇을 준비 완료로 볼지 정의하는 데 있다. 데이터베이스의 실제 응답을 healthcheck로 확인하고 API가 service_healthy를 기다리게 하면 시작 직후 발생하는 연결 오류를 줄일 수 있다. 이미지 빌드 단계까지 정리하려면 도커 이미지 크기 줄이기를 이어서 볼 수 있으며, 세부 조건은 Docker Compose 시작 순서 공식 문서에서 확인할 수 있다.
'개발 > 트러블슈팅' 카테고리의 다른 글
| GitHub Actions AWS OIDC 사용법: 액세스 키 없이 배포 역할 연결하기 (0) | 2026.09.06 |
|---|---|
| Codex Skills 만드는 법: SKILL.md 구조와 적용 범위 (0) | 2026.09.03 |
| OpenAI Responses API 구조화 출력: JSON Schema로 응답 형태 고정하기 (0) | 2026.09.02 |
| Playwright 로그인 유지 방법: userDataDir과 storageState 차이 (0) | 2026.08.31 |
| Codex AGENTS.md 작성법: 프로젝트 규칙을 폴더별로 나누는 방법 (0) | 2026.08.29 |
댓글