개요
수수료를 비교하는 코드에서 에러가 발생했다. 부과금액이 0원이 아닌 수수료만 추출해야 하는 상황에서 나는 다음과 같이 코드를 작성했다. 참고로 코틀린에서 BigDecimal 연산시에 == 연산자를 사용하면 equals와 동일하게 작동한다(관련 링크).
// 부과금액(amount)이 0원이 아닌 수수료만 추출한다
val paidFees = extraFeeService.findExtraFees(...).filter { it.amount != BigDecimal.ZERO }
@Service
class ExtraFeeService(...) {
fun findExtraFees(...): List<ExtraFee> {
// 수수료 API 서버에 요청을 날림
}
}
data class ExtraFee(
val amount: BigDecimal,
...
)
그런데 실제론 부과금액이 0원인 수수료까지 포함되어 추출되었다. extraFeeService 내부에서 별도의 수수료 API 서버에 요청을 날리고 응답을 받아오는데, 역직렬화 과정에서 BigDecimal에 "0.0000"이 들어가는 것이었다.
이러한 이유로 맨 위에 있는 코드는 부과금액이 0원인 수수료를 제대로 판별하지 못했다.
"0" != "0.0000" // true
BigDecimal 비교 원리
BigDecimal은 unscaled value와 scale이라는 속성을 가지고 있다. unscaled value는 정수로 표현된 BigDecimal의 값이며, scale은 소수점 이후의 자리 수이다. 즉, BigDecimal 3.14는 314라는 unscaled value와 2라는 scale을 지니고 있는 것이다. equals 메서드는 이 두 속성의 값이 동일한 경우에만 true를 리턴한다.
한편, BigDecimal.ZERO는 unscaled value와 scale이 모두 0이다. 한편 BigDecimal "0.0000"은 unscaled value가 0이지만, scale이 4이다. 즉, BigDecimal.ZERO와 BigDecimal "0.0000"을 equals(코틀린의 ==)로 비교하면 다른 값이라고 판별된다.
따라서 scale을 무시하고 unscaled value로만 BigDecimal을 비교하고 싶다면 equals 대신 compareTo를 사용해야 한다.
val paidFees = extraFeeService.findExtraFees(...).filter {
it.amount.compareTo(BigDecimal.ZERO) != 0
}
참고
- https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html
- https://www.baeldung.com/java-bigdecimal-zero
- https://www.baeldung.com/java-bigdecimal-biginteger
- https://jsonobject.tistory.com/466
- https://stackoverflow.com/questions/3866514/why-bigdecimal5-50-not-equals-to-bigdecimal5-5-and-how-to-work-around-th/3866544#3866544
- https://kingjakeu.github.io/java/2020/12/23/bigdecimal/
'프로그래밍 언어 > Java + Kotlin' 카테고리의 다른 글
[Kotlin] 코틀린에서 애너테이션 사용하기 (vs 자바) (0) | 2022.12.19 |
---|---|
[Java] JVM을 통해 알아보는 자바 프로그램 실행방식 (0) | 2022.12.15 |
[Kotlin] scope function - let, run, also, apply, with 차이 (0) | 2022.08.13 |
[Kotlin] 코틀린 유용한 함수 - padEnd (0) | 2022.07.19 |
[Kotlin] 코틀린에선 빌더 패턴(Builder Pattern) 대신 data class를 사용하라 (0) | 2022.06.22 |