#Backend

DDL이 코드 밖에서 온다면, 테스트 DB 구성을 빌드 안에 선언한다

DDL이 코드 밖에서 온다면, 테스트 DB 구성을 빌드 안에 선언한다
01

Summary

Flaky 테스트는 안녕! 빌드 스크립트에 테스트 DB 구성을 선언하는 똑똑한 방법

Liquibase 기반 멀티 모듈 환경에서 Testcontainers의 실행 비용을 극적으로 낮춘 flex팀의 여정

본 아티클은 애플리케이션 코드가 아닌 Liquibase 마이그레이션 파일을 진실의 원천으로 삼는 환경에서 겪는 테스트 데이터베이스 구성의 문제를 지적합니다. 공유 데이터베이스나 초기화 스크립트에 수동으로 관리하던 스키마 구성을 root Gradle 빌드 안의 variant 선언으로 투명하게 가져왔습니다. 나아가 DB 덤프 최적화와 Gradle 빌드 캐싱 기술을 조화시켜 테스트 격리와 성능 향상을 모두 이뤄낸 설계 패러다임을 제안합니다.

  • 01Hibernate ddl-auto에 의존하지 않고 운영 환경과 동일한 실제 마이그레이션 히스토리 기반으로 스키마 구축
  • 02Testcontainers를 매번 초기화하고 재생하는 느린 스키마 생성 프로세스의 비용 및 병목 해결
  • 03root 빌드에 테스트 DB variant를 명시적으로 선언하여 마이그레이션 의존성 흐름을 명세화하고 조립 자동화
  • 04프로젝트 단위의 병렬 빌드 시 DB 테이블이나 테스트 데이터가 서로 섞이지 않는 엄격한 데이터베이스 격리 구현
  • 05덤프 파일 내 비결정적인 요소를 정규화 제거하여 Gradle 빌드 캐시 적중률을 극대화

RECOMMENDATION

멀티 모듈 구조에서 데이터베이스 마이그레이션 도구를 사용하면서, 스키마 정합성 검증과 테스트 수행 속도 사이의 효율적 균형을 찾고자 하는 백엔드 엔지니어들에게 권장합니다.

The Problem

스키마 변경 사항(DDL)이 애플리케이션 코드가 아닌 Liquibase changelog로 관리되어 테스트 시 각 모듈별 스키마 의존 관계가 빌드 시스템에 명시되지 않는 문제가 있었습니다. 이로 인해 특정 모듈 테스트를 단독 실행할 때 연관 테이블 누락으로 에러가 발생하거나 CI 병렬 테스트 시 간헐적 실패가 발생했습니다.

The Solution

root 빌드 파일에 테스트용 데이터베이스 변리언트(variant)를 정의해 필요한 스키마 프로젝트들을 선언적으로 지정하고 각 변리언트마다 전용 데이터베이스를 격리하여 할당했습니다. 실제 DB에 적용된 마이그레이션 결과를 엔진 덤프 형태로 추출한 뒤 비결정적 요소를 제거하여 Gradle 빌드 캐시에 저장하고 재사용하도록 구현했습니다.

The Result

테스트 DB 구성 정보가 빌드 스크립트에 명확히 선언되어 사람의 관례나 위키에 의존하지 않게 되었고 격리된 DB 환경 덕분에 안정적인 병렬 테스트 실행이 가능해졌습니다. 매번 처음부터 마이그레이션을 재생하는 불필요한 연산 비용을 덤프 캐싱을 통해 차단하여 전체 테스트 수행 속도가 대폭 단축된 것으로 추론됩니다.

Trade-off

동일한 데이터베이스 구성을 여러 DBMS 엔진을 넘나들며 테스트하기가 번거로워졌을 수 있으며, 결정적 덤프 생성을 위해 생성 시간이나 시퀀스 값 같은 비결정적인 요소들을 매번 정규화해야 하는 추가적인 전처리 로직 유지보수 공수가 수반됩니다.

03

Key Concepts

Concept · 01

Liquibase

데이터베이스 스키마 마이그레이션을 버전별 changelog 파일로 기록하고 순차적으로 적용할 수 있도록 도와주는 데이터베이스 형관리 및 버전 제어 도구입니다.

  • 애플리케이션 엔티티 코드가 아닌 changelog 자체를 데이터베이스 스키마 형상의 유일한 진실의 원천으로 활용했습니다.
  • 테스트용 데이터베이스 variant를 구성하기 위한 핵심적인 스키마 마이그레이션 입력 파일로 연동되었습니다.
Concept · 02

Testcontainers

통합 테스트 환경에서 Docker 컨테이너를 동적으로 구동하고 제어할 수 있도록 도와주는 오픈소스 라이브러리입니다.

  • 실제 프로덕션 데이터베이스와 동일한 DBMS 엔진 환경을 로컬 및 CI 환경에 온디맨드로 제공하기 위해 도입되었습니다.
  • 매번 컨테이너를 띄우고 수많은 마이그레이션을 실행하는 긴 수행 시간 병목이 발생하여 캐싱 기법의 도입 계기가 되었습니다.
Concept · 03

Database Variant (데이터베이스 변리언트)

특정 테스트 환경이나 모듈 조합에 필요한 스키마, 계정 권한, 초기화 설정 등을 빌드 레벨에서 정의한 독립적 데이터베이스 단위를 의미합니다.

  • root 빌드 파일 내에 어떤 스키마 프로젝트들을 포함할지 선언식으로 정의하여 테스트 DB 구성 정보를 빌드 내에 명세화했습니다.
  • 각 변리언트별 격리된 물리적 전용 데이터베이스를 할당함으로써 병렬 빌드 실행 안정성을 확보했습니다.