테스트 때문에 엔티티에 setter를 추가하면 안 되는 이유
테스트 때문에 엔티티에 setter를 추가하면 안 되는 이유
결론부터 말하면
- ❌ 엔티티에
setTempHoldExpiresAt()을 테스트 때문에 추가하는 건 비추천 - ✅ Repository 레벨에서 “테스트 전용 만료 조작”을 제공하는 게 정석
지금 구조에서는 오히려
setter가 없는 것이 설계적으로 올바른 상태다.
왜 setTempHoldExpiresAt()이 없는 게 맞는가?
1️⃣ Reservation은 “상태 전이 규칙”을 가진 도메인이다
Reservation은 단순한 데이터 객체가 아니다.
명확한 상태 전이 규칙(State Transition) 을 가진 도메인 엔티티다.
예를 들면:
TEMP_HOLD → CONFIRMEDTEMP_HOLD → EXPIRED
여기서 중요한 점은,
tempHoldExpiresAt은
상태 전이의 결과이지,
외부에서 마음대로 수정할 수 있는 값이 아니라는 것이다.
즉, 만료 시각은
“임시 배정 상태에 진입했을 때 계산되어 설정되는 값”이며
도메인 규칙의 일부다.
만약 엔티티에 이런 setter가 생긴다면:
1
reservation.setTempHoldExpiresAt(...)
이는 곧,
- 상태 전이를 거치지 않고
- 도메인 규칙을 우회하여
- 내부 상태를 직접 조작할 수 있는 통로를 여는 것과 같다 👉 도메인 캡슐화가 깨지는 순간이다.
2️⃣ 테스트 편의를 위해 도메인을 훼손하면 안 된다
테스트 때문에 setter를 추가하는 순간, 사실상 이런 선택을 한 것과 다르지 않다.
- 도메인 캡슐화 포기
- 상태 전이 규칙 무력화
- “테스트 편의성 > 설계 안정성” 우선
이건 좋은 테스트가 아니다.
좋은 테스트는 설계를 바꾸지 않는다. 나쁜 테스트는 설계를 망가뜨린다.
더 큰 문제는 이것이다.
- 이 setter는 실서비스 코드에도 그대로 남는다
- 누군가 실수로, 혹은 급한 수정으로 사용해버릴 수 있다
- 그 순간 도메인의 불변 조건은 무너진다
테스트를 위해 추가한 코드가 운영 리스크로 돌아오는 전형적인 패턴이다.
정석적인 해결 방법 (추천)
✅ Repository 레벨에서 “테스트 전용 만료 조작”을 제공한다
도메인에는 손대지 않고, 영속성 계층에서만 테스트를 위한 조작을 허용하는 방식이다.
예를 들면 다음과 같다.
1
2
3
4
5
6
7
8
public interface ReservationRepositoryPort {
void forceExpire(
UUID reservationId,
LocalDateTime expiredAt
);
}
이 메서드는 다음 성격을 가진다.
- 테스트 전용
- 상태 전이 규칙을 대체하지 않음
- 단순히 DB 상태를 조작하여 시간 경과를 시뮬레이션
이 방식의 장점은 명확하다
- ✔ 엔티티 불변성 유지
- ✔ 도메인 규칙 보호
- ✔ 테스트에서만 시간 조작 가능
- ✔ 실서비스 로직과 명확히 분리
도메인은 여전히 이렇게 말할 수 있다.
“나는 내 규칙대로만 상태가 바뀐다.”
왜 Repository 레벨이 적절한가?
이 선택의 핵심은 책임 분리다.
- 도메인
- 상태 전이 규칙을 지킨다
- 외부 조작을 허용하지 않는다
- Repository
- 상태를 어떻게 저장하고 조회할지 책임진다
- 테스트에서는 현실적으로 필요한 “시간 이동”을 시뮬레이션한다
테스트에서 시간 조작이 필요한 건 현실이다. 하지만 그 책임을 도메인에 넘기는 순간, 설계는 오염된다.
즉,
도메인은 규칙을 지키고 Repository는 현실을 시뮬레이션한다
이 역할 분리가 핵심이다.
정리
setTempHoldExpiresAt()이 없는 것은 설계적으로 정상이다- 테스트 때문에 엔티티에 setter를 추가하는 것은 안티 패턴이다
- 테스트 전용 상태 조작은 Repository 레벨에서 해결하는 게 정석이다
결과적으로:
- 도메인은 깨끗해지고
- 테스트는 현실적이며
- 구조는 오래 버틴다
한 줄 요약
테스트를 위해 도메인을 망가뜨리지 말자. 설계를 지키는 테스트가 결국 가장 강한 테스트다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.