[BE-31] (후속·테스트 인프라) Testcontainers 공유 싱글턴 컨테이너 전환

상태: 후속 개선 (test-infra). 기능 변경 없음. BE-20·BE-21·수정 B 진행 중 반복 발견된 전체 스위트 간헐 실패의 근본 원인 해소.

배경 — 발견 경위

./gradlew test(전체 스위트)가 공유 개발 머신에서 간헐적으로 실패합니다. 실패 형태는 항상 동일:

Kotest > initializationError FAILED
  ContainerLaunchException → CJCommunicationsException → EOFException
  at ...RecruitmentE2EScenarioTest.<clinit>(...)
1 test completed, 1 failed   ← 테스트 실패 0, discovery 단계 컨테이너 기동 실패 1
  • 테스트 코드 결함이 아닙니다. 개별/범위 지정(--tests) 실행은 100% 통과합니다. 전체 실행에서만 실패합니다.
  • 원인: 27개 통합 스펙이 각자 MySQLContainer를 선언(공유 base 클래스 없음)하고, Kotest discovery가 전체 spec 클래스를 로드하며 companion object <clinit>를 트리거해 27개 컨테이너가 discovery 단계에 동시 기동됩니다. 이 머신에 다른 프로젝트 상시 스택(11일·2주 된 mysql 컨테이너 등 26개)이 떠 있어, 버스트로 Docker daemon이 과부하되며 그중 하나가 EOF로 실패 → discovery 전체 중단.

측정: grep -rln MySQLContainer src/test = 27파일, 공유 base/추상 클래스 0건, .start() 계열 64건.

문제

한 JVM에서 통합 테스트마다 독립 컨테이너를 띄우는 구조라, 스펙 수에 비례해 동시 컨테이너가 늘어납니다. 현재 27개 → 향후 스펙이 늘면 더 악화. 공유 CI/개발 머신에서 머지 게이트(전체 ./gradlew test)를 신뢰할 수 없게 만듭니다.

해결책 — Testcontainers 싱글턴 컨테이너 패턴

  • 컨테이너를 JVM당 1개만 띄우고 전 스펙이 재사용합니다(Testcontainers 공식 “Singleton containers” 패턴). 27개 → 1개.
  • 구현: object SharedMySqlContainer(Kotlin object)가 MySQLContainer를 한 번 start()하고, 전 통합 스펙이 이 객체의 JDBC URL을 참조. Spring Boot 스펙은 @DynamicPropertySource(또는 공통 설정)로 데이터소스를 이 컨테이너에 바인딩.
  • 스펙 간 데이터 격리는 컨테이너 재기동이 아니라 트랜잭션 롤백 또는 테이블 truncate(afterEach/afterSpec)로 처리 — Flyway 스키마는 1회만 적용.
  • 대안: withReuse(true) + ~/.testcontainers.propertiestestcontainers.reuse.enable=true — 로컬 반복 실행 가속(단 CI에서는 싱글턴만으로 충분).

범위·주의

  • 27개 스펙의 컨테이너 선언 제거 + 공유 객체 참조로 교체 + 데이터소스 배선 통합. 스펙별 셋업(BehaviorSpec/DescribeSpec, @SpringBootTest 유무, raw JDBC)이 달라 배선 방식 확인 필요.
  • 검증 자체가 전체 스위트 신뢰성 회복 — 전환 후 ./gradlew test가 컨테이너 1개로 안정 통과하는지가 완료 기준(exit 0). 전환 전엔 이 스위트가 간헐 실패하므로, 전환이 곧 자기 검증.
  • 기능·프로덕션 코드 무변경 — 테스트 인프라만. 각 스펙의 테스트 의미(검증 대상)는 보존.

참고

  • BE-21 검증 문서: docs/ops/be-21-bootstrap-verification.md (전체 스위트 간헐 실패 관측)
  • 수정 B(PR #49) — 변경 모듈 테스트는 exit 0였으나 전체 스위트는 이 이슈로 환경 블록됨(그 PR과 무관)
  • Testcontainers Singleton containers 공식 문서 패턴