Skip to main content
데이터베이스 트랜잭션을 사용하여 테스트를 격리하고, 테스트 간 데이터 오염을 방지하는 방법을 알아봅니다.

데이터베이스 테스트 격리란?

Sonamu는 각 테스트를 트랜잭션으로 감싸서 실행하고, 테스트 종료 시 자동으로 롤백합니다. 이를 통해:
  • 테스트 간 데이터가 섞이지 않음
  • 테스트 실행 순서와 무관하게 항상 동일한 결과
  • DB를 초기 상태로 되돌리는 별도 cleanup 불필요

자동 트랜잭션 관리

bootstrap() 함수

Sonamu의 bootstrap() 함수가 자동으로 트랜잭션을 관리합니다:

bootstrap()의 역할

핵심:
  1. beforeEach: 각 테스트 시작 전 트랜잭션 생성
  2. afterEach: 각 테스트 종료 후 트랜잭션 롤백
  3. 테스트 성공/실패 여부와 무관하게 항상 롤백

테스트 환경 DB 설정

sonamu.config.ts

테스트용 DB는 sonamu.config.ts에서 설정합니다:
중요:
  • 테스트 DB는 개발/운영 DB와 완전히 분리
  • pool.max: 1로 설정하여 단일 연결 사용 (트랜잭션 격리 보장)

DB 클래스의 테스트 모드

DB 클래스는 NODE_ENV=test일 때 자동으로 테스트 모드로 전환됩니다:

트랜잭션 격리 메커니즘

createTestTransaction()

beforeEach에서 호출되어 새로운 트랜잭션을 시작합니다:
작동 방식:
  1. Write DB 인스턴스 가져오기
  2. 새 트랜잭션 시작
  3. testTransaction 속성에 저장
  4. 이후 모든 쿼리는 이 트랜잭션을 통해 실행됨

clearTestTransaction()

afterEach에서 호출되어 트랜잭션을 롤백하고 정리합니다:
작동 방식:
  1. 현재 트랜잭션 롤백 (모든 변경사항 취소)
  2. testTransactionnull로 초기화
  3. 다음 테스트를 위한 준비 완료

실전 예제

기본 CRUD 테스트

여러 테이블 작업

트랜잭션 내 에러 처리

복잡한 비즈니스 로직 테스트

테스트 격리 검증

격리 확인 테스트

수동 트랜잭션 제어

필요한 경우 트랜잭션을 수동으로 제어할 수 있습니다:

트랜잭션 외부 작업 (주의)

일부 작업은 트랜잭션 범위 밖에서 실행되므로 주의가 필요합니다:
트랜잭션으로 롤백되지 않는 작업들:
  1. 파일 시스템 작업
  2. 외부 API 호출
  3. DDL 문 (PostgreSQL의 경우 예외)
  4. 시간 함수 (NOW(), CURRENT_TIMESTAMP 등)

파일 시스템 Mocking

파일 작업은 Naite Mock을 사용합니다:
자세한 내용은 API Mocking을 참고하세요.

성능 최적화

단일 연결 풀

테스트 환경에서는 pool.max: 1로 설정하여 단일 연결만 사용합니다:
이유:
  • 트랜잭션 격리를 보장하려면 같은 연결을 사용해야 함
  • 여러 연결을 사용하면 트랜잭션이 공유되지 않음

병렬 실행 제한

테스트는 순차적으로 실행하는 것이 안전합니다:
또는 vitest.config.ts:

주의사항

데이터베이스 테스트 시 주의사항:
  1. bootstrap() 필수: bootstrap(vi)를 호출하지 않으면 트랜잭션이 생성되지 않아 실제 DB에 데이터가 저장됩니다.
  2. 테스트 DB 분리: 개발 DB와 테스트 DB를 반드시 분리하세요. 실수로 개발 데이터가 삭제될 수 있습니다.
  3. 외부 리소스 정리: 파일, 외부 API 호출 등은 수동으로 정리해야 합니다.
  4. DDL 주의: MySQL에서는 DDL 문이 즉시 커밋되어 롤백되지 않습니다. 테스트에서 테이블 생성/삭제는 피하세요.
  5. 병렬 실행 제한: 트랜잭션 격리를 위해 테스트는 순차 실행이 권장됩니다.

다음 단계

runWithMockContext

Mock Context로 테스트하기

API Mocking

외부 API 호출 모킹하기

Naite 로깅

테스트 로그 기록 및 추적

Bootstrap

테스트 환경 초기화