Sonamu의 테스트 데이터베이스 시스템과 설정 방법을 알아봅니다.
테스트 DB 개요
자동 롤백 Transaction 기반 테스트 후 자동 정리
Fixture 지원 실제 데이터 복사 일관된 테스트 환경
간편한 초기화 한 명령어로 설정 스키마 자동 복사
DB 구조
Sonamu는 여러 데이터베이스를 사용합니다:
역할 :
development_master : 개발 중 사용하는 DB
fixture : 테스트에 사용할 공통 데이터 저장 (팀 공유 가능)
test : 실제 테스트가 실행되는 DB (Transaction 기반)
DB 설정
sonamu.config.json
{
"db" : {
"development_master" : {
"client" : "pg" ,
"connection" : {
"host" : "localhost" ,
"port" : 5432 ,
"user" : "postgres" ,
"password" : "postgres" ,
"database" : "myapp_dev"
}
},
"fixture" : {
"client" : "pg" ,
"connection" : {
"host" : "fixture-db.example.com" ,
"port" : 5432 ,
"user" : "postgres" ,
"password" : "postgres" ,
"database" : "myapp_fixture"
}
},
"test" : {
"client" : "pg" ,
"connection" : {
"host" : "localhost" ,
"port" : 5432 ,
"user" : "postgres" ,
"password" : "postgres" ,
"database" : "myapp_test"
}
}
}
}
주의 :
test와 production_master는 절대 같은 DB를 사용하면 안 됩니다
Sonamu가 초기화 시 자동으로 검증합니다
// sonamu/src/testing/fixture-manager.ts
if ( Sonamu . dbConfig . test && Sonamu . dbConfig . production_master ) {
const tConn = Sonamu . dbConfig . test . connection ;
const pConn = Sonamu . dbConfig . production_master . connection ;
if (
` ${ tConn . host ?? "localhost" } : ${ tConn . port ?? 5432 } / ${ tConn . database } ` ===
` ${ pConn . host ?? "localhost" } : ${ pConn . port ?? 5432 } / ${ pConn . database } `
) {
throw new Error ( "테스트DB와 프로덕션DB에 동일한 데이터베이스가 사용되었습니다." );
}
}
Fixture 초기화
pnpm sonamu fixture init
개발 DB의 스키마를 Fixture DB와 Test DB로 복사합니다.
작동 과정 :
Development DB 덤프
# 스키마만 덤프 (데이터 제외)
pg_dump -d myapp_dev -s > /tmp/schema.sql
# 마이그레이션 기록 덤프
pg_dump myapp_dev knex_migrations knex_migrations_lock > /tmp/migrations.sql
Fixture DB 생성
# DB 생성
psql -c "DROP DATABASE IF EXISTS myapp_fixture"
psql -c "CREATE DATABASE myapp_fixture"
# 스키마 복원
psql myapp_fixture < /tmp/schema.sql
psql myapp_fixture < /tmp/migrations.sql
Test DB 생성
# DB 생성
psql -c "DROP DATABASE IF EXISTS myapp_test"
psql -c "CREATE DATABASE myapp_test"
# 스키마 복원
psql myapp_test < /tmp/schema.sql
psql myapp_test < /tmp/migrations.sql
결과 :
Fixture DB: 빈 스키마 (나중에 데이터 추가)
Test DB: 빈 스키마 (테스트마다 Fixture에서 동기화)
실행 예시
$ pnpm sonamu fixture init
DUMP...
SYNC to (REMOTE) Fixture DB...
DROP DATABASE IF EXISTS ` myapp_fixture `
CREATE DATABASE ` myapp_fixture `
# 스키마 복원...
SYNC to (LOCAL) Testing DB...
DROP DATABASE IF EXISTS ` myapp_test `
CREATE DATABASE ` myapp_test `
# 스키마 복원...
✅ Fixture initialization complete!
스킵 조건
Fixture DB와 Test DB가 동일한 경우 Test DB 생성을 스킵합니다:
// CLI 코드
const toSkip = (() => {
const remoteConn = Sonamu . dbConfig . fixture . connection ;
const localConn = Sonamu . dbConfig . test . connection ;
return remoteConn . host === localConn . host && remoteConn . database === localConn . database ;
})();
$ pnpm sonamu fixture init
SYNC to (REMOTE) Fixture DB...
✅ Complete
SYNC to (LOCAL) Testing DB...
⚠️ Skipped! (Same as Fixture DB )
테스트 DB 작동 원리
Transaction 기반
각 테스트는 독립된 Transaction에서 실행됩니다:
// sonamu/src/testing/bootstrap.ts
export function bootstrap ( vi : VitestUtils ) {
beforeEach ( async () => {
// 각 테스트 시작 전: Transaction 시작
await DB . createTestTransaction ();
});
afterEach ( async () => {
// 각 테스트 종료 후: 자동 롤백
await DB . clearTestTransaction ();
});
}
흐름 :
// 테스트 1 시작
BEGIN TRANSACTION ;
INSERT INTO users ( ... ); // 테스트 데이터 생성
SELECT * FROM users ; // 테스트 검증
ROLLBACK ; // 자동 롤백 - 데이터 사라짐
// 테스트 2 시작
BEGIN TRANSACTION ;
// 깨끗한 DB 상태에서 시작
INSERT INTO posts ( ... );
ROLLBACK ;
격리성 :
test ( "사용자 생성" , async () => {
const userModel = new UserModel ();
await userModel . create ({ username: "john" });
// 테스트 종료 후 자동 삭제
});
test ( "다음 테스트는 깨끗한 DB" , async () => {
const userModel = new UserModel ();
const { users } = await userModel . getUsers ();
expect ( users ). toHaveLength ( 0 ); // 이전 테스트의 john이 없음
});
빠른 실행 :
실제 DELETE 없음 → ROLLBACK만
DB 정리 시간 절약
베스트 프랙티스
1. DB 분리
// ✅ 올바른 설정: DB 분리
{
"development_master" : { "database" : "myapp_dev" },
"fixture" : { "database" : "myapp_fixture" },
"test" : { "database" : "myapp_test" }
}
// ❌ 잘못된 설정: 같은 DB
{
"development_master" : { "database" : "myapp" },
"test" : { "database" : "myapp" } // 위험!
}
2. Fixture DB 공유
팀 전체가 동일한 Fixture DB를 사용하면 일관된 테스트 환경을 유지할 수 있습니다:
{
"fixture" : {
"client" : "pg" ,
"connection" : {
"host" : "fixture-db.company.com" , // 팀 공유 서버
"database" : "myapp_fixture"
}
},
"test" : {
"client" : "pg" ,
"connection" : {
"host" : "localhost" , // 로컬
"database" : "myapp_test"
}
}
}
3. 정기적인 초기화
스키마가 변경되면 Fixture를 다시 초기화해야 합니다:
# 마이그레이션 실행 후
pnpm sonamu migrate run
# Fixture 초기화
pnpm sonamu fixture init
문제 해결
연결 실패
Error: connect ECONNREFUSED 127.0.0.1:5432
해결 :
PostgreSQL 서버가 실행 중인지 확인
sonamu.config.json의 연결 정보 확인
권한 오류
Error: permission denied for database myapp_test
해결 :
-- DB 사용자에게 권한 부여
GRANT ALL PRIVILEGES ON DATABASE myapp_test TO postgres;
DB 이미 존재
⚠️ Database "myapp_test" Already exists
해결 :
# DB를 강제로 재생성하려면 수동으로 삭제
psql -c "DROP DATABASE myapp_test"
pnpm sonamu fixture init
주의사항
테스트 DB 사용 시 주의사항 : 1. 프로덕션 DB 절대 금지 : test DB는 프로덕션과 완전히 분리 2.
스키마 동기화 : 마이그레이션 후 fixture init 실행 3. Transaction 기반 : 테스트 간 격리는
자동 처리 4. Fixture 공유 : 팀원과 동일한 Fixture DB 사용 권장 5. 정기적인 초기화 : 개발 DB
스키마 변경 시 재초기화
다음 단계
Fixture 생성 fixture.ts 작성하기
Fixture 동기화 Fixture → Test DB