테스트에서는 실제 DB, 인메모리 DB, Mock 중 무엇을 사용해야 할까요? #32
Replies: 3 comments
-
|
저는 이 테스트가 무엇을 신뢰하게 만들고 싶은가, 세 가지는 "더 빠른가 vs 더 정확한가"의 단순 트레이드오프가 아니라, Mock은 "내 코드의 판단 로직"을, 그래서 저는 "무조건 빠른 인메모리" 또는 "무조건 안전한 실제 DB"가 아니라, 1. 세 가지는 검증하는 것이 다르다이전 Mock 토론과 Repository 토론에서 정리했던 핵심을 이어받으면, 선택의 출발점은 항상 내가 지금 무엇을 검증하고 싶은가입니다.
여기서 제가 강조하고 싶은 건, 이 셋이 일렬로 줄 세워지는 게 아니라는 점입니다. Mock은 "DB를 안 거치고 내 로직만 본다", 인메모리는 "표준 동작은 보지만 운영 고유 동작은 못 본다", 실제 DB는 "다 보지만 비싸다"는 식으로, 각자 다른 영역을 비추는 도구입니다. 그래서 "무엇이 가장 좋은가"가 아니라 "이 검증에는 어떤 신뢰가 필요한가"를 먼저 물어야 한다고 생각합니다. 2. Mock을 쓸 때 — DB가 검증 대상이 아닐 때이전 Mock 토론의 결론을 그대로 가져오면, 저는 외부 시스템을 제외하고는 Mock을 보수적으로 쓰는 편입니다. 다만 명확히 Mock이 어울리는 경우가 있습니다. (a) Service의 판단 로직만 좁게 검증하고 싶을 때@Test
void 중복_예약이면_예외를_던진다() {
given(reservationRepository.existsBySlot(any())).willReturn(true);
assertThatThrownBy(() -> reservationService.create(요청))
.isInstanceOf(DuplicateReservationException.class);
}이 테스트의 관심사는 "Repository가 true를 줬을 때 Service가 예외를 던지는가"입니다. 실제 DB에서 데이터를 어떻게 조회하는지는 관심사가 아닙니다. 이럴 때 Mock으로 Repository를 대체하면 테스트 대상의 책임에만 집중할 수 있습니다. (b) 외부 시스템(API, 결제, 메일 등)일 때네트워크를 타거나 비용·부작용이 있는 의존성은 Mock이나 Fake가 적절합니다. Mock의 한계 (이전 토론에서 정리한 것)다만 Mock 기반 Service 테스트는 "유스케이스가 동작하는가"보다 "내가 예상한 호출 순서대로 불렸는가"를 검증하는 방향으로 흐르기 쉽습니다. 그러면 구현에 강하게 묶여 리팩터링 내성이 약해집니다. 그래서 저는 Mock은 "DB가 검증 대상이 아니라고 확신할 때만" 쓰는 편이 좋다고 생각합니다. 이번 미션처럼 비즈니스 로직 대부분이 도메인 규칙 + DB 상태 변화로 구성된 경우, Service를 전부 Mock으로 테스트하는 건 오히려 신뢰를 떨어뜨릴 수 있다고 봅니다. 3. 인메모리 DB(H2)를 쓸 때 — 빠른 피드백이 필요하고 표준 동작이면 충분할 때인메모리 DB는 실제 DB와 Mock 사이의 절충안입니다. 진짜 SQL을 실행하고 진짜 매핑·제약을 검증하지만, 메모리에서 동작해 빠르고 가볍습니다. (a) 어울리는 경우
@DataJdbcTest
class ReservationRepositoryTest {
@Autowired private ReservationRepository repository;
@Test
void 취소된_예약은_조회에서_제외된다() {
// H2에서 빠르게 SQL 동작 확인
}
}(b) 인메모리 DB의 함정 — 이게 핵심입니다저는 인메모리 DB를 쓸 때 가장 경계해야 할 것이 H2에서 통과한 SQL이 MySQL에서 실패할 수 있다는 점이라고 생각합니다. 이전 격리 수준 토론에서도 나온 "기본값을 무조건 믿지 않는다"와 같은 맥락입니다. 대표적인 차이들:
그래서 저는 'H2 통과 = 운영 OK'가 아니라 'H2 통과 = 표준 SQL은 OK' 정도로만 신뢰합니다. 운영 고유 동작이 중요한 검증(특히 동시성, 방언 의존 쿼리)은 H2로 안심하면 안 된다고 생각합니다. 4. 실제 DB를 쓸 때 — 운영에서 실제로 일어날 일을 신뢰하고 싶을 때실제 운영과 같은 DB(MySQL 등)를 띄워서 테스트하는 방식입니다. 요즘은 Testcontainers로 도커 컨테이너에 실제 DB를 띄우는 방식이 많이 쓰입니다. (a) 어울리는 경우
@SpringBootTest
@Testcontainers
class ReservationConcurrencyTest {
@Container
static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0");
@Test
void 동시에_같은_슬롯을_예약하면_하나만_성공한다() {
// 실제 MySQL에서 UNIQUE 제약이 동시성을 막는지 검증
}
}(b) 비용
그래서 모든 테스트를 실제 DB로 하기보다, 운영 고유 동작이나 동시성처럼 실제 DB가 아니면 검증할 수 없는 것에 한정해서 쓰는 편이 좋다고 생각합니다. 5. "운영 DB와 다른 DB로 테스트하는 게 의미 있는가"라는 질문이 주제에서 가장 논쟁적인 지점이라고 생각합니다. "운영은 MySQL인데 테스트는 H2로 하면, 그 테스트를 믿을 수 있나?"라는 질문입니다. 저는 이걸 '무엇을 신뢰하려는 테스트냐'로 나눠서 봐야 한다고 생각합니다.
즉 H2 테스트가 "무의미"한 게 아니라, H2 테스트가 주는 신뢰의 범위를 정확히 알고 쓰는 것이 중요하다고 봅니다. H2를 쓰면서 "이걸로 운영 동시성까지 검증됐다"고 착각하는 게 진짜 위험입니다. 이상적으로는 빠른 H2 테스트로 개발 중 빠른 피드백을 받고, CI에서는 Testcontainers로 실제 DB 검증을 한 번 더 거치는 2단 구성이 좋다고 생각합니다. 다만 이번 미션 범위에서는 거기까지 가지 않더라도, "지금 내 테스트가 운영을 얼마나 대변하는가"를 의식하는 것만으로도 충분히 의미 있다고 봅니다. 6. 미션에서의 제 선택이번 예약 대기 미션을 기준으로 제가 셋을 나누는 방식입니다. 도메인 규칙 → DB 없이 단위 테스트
Repository 쿼리 → 인메모리 DB(H2)로 기본 검증
유스케이스 흐름 → 통합 테스트 (실제 DB 또는 H2)
동시성 → 실제 DB가 아니면 무의미
외부 의존성 → Mock/Fake
7. 정리저는 이 선택을 이렇게 정리할 수 있을 것 같습니다.
핵심은 "무조건 빠른 것" 또는 "무조건 안전한 것"을 고르는 게 아니라, 각 도구가 비추는 영역을 알고 검증 대상에 맞게 나눠 쓰는 것이라고 생각합니다. 그리고 어떤 도구를 쓰든, 그 테스트가 운영 환경을 얼마나 대변하는지를 항상 의식하는 자세가 가장 중요하다고 봅니다. |
Beta Was this translation helpful? Give feedback.
-
1. 테스트 목적에 따라 실제 DB / H2 / Mock을 어떤 기준으로 선택해야 할까요?1. Mock 사용: 비즈니스 로직(service) 단위 테스트비즈니스 로직의 에러를 확인하기위해서는 해당 클래스의 외부 의존성을 제거해야합니다. 2. 실제 DB: DB 계층(Repository) 단위 테스트 / API 통합테스트(controller)실제 레포지토리의 정상 작동 여부를 확인하는 테스트라면 실제 운영 DB와 같은 종류를 쓰는 것이 좋습니다.
|
Beta Was this translation helpful? Give feedback.
-
전제
Mock이 전제를 둘로 쪼개면 인메모리전제 중, 앞부분에 해당하는 실제 DB"DB의 값을 가져오는 것"과 "동작을 수행하는 것" 전체 흐름을, 운영과 동일한 조건에서 보호합니다. 인메모리와 실제DB인메모리는 실제 DB에 비해 빠르지만, 운영에서 문제가 없음을 보장하지 않습니다. SQL의 문법적 오류와 실행은 인메모리로 정상 동작함을 보호할 수 있습니다. 다만, 트랜잭션이 제대로 적용되는 지 같은 여부는 운영환경과 같은 실제 DB로 테스트하는 것이 올바르다고 생각합니다. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
테스트를 작성할 때 DB 의존성을 어떻게 다룰지 고민하게 됩니다.
각 방식은 장단점이 있는 것 같습니다.
실제 DB를 사용하면 운영 환경과 가장 비슷하게 검증할 수 있지만,
테스트 속도나 환경 구성 비용이 커질 수 있습니다.
반대로 H2 같은 인메모리 DB는 빠르고 가볍지만,
운영 DB와 SQL 문법, 제약 조건, 트랜잭션 동작이 달라
테스트에서는 통과했는데 운영에서는 실패하는 문제가 생길 수 있습니다.
Mock은 특정 계층을 빠르게 단위 테스트하기 좋지만,
실제 DB 연동 과정에서 발생하는 문제는 잡기 어렵습니다.
그렇다면 테스트 목적에 따라 실제 DB / H2 / Mock을 어떤 기준으로 선택해야 할까요?
또한 운영 DB와 테스트 DB가 다를 때 발생할 수 있는 위험은 어떤 것들이 있을까요?
Beta Was this translation helpful? Give feedback.
All reactions