Forge Fate
백엔드·인프라

Docker Compose 서비스 간 API 연동, 안전하게 검증하는 7단계

약 7분

근거와 검증 범위 — 공식 문서 기반 검증 절차

Docker·HTTP 공식 자료를 바탕으로 연동 확인 순서와 예시 명령을 정리합니다. 특정 스택의 버전·요청 응답·실행 로그를 첨부한 검증 완료 보고서가 아니므로 실제 환경에서 각 단계를 확인해야 합니다.

컨테이너가 모두 running이라고 해서 API 연동까지 성공한 것은 아니다. 주소와 포트, 준비 상태, 인증, 읽기 계약, 쓰기 후 재조회, 테스트 데이터 삭제를 각각 확인해야 한다. Compose 역시 의존 컨테이너의 실행은 관리할 수 있지만, 애플리케이션이 요청을 처리할 준비까지 자동으로 보장하지는 않는다.[S4]

1. 호출 위치에 맞는 주소를 선택한다

가장 먼저 요청을 보내는 주체가 호스트인지 컨테이너인지 구분한다.

같은 Compose 네트워크에 있는 컨테이너끼리는 서비스 이름과 컨테이너 내부 포트를 사용한다.[S1]

text
http://api:8080

반면 호스트에서 컨테이너로 요청할 때는 공개된 호스트 포트를 사용한다.[S1]

text
http://localhost:18080

예를 들어 18080:8080은 호스트의 18080을 컨테이너의 8080에 연결한다. 다른 컨테이너가 api를 호출할 때는 api:8080을 사용하며, api:18080이나 localhost:18080을 사용하지 않는다.[S1]

동적으로 할당됐거나 충돌 가능성이 있는 호스트 포트는 다음과 같이 확인한다.[S1]

bash
docker compose port api 8080

network_mode: host, 외부 네트워크, 프록시 또는 서로 다른 Compose 프로젝트가 개입한다면 이 기본 규칙만으로 판단하지 말고 실제 네트워크 구성을 별도로 확인해야 한다.[S1]

2. host.docker.internal의 사용 조건을 확인한다

host.docker.internal은 같은 Compose 네트워크의 서비스끼리 통신할 때 쓰는 주소가 아니다. 컨테이너에서 호스트에서 실행 중인 프로세스에 접근할 때 고려한다.

Docker Desktop에서는 이 이름이 호스트의 내부 IP로 해석된다.[S2] 일반 Docker Engine에서는 자동으로 제공되지 않을 수 있으므로 host-gateway 매핑이 필요할 수 있다.[S3]

yaml
services:  worker:    extra_hosts:      - "host.docker.internal:host-gateway"

이 설정은 사용 중인 Engine과 Compose 버전에서 직접 검증해야 한다. 원격 Docker daemon을 사용한다면 여기서 말하는 호스트는 개발자 PC가 아니라 daemon이 실행되는 시스템일 수도 있다.[S3]

API 요청을 보내기 전에 호출 컨테이너 내부에서 이름 해석과 대상 포트 접속부터 확인한다.[S2][S3]

3. 실행 상태와 준비 상태를 분리한다

먼저 최종 Compose 구성과 현재 상태를 살핀다.

bash
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 헤더를 사용하고 예제에는 변수만 표시한다.

bash
curl \  -H "Authorization: Bearer ${TOKEN}" \  -H "Accept: application/json" \  "http://api:8080/v1/resources/example"

셸 추적이 활성화돼 있거나 실행 명령이 기록되는 환경이라면 위 방식도 토큰을 노출할 수 있으므로 기록 정책을 먼저 확인한다.

가능한 Linux 컨테이너 환경에서는 Compose secret을 서비스별로 허용하고 /run/secrets/<secret_name> 파일에서 읽게 할 수 있다.[S6]

yaml
services:  worker:    secrets:      - api_tokensecrets:  api_token:    file: ./secrets/api_token.txt

Compose secrets는 노출 위험을 줄이지만 애플리케이션의 요청 로그, 오류 출력, 디버그 모드나 셸 기록까지 자동으로 보호하지는 않는다.[S6][S7] 또한 로컬 Compose 네트워크 자체를 전송 암호화나 강한 격리의 보장으로 간주해서는 안 된다.

5. 쓰기 전에 읽기 계약을 검증한다

첫 API 검사는 GET으로 수행한다. GET은 HTTP 의미상 안전 메서드지만, 구현 과정의 로깅이나 표준을 벗어난 상태 변경 같은 부수 효과까지 없다고 보장하지는 않는다.[S5] 따라서 GET 우선은 위험을 낮추는 순서이지 무부작용 보장이 아니다.

다음 항목을 개별적으로 확인한다.

  • 기대한 상태 코드
  • 예상한 Content-Type
  • 리소스 식별자와 필수 응답 필드
  • 인증 실패와 리소스 부재의 구분
  • 오류 응답이 API 계약과 일치하는지 여부

단일 200 OK만 확인하지 말고 헤더와 본문의 최소 계약까지 검사한다. 이 단계를 통과해야 주소, 인증 및 읽기 경로가 적어도 기대한 수준으로 동작한다고 판단할 수 있다.[S5]

6. 전용 slug로 쓰기·재조회·삭제를 검증한다

기존 데이터와 혼동되지 않는 테스트 전용 slug를 만든다.

text
verify-<UTC시각>-<난수>

이는 표준 요구사항이 아니라 충돌과 오삭제 가능성을 낮추기 위한 실무 권고다. 실제 slug 제약과 중복 처리 방식은 API 명세에서 확인한다.

검증 순서는 다음과 같다.

  1. 전용 slug로 리소스를 한 번 생성한다.
  2. 응답 상태와 생성된 식별자를 확인한다.
  3. 같은 slug를 GET으로 다시 조회한다.
  4. 저장된 값이 쓰기 요청 및 읽기 계약과 일치하는지 비교한다.
  5. API가 공식적으로 제공하는 삭제 또는 복구 절차를 실행한다.
  6. 다시 조회해 리소스 부재를 확인한다.

이 순서로 DNS·TCP 연결, HTTP 인증, 읽기 스키마, 쓰기 후 저장 결과, 삭제를 서로 다른 검사로 분리할 수 있다. 생성 코드나 삭제 후 기대 상태는 API마다 다를 수 있으므로 임의로 201이나 404로 고정하지 않는다.

7. API 데이터와 Compose 스택을 차례로 정리한다

외부 데이터 저장소에 생성된 레코드는 docker compose down만으로 삭제되지 않을 수 있다.[S8] 따라서 테스트 리소스를 API로 먼저 삭제하고 부재까지 확인한다.

격리된 테스트 스택이 더 필요 없다면 다음 명령으로 서비스 컨테이너와 Compose 네트워크를 정리한다.[S8]

bash
docker compose down

--volumes는 선언된 named volume과 연결된 anonymous volume까지 제거할 수 있으므로, 해당 데이터가 폐기 가능한 테스트 전용 데이터임을 확인한 경우에만 사용한다.[S8]

bash
docker compose down --volumes

Bind 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

오류 제보·의견 보내기

글 제목과 주소가 포함된 메일 초안을 엽니다. 내용과 받는 사람을 확인한 뒤 보내주세요.

받는 사람: [email protected]

메일 앱에서 작성

메일 앱이 열리지 않으면 아래 내용을 복사해 평소 쓰는 이메일에 붙여 넣으세요.

문의 안내