Docker Compose 서비스 간 API 연동, 안전하게 검증하는 7단계
근거와 검증 범위 — 공식 문서 기반 검증 절차
Docker·HTTP 공식 자료를 바탕으로 연동 확인 순서와 예시 명령을 정리합니다. 특정 스택의 버전·요청 응답·실행 로그를 첨부한 검증 완료 보고서가 아니므로 실제 환경에서 각 단계를 확인해야 합니다.
컨테이너가 모두 running이라고 해서 API 연동까지 성공한 것은 아니다. 주소와 포트, 준비 상태, 인증, 읽기 계약, 쓰기 후 재조회, 테스트 데이터 삭제를 각각 확인해야 한다. Compose 역시 의존 컨테이너의 실행은 관리할 수 있지만, 애플리케이션이 요청을 처리할 준비까지 자동으로 보장하지는 않는다.[S4]
1. 호출 위치에 맞는 주소를 선택한다
가장 먼저 요청을 보내는 주체가 호스트인지 컨테이너인지 구분한다.
같은 Compose 네트워크에 있는 컨테이너끼리는 서비스 이름과 컨테이너 내부 포트를 사용한다.[S1]
http://api:8080반면 호스트에서 컨테이너로 요청할 때는 공개된 호스트 포트를 사용한다.[S1]
http://localhost:18080예를 들어 18080:8080은 호스트의 18080을 컨테이너의 8080에 연결한다. 다른 컨테이너가 api를 호출할 때는 api:8080을 사용하며, api:18080이나 localhost:18080을 사용하지 않는다.[S1]
동적으로 할당됐거나 충돌 가능성이 있는 호스트 포트는 다음과 같이 확인한다.[S1]
docker compose port api 8080network_mode: host, 외부 네트워크, 프록시 또는 서로 다른 Compose 프로젝트가 개입한다면 이 기본 규칙만으로 판단하지 말고 실제 네트워크 구성을 별도로 확인해야 한다.[S1]
2. host.docker.internal의 사용 조건을 확인한다
host.docker.internal은 같은 Compose 네트워크의 서비스끼리 통신할 때 쓰는 주소가 아니다. 컨테이너에서 호스트에서 실행 중인 프로세스에 접근할 때 고려한다.
Docker Desktop에서는 이 이름이 호스트의 내부 IP로 해석된다.[S2] 일반 Docker Engine에서는 자동으로 제공되지 않을 수 있으므로 host-gateway 매핑이 필요할 수 있다.[S3]
services: worker: extra_hosts: - "host.docker.internal:host-gateway"이 설정은 사용 중인 Engine과 Compose 버전에서 직접 검증해야 한다. 원격 Docker daemon을 사용한다면 여기서 말하는 호스트는 개발자 PC가 아니라 daemon이 실행되는 시스템일 수도 있다.[S3]
API 요청을 보내기 전에 호출 컨테이너 내부에서 이름 해석과 대상 포트 접속부터 확인한다.[S2][S3]
3. 실행 상태와 준비 상태를 분리한다
먼저 최종 Compose 구성과 현재 상태를 살핀다.
docker compose configdocker compose ps그다음 healthcheck와 depends_on.condition: service_healthy를 사용하거나, 호출 컨테이너 안에서 준비 상태 엔드포인트를 직접 확인한다. service_healthy 조건을 사용하면 Compose가 의존 서비스의 healthcheck 통과를 기다릴 수 있다.[S4]
다만 healthcheck나 TCP 연결 성공은 다음 항목을 입증하지 않는다.
- 올바른 토큰이 적용됐는가
- 기대한 HTTP 상태 코드가 반환되는가
- 응답 스키마가 계약과 일치하는가
- 쓰기와 삭제가 정상 동작하는가
실제 경로와 성공 상태 코드, 필수 JSON 필드, 오류 형식 및 토큰 scope는 대상 API 명세를 기준으로 정해야 한다.
4. 토큰을 노출하지 않는다
Bearer 토큰은 그 자체를 가진 주체가 사용할 수 있으므로 원문 보호가 중요하다.[S7] 토큰을 URL, Compose YAML, 명령 출력, 로그, 화면 캡처 또는 문서에 넣지 않는다.
요청에는 Authorization 헤더를 사용하고 예제에는 변수만 표시한다.
curl \ -H "Authorization: Bearer ${TOKEN}" \ -H "Accept: application/json" \ "http://api:8080/v1/resources/example"셸 추적이 활성화돼 있거나 실행 명령이 기록되는 환경이라면 위 방식도 토큰을 노출할 수 있으므로 기록 정책을 먼저 확인한다.
가능한 Linux 컨테이너 환경에서는 Compose secret을 서비스별로 허용하고 /run/secrets/<secret_name> 파일에서 읽게 할 수 있다.[S6]
services: worker: secrets: - api_tokensecrets: api_token: file: ./secrets/api_token.txtCompose secrets는 노출 위험을 줄이지만 애플리케이션의 요청 로그, 오류 출력, 디버그 모드나 셸 기록까지 자동으로 보호하지는 않는다.[S6][S7] 또한 로컬 Compose 네트워크 자체를 전송 암호화나 강한 격리의 보장으로 간주해서는 안 된다.
5. 쓰기 전에 읽기 계약을 검증한다
첫 API 검사는 GET으로 수행한다. GET은 HTTP 의미상 안전 메서드지만, 구현 과정의 로깅이나 표준을 벗어난 상태 변경 같은 부수 효과까지 없다고 보장하지는 않는다.[S5] 따라서 GET 우선은 위험을 낮추는 순서이지 무부작용 보장이 아니다.
다음 항목을 개별적으로 확인한다.
- 기대한 상태 코드
- 예상한
Content-Type - 리소스 식별자와 필수 응답 필드
- 인증 실패와 리소스 부재의 구분
- 오류 응답이 API 계약과 일치하는지 여부
단일 200 OK만 확인하지 말고 헤더와 본문의 최소 계약까지 검사한다. 이 단계를 통과해야 주소, 인증 및 읽기 경로가 적어도 기대한 수준으로 동작한다고 판단할 수 있다.[S5]
6. 전용 slug로 쓰기·재조회·삭제를 검증한다
기존 데이터와 혼동되지 않는 테스트 전용 slug를 만든다.
verify-<UTC시각>-<난수>이는 표준 요구사항이 아니라 충돌과 오삭제 가능성을 낮추기 위한 실무 권고다. 실제 slug 제약과 중복 처리 방식은 API 명세에서 확인한다.
검증 순서는 다음과 같다.
- 전용 slug로 리소스를 한 번 생성한다.
- 응답 상태와 생성된 식별자를 확인한다.
- 같은 slug를 GET으로 다시 조회한다.
- 저장된 값이 쓰기 요청 및 읽기 계약과 일치하는지 비교한다.
- API가 공식적으로 제공하는 삭제 또는 복구 절차를 실행한다.
- 다시 조회해 리소스 부재를 확인한다.
이 순서로 DNS·TCP 연결, HTTP 인증, 읽기 스키마, 쓰기 후 저장 결과, 삭제를 서로 다른 검사로 분리할 수 있다. 생성 코드나 삭제 후 기대 상태는 API마다 다를 수 있으므로 임의로 201이나 404로 고정하지 않는다.
7. API 데이터와 Compose 스택을 차례로 정리한다
외부 데이터 저장소에 생성된 레코드는 docker compose down만으로 삭제되지 않을 수 있다.[S8] 따라서 테스트 리소스를 API로 먼저 삭제하고 부재까지 확인한다.
격리된 테스트 스택이 더 필요 없다면 다음 명령으로 서비스 컨테이너와 Compose 네트워크를 정리한다.[S8]
docker compose down--volumes는 선언된 named volume과 연결된 anonymous volume까지 제거할 수 있으므로, 해당 데이터가 폐기 가능한 테스트 전용 데이터임을 확인한 경우에만 사용한다.[S8]
docker compose down --volumesBind mount, external volume 및 외부 데이터 저장소는 별도의 정리 대상이다.[S8]
최종 실행 체크리스트
- 호출 주체가 호스트인지 컨테이너인지 구분했다.
- 컨테이너 간 주소에
서비스명:컨테이너포트를 사용했다.[S1] - 호스트 포트 충돌이나 동적 매핑을
docker compose port로 확인했다.[S1] -
host.docker.internal이 필요한 상황인지 판단하고 이름 해석을 확인했다.[S2][S3] - healthcheck 또는 준비 상태 응답을 확인했다.[S4]
- 토큰 원문을 URL, 출력, 로그, YAML 및 문서에 노출하지 않았다.[S6][S7]
- GET으로 인증, 상태 코드,
Content-Type과 필수 필드를 먼저 검증했다.[S5] -
verify-<UTC시각>-<난수>형식의 전용 slug로 생성했다. - 같은 slug를 재조회해 저장 결과를 비교했다.
- 생성한 리소스를 API로 삭제하고 부재를 확인했다.
-
docker compose down을 실행했다.[S8] -
--volumes는 데이터 폐기 의도가 명확할 때만 사용했다.[S8]
Sources
- [S1] Networking in Compose | Docker, Inc. | 페이지에 게시·수정일이 표시되지 않음 | https://docs.docker.com/compose/how-tos/networking/ ↩
- [S2] Explore networking how-tos on Docker Desktop | Docker, Inc. | 페이지에 게시·수정일이 표시되지 않음 | https://docs.docker.com/desktop/features/networking/networking-how-tos/ ↩
- [S3] dockerd | Docker, Inc. | 페이지에 게시·수정일이 표시되지 않음 | https://docs.docker.com/reference/cli/dockerd/ ↩
- [S4] Control startup and shutdown order in Compose | Docker, Inc. | 페이지에 게시·수정일이 표시되지 않음 | https://docs.docker.com/compose/how-tos/startup-order/ ↩
- [S5] RFC 9110: HTTP Semantics | IETF; R. Fielding, M. Nottingham, J. Reschke | 2022-06 | https://www.rfc-editor.org/rfc/rfc9110.html ↩
- [S6] Manage secrets securely in Docker Compose | Docker, Inc. | 페이지에 게시·수정일이 표시되지 않음 | https://docs.docker.com/compose/how-tos/use-secrets/ ↩
- [S7] RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage | IETF; M. Jones, D. Hardt | 2012-10 | https://www.rfc-editor.org/info/rfc6750/ ↩
- [S8] docker compose down | Docker, Inc. | 페이지에 게시·수정일이 표시되지 않음 | https://docs.docker.com/reference/cli/docker/compose/down/ ↩
오류 제보·의견 보내기
글 제목과 주소가 포함된 메일 초안을 엽니다. 내용과 받는 사람을 확인한 뒤 보내주세요.
받는 사람: [email protected]
메일 앱에서 작성메일 앱이 열리지 않으면 아래 내용을 복사해 평소 쓰는 이메일에 붙여 넣으세요.
관련 글
백엔드·인프라 초급에서 중급으로 성장하는 Spring 백엔드 개발자 실전 로드맵
Java와 Spring 핵심 원리부터 REST API, 데이터베이스, 테스트, 보안과 운영까지 학습 순서를 제안합니다. 기간 대신 설명·구현·테스트·진단 역량으로 성장 단계를 점검합니다.
백엔드·인프라 Spring Boot 4.1 주요 변경점: gRPC·Jackson·HTTP 주소 필터링
Spring Boot 4.1의 gRPC 지원, Jackson 읽기·쓰기 설정, HTTP 요청 주소 필터링을 살펴보고, 업그레이드 전 확인할 Maven 테스트 AOT 설정 변경을 정리한다.