엄밀히는 compareTo 규약 위반이고, 프로젝트 전체에서 같은 정밀도로 BigDecimal 을 써야 하는 이유이기도 합니다.
포함 여부: in vs contains
컬렉션 포함 여부는 contains 대신 in 으로도 쓸 수 있는데, 항상 in 이 낫지는 않습니다. 기준은 문장의 주어로 두고 싶은 쪽이 무엇인가 입니다.
강조 대상
자연스러운 형태
영어 문장 비유
요소
tag in SUPPORTED_TAGS
”There’s a soda in the fridge”
컬렉션
user.tags.contains(ADMIN_TAG)
”A human has a liver”
println(tag in SUPPORTED_TAGS) // tag가 관심사 → inval admins = users.map { user -> user.tags.contains(ADMIN_TAG) } // user가 관심사 → containsval admins = users.map { user -> ADMIN_TAG in user.tags }
두 번째 예는 contains 가 더 명확해 보이지만 사람마다 다르게 느낄 수 있으므로, 코드 리뷰에서 강제할 규칙이 아니라 제안 정도로 받아들이는 것이 좋습니다.
자체 클래스에 연산자 추가
측정 단위, 금액 같은 자체 타입에도 연산자를 정의할 수 있습니다.
@JvmInlinevalue class Centimeter(private val value: Double) { operator fun plus(other: Centimeter): Centimeter = Centimeter(value + other.value) operator fun plus(other: Millimeter): Centimeter = Centimeter(value + other.value * 10) // ...}
plus 가 실제로 “더하기”를 뜻하므로 연산자 의미와 함수 이름이 일치하는 좋은 사례입니다.
내 생각
BigDecimal 동등 비교는 compareTo 로 통일합니다. DB에서 DECIMAL(10,2) 로 읽은 100.00 과 코드의 BigDecimal("100") 을 == 로 비교하면 false 가 나오므로, 금액 비교는 a.compareTo(b) == 0 이나 setScale 로 정규화한 뒤 비교하는 편이 안전합니다.
기간 검증은 in start..end 가 가장 읽기 좋습니다. 쿠폰 유효기간, 이벤트 기간 체크처럼 경계 포함 여부가 중요한 로직에서 !isBefore && !isAfter 조합은 경계 버그를 숨기기 쉽고, .. 는 양끝 포함이라는 의미가 한눈에 보입니다.
금액은 Money 값 객체 + 연산자로 감싸는 것이 한 단계 더 좋습니다. 통화·scale 정책을 plus 안에 가둬 두면 위의 equals 함정도 도메인 경계에서 한 번에 막을 수 있습니다.