티스토리 뷰
API Server를 개발 중에 로컬에서는 MySQL, 테스트 환경에서는 H2를 사용하고 있다
테스트 환경에서 실제 사용할 DB가 아닌 인메모리 데이터베이스를 쓰는 것은 자칫 위험할 수 있다
테스트를 돌리더라도 실제 DB와 상호작용을 한 것이 아니니 신뢰도가 100%라 할 수는 없을 것이다
그럼에도 실제 DB를 쓰지 않는 이유는 두 가지다
1. 외부 의존성을 빼고 오프라인 상태에서도 돌아갈 수 있게끔 하기 위해
2. 인메모리 데이터베이스를 이용해 전체 테스트를 빠르게 돌리기 위해
JPA, QueryDSL 사용 중이며 H2에서 BigDecimal를 다루다 만났다, 정확한 문제 상황은 다음과 같다
MyEntity 안에 BigDecimal 타입으로 설정한 price를 case-when 절로 조회해 올 때 문제가 발생했다
return Expressions.asNumber(
Expressions.cases()
.when(MyEntity.status.eq(ACTIVE))
.then(MyEntity.price.value)
.otherwise(BigDecimal.ZERO)
).sum();
문제 원인은 위와 같이 짜둔 상태에서 JPA가 만들어낸 쿼리에 cast 구문이 추가되면서 발생한다
org.h2.command.Parser는 자신만의 룰로 JPA가 만들어준 쿼리를 토큰 단위로 잘라 담아둔 후 하나씩 파싱 한다
내가 캐스팅한 대로 BigDecimal로 뽑아오기 위해 NUMERIC(?8, ..)을 파싱 하려 시도한다
NUMERIC token을 만났을 때 어떻게 파싱 하는지 살펴보기 위해 과정을 따라가 보면
parseNumericType에서 괄호 뒤에 있는 정밀도를 찾으러 떠난다
readPositiveLong()이라는 메서드 이름을 살펴보면 괄호 뒤에 long 타입 양수가 와야 함을 예측할 수 있다
readPositiveLong()에서는 long 타입을 읽고 양수가 아니라면 에러를 터트리려 하고 있다
readLong()에서는 현재의 token 타입을 보고 음수/양수 판단과 함께 숫자가 아니라면 SyntaxError를 터트리려 하고 있다
토큰 타입 확인할 땐 currentTokenType이 숫자로 나와 헷갈릴 수 있는데
디버깅 포인트에서 currentTokenType이 위치한 곳으로 가보면
token, currentToken을 참고해 현재 토큰이 무엇인지 알 수 있다
여기서 치환되었어야 할 '?8' token이 등장해 버렸다
그러니 숫자가 와야 할 자리에 '?' 문자가 왔으니 SyntaxError가 발생해 장렬하게 테스트가 터진다
결론으로 테스트 환경에서 H2를 쓰더라도 MySQL과 호환성을 지킬 수 있게 해 주면 된다
테스트 환경에서 사용할 application.yml에 다음 내용을 추가하면 된다
jdbc-url에 경로 입력 후 MYSQL 모드로 돌아가도록 만들고 dialect 지정까지 해주어야 한다
dialect 추가 없이 MODE만 변경하면 위 로직을 그대로 타기 때문에 동일한 에러가 발생할 것이다
spring:
datasource:
hikari:
jdbc-url: jdbc:h2:mem:testdb;MODE=MYSQL
driver-class-name: org.h2.Driver
username: ..
password: ..
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
'Spring > Spring Data' 카테고리의 다른 글
[JPA] 간접 참조 연관 관계 EventListener로 삭제하기 - 2 (0) | 2022.10.02 |
---|---|
[JPA] 간접 참조 연관 관계 EventListener로 삭제하기 - 1 (0) | 2022.10.02 |
[Redis] HATEOAS와 @Cacheable - 3 (0) | 2021.12.25 |
[Redis] HATEOAS와 @Cacheable - 2 (0) | 2021.12.22 |
[Redis] LocalDateTime Serialization / Deserialization 삽질기 - 2 (4) | 2021.12.22 |