자바 백엔드 입문 54편 — Testcontainers 실제 DB 통합 테스트

2026-05-17자바 백엔드 입문

자바 백엔드 입문 54편. H2 인메모리 대신 진짜 PostgreSQL을 Docker로 띄워 통합 테스트하는 Testcontainers 표준 패턴 풀어쓴 학습 노트.

📚 자바 백엔드 입문 · 54편 — Testcontainers 실제 DB 통합 테스트

이 글은 자바 백엔드 입문 시리즈 59편 중 54편이에요. 52편 @SpringBootTest 에서 H2 인메모리 DB로 테스트했죠. 이번 54편은 그 한계를 넘는 Testcontainers — 진짜 PostgreSQL·Redis·Kafka를 Docker로 띄워 테스트.

H2 인메모리의 한계

H2는 빠르고 가벼워요. 다만 "운영과 다르다" 는 결정적 단점.

  • 방언 차이 — PostgreSQL의 JSONB·Array 타입, MySQL의 FULLTEXT INDEX 같은 DB 고유 기능이 H2엔 없음
  • 함수 차이DATE_TRUNC·STRING_AGG 같은 함수 동작 다름
  • 테스트는 통과, 운영은 실패 — H2에서 OK인 SQL이 PostgreSQL에서 깨짐

해결책 = Testcontainers. "테스트할 때마다 진짜 PostgreSQL을 Docker로 띄움".

Testcontainers란

Testcontainers = "Docker 컨테이너를 자바 테스트 안에서 자동으로 띄우고 종료하는 라이브러리". PostgreSQL·MySQL·Redis·Kafka·Elasticsearch 등 거의 모든 인프라 지원.

[테스트 시작]
  ↓
[Testcontainers가 Docker로 PostgreSQL 띄움]
  ↓
[Spring Boot가 그 PostgreSQL에 연결]
  ↓
[테스트 실행 — 진짜 PostgreSQL 사용]
  ↓
[테스트 종료 — Docker 컨테이너 자동 정리]

전제 조건 = 개발 PC·CI 환경에 Docker 설치. 거의 모든 회사 환경에 있어요.

의존성 추가

testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:postgresql'
testImplementation 'org.springframework.boot:spring-boot-testcontainers'   // Spring Boot 3.1+

첫 테스트 — PostgreSQL

@SpringBootTest
@Testcontainers
class OrderRepositoryTest {

    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16")
            .withDatabaseName("testdb")
            .withUsername("test")
            .withPassword("test");

    @DynamicPropertySource
    static void overrideProps(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }

    @Autowired
    private OrderRepository orderRepository;

    @Test
    void 주문_저장() {
        Order saved = orderRepository.save(new Order(...));
        assertThat(orderRepository.findById(saved.getId())).isPresent();
    }
}

3단계: 1. @Container 로 PostgreSQL 컨테이너 정의 2. @DynamicPropertySource 로 Spring datasource 설정 동적 변경 3. 평범한 @SpringBootTest 테스트

테스트 시작 시 — Testcontainers가 Docker로 PostgreSQL 16 띄움. 컨테이너 준비되면 Spring이 그곳에 연결. 테스트 끝나면 컨테이너 자동 정리.

Spring Boot 3.1+ — @ServiceConnection (간편)

Spring Boot 3.1부터 더 간편한 @ServiceConnection.

@SpringBootTest
@Testcontainers
class OrderRepositoryTest {

    @Container
    @ServiceConnection                        // ← 한 줄
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");

    @Autowired
    private OrderRepository orderRepository;

    @Test
    void 주문_저장() { ... }
}

@ServiceConnection 한 줄이 @DynamicPropertySource 보일러플레이트 대체. PostgreSQL뿐 아니라 Redis·Kafka·MongoDB도 자동 연결.

여러 컨테이너 — Redis + PostgreSQL

@SpringBootTest
@Testcontainers
class FullIntegrationTest {

    @Container @ServiceConnection
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");

    @Container @ServiceConnection
    static GenericContainer<?> redis = new GenericContainer<>("redis:7-alpine")
            .withExposedPorts(6379);

    @Container @ServiceConnection
    static KafkaContainer kafka = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:7.4.0"));

    // 진짜 PostgreSQL + Redis + Kafka 다 띄우고 통합 테스트
}

회사 운영 환경과 거의 똑같은 상태로 테스트 가능.

컨테이너 재사용 — 빠른 테스트

매 테스트마다 컨테이너 새로 띄우면 느려요. 재사용 설정.

@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16")
        .withReuse(true);                              // ← 컨테이너 재사용

~/.testcontainers.propertiestestcontainers.reuse.enable=true 박으면 — 같은 컨테이너를 여러 테스트에서 재사용. 첫 테스트만 컨테이너 시동 1~2초, 다음부터 즉시.

@DataJpaTest + Testcontainers

가벼운 JPA 슬라이스 테스트에도 적용.

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)   // ← H2 자동 교체 끄기
@Testcontainers
class OrderRepositoryJpaTest {

    @Container @ServiceConnection
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");

    @Autowired private OrderRepository orderRepository;
    @Autowired private TestEntityManager em;

    @Test
    void 상태별_조회() { ... }
}

@DataJpaTest 가 H2 자동 교체를 기본으로 하니까 — @AutoConfigureTestDatabase(replace = NONE) 로 명시적 끄기.

Spring Boot 3.1+ Devtools 모드

개발 중에도 Testcontainers 활용. main 메서드 만들기.

@TestConfiguration(proxyBeanMethods = false)
public class TestcontainersConfig {

    @Bean
    @ServiceConnection
    PostgreSQLContainer<?> postgresContainer() {
        return new PostgreSQLContainer<>("postgres:16");
    }
}

// 개발 시작 시 사용
public class DevApplication {
    public static void main(String[] args) {
        SpringApplication.from(MyShopApplication::main)
                .with(TestcontainersConfig.class)
                .run(args);
    }
}

DevApplication 실행 시 — 진짜 PostgreSQL 자동 시동 + Spring Boot 시작. 로컬 PostgreSQL 설치 안 해도 개발 가능.

CI 환경 — GitHub Actions

GitHub Actions에서도 Docker가 자동 제공돼서 — Testcontainers가 그대로 동작.

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          java-version: '17'
      - run: ./gradlew test

별도 설정 없이 PR마다 진짜 PostgreSQL로 테스트.

⚠️ Testcontainers의 함정

Docker 의존 — 개발자 PC에 Docker Desktop 필수. CI 환경에 Docker 없으면 동작 X. 컨테이너 시동에 1~10초 — 단위 테스트엔 무겁고, 통합 테스트엔 합리적. 가벼운 단위 테스트는 Mockito, 통합 테스트는 Testcontainers 조합.

한 줄 정리 — Testcontainers = 진짜 PostgreSQL·Redis·Kafka를 Docker로 자동 띄워 통합 테스트. H2 방언 차이 함정 회피. Spring Boot 3.1+ @ServiceConnection 한 줄로 표준화. 운영 환경과 똑같은 테스트.

시험 직전 한 번 더 — Testcontainers 입문자가 매번 헷갈리는 것

  • Testcontainers = Docker 컨테이너를 테스트 안에서 자동 관리
  • 지원 = PostgreSQL·MySQL·Redis·Kafka·Elasticsearch·MongoDB 등
  • 전제 조건 = Docker 설치
  • H2 인메모리의 한계 = 운영 DB와 방언·함수 차이
  • 의존성 = testcontainers/junit-jupiter + testcontainers/postgresql + spring-boot-testcontainers
  • @Testcontainers = 클래스 레벨, 컨테이너 자동 시작·종료
  • @Container = 컨테이너 필드 표시
  • @DynamicPropertySource = Spring 설정 동적 변경 (옛 방식)
  • @ServiceConnection = Spring Boot 3.1+ 간편 (권장)
  • 한 줄로 PostgreSQL·Redis·Kafka 자동 연결
  • 컨테이너 재사용 = .withReuse(true) + ~/.testcontainers.properties
  • 첫 테스트만 시동, 다음부터 즉시
  • @DataJpaTest + Testcontainers = replace = NONE 명시
  • Devtools 모드 = SpringApplication.from(...).with(...) 로컬 개발
  • 로컬 DB 설치 안 해도 됨
  • CI 환경 (GitHub Actions) = Docker 자동 제공
  • 컨테이너 시동 = 1~10초
  • 가벼운 단위 테스트 = Mockito, 통합 테스트 = Testcontainers
  • 두 가지 조합이 한국 회사 표준
  • 운영 환경과 똑같은 테스트로 "테스트 통과 = 운영도 OK" 보장

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글:

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!