컬리 딜리버리프로덕트팀이 서버가 분리된 환경에서 서버 간 제약조건 불일치로 생기는 버그를 잡기 위해, TestContainers로 실제 QA와 비슷한 통합테스트를 만든 경험을 다룬다. Kafka·PostgreSQL·Spring 애플리케이션 컨테이너를 하나의 시나리오로 묶어 유저 스토리를 관통 검증한다.
핵심 포인트- 서버별로 관심사만 나눠 mock으로 테스트하면, 조회 서버에만 추가된 제약(배송 가능 지역)이 수집 서버와 어긋나는 버그를 QA 단계에서야 발견한다.
- TestContainers는 테스트 코드에서 도커 컨테이너를 띄워, 도커만 있으면 어디서나 동일한 통합 환경을 재현한다.
- 시나리오에 이벤트 브로커(Kafka)·수집 서버·주문 DB(Postgres)·조회 서버 4개 컨테이너가 필요하다.
- 컨테이너를 공통 Network와 alias로 묶어 내부 통신하고, application은 환경변수로 연결 대상을 런타임 주입한다.
- 배포 이미지 생성 workflow 완료를 트리거로 GitHub Actions에서 통합테스트를 자동 실행한다.
상세 정리- 문제 상황: 고객이 주문한 주문건을 배송관리서비스에서 확인한다는 스토리에서, 조회 쪽에 배송 가능 지역 제약을 추가하면 서버별 mock 테스트는 모두 통과하지만 실제로는 일부 주문이 안 보이는 버그가 QA에서야 드러난다.
- 해법 방향: 서버 구분 없이 하나로 합친 통합테스트라면 조회 제약 변경 시 테스트가 깨져 조기 발견이 가능해, TestContainers로 실제와 유사한 자동화 테스트를 만들기로 했다.
- 환경 구성: testcontainers-postgresql·kafka·junit-jupiter 의존성을 추가하고 멀티프로젝트에 모듈로 붙였다. 테스트 코드는 Kotlin으로 작성했다.
- 컨테이너 정의: KafkaContainer(cp-kafka), PostgisContainerProvider(DB명·유저·포트), Spring 앱은 GenericContainer로 Container Registry에서 이미지를 pull했고, withImagePullPolicy(alwaysPull)로 코드 수정 시 최신 이미지를 쓰게 했다.
- 네트워크: 전역 Network를 만들고 각 컨테이너에 withNetwork와 withNetworkAliases로 alias를 부여해 컨테이너끼리 alias로 통신하게 했다. 앱은 application-local.yml에서 POSTGRES_HOST·KAFKA_HOST를 환경변수로 받아 런타임 연결한다.
- 삽질 지점: 내부 연결 실패 시 Cannot assign requested address 로그가 남고, 연결돼도 인증 401이 떠서 local 프로필에서 magic-token으로 인증을 우회했다. 테스트에서 앱 호출은 getFirstMappedPort로 포워딩된 포트를 받아 접근한다.
- 시나리오 검증: given으로 KafkaProducer가 주문 메시지를 발행하고, when으로 조회 API를 호출하며, then으로 orderNo 일치를 assert해 Kafka 발행부터 조회까지 관통 확인한다.
- CI 연동: GitHub Actions에서 workflow_run(배포 이미지 생성 workflow completed)을 트리거로 통합테스트를 실행하고 결과를 리포팅받는다.
- 의의: 기본 시나리오를 자동화하면 QA는 비정상 케이스에 집중할 수 있고, DB 등 인프라 버전 변경 시 영향도 파악도 수월해진다.
왜 읽나Spring/Kotlin 백엔드에서 Kafka·DB를 포함한 실전 통합테스트를 구축하려는 개발자에게 TestContainers 네트워크 구성과 인증·포트 함정을 보여주는 실전 가이드.