그 동안 써야지,, 하면서 계속 임시 저장되고만 있었던 게시글을 마무리 해보려고 한다.
최근 회사 내부에서 CI/CD 파이프라인에 소나큐브를 도입하면서 코드 리뷰 또한 많이 활성화가 되었다.
그중 Enum 타입에 대한 비교에 대해서 짧게 코드리뷰를 한 내용이 있는데 내용을 축약해서 정리해본다.
# 발단
- 내가 등록한 PR에 있던 코드 중 특정 코드가 대략 아래처럼 되어있었다.
public enum PaymentType
REMITTANCE,
ACCOUNT_TRANSFER,
VISA_MASTER_CREDIT_CARD
}
public void someThingMethod(UserRequest userRequest){
if(userRequest.getPaymentType() == PaymentType.REMITTANCE){
// do something
}
}
이에 A 개발자 분이 다음과 같은 코드 코드 리뷰를 달아주셨다.
# A 개발자분의 답변
PaymentType.REMITTANCE.equals(userRequest.getPaymentType())
enum에 대한 == 는 주소값들 비교합니다. 컴파일 시 타입 체크가 들어가기 때문에 다른 타입으로 비교하는 코드가 들어갔을 시 컴파일 단계에서 에러를 확인할 수 있습니다. 다만 NPE는 잡지 못합니다.
equals는 오브젝트의 값들 비교하고, 내부적으로 값 비교 시 == 를 사용합니다. 컴파일 단계에서는 타입 체크를 하지 않습니다. 런타임 시 NPE는 잡을 수 있습니다.
이 코멘트를 보고, 생각에 빠지게 되었다. Enum Class의 경우 Constant 격의 Class로 Class Loader에 의해 메모리로 로딩될 시, Method Area로 할당되고 모든 Enum 객체는 동일한 객체를 공유해서 쓴다고 이해하고 있었다. 따라서 주소 값이 같기 때문에 == 비교를 하면 좀 더 빠르게 비교가 이루어질 것이라고 생각했고 equals의 경우 type check가 되지 않기 때문에 엉뚱한 값으로 비교 시 런타임 에러가 발생하기 때문에 == 비교가 맞다고 생각을 해왔다. 다만 validation 로직이 없었을 경우 == 비교 시 NPE가 발생할 수 있다는 사실은 간과하고 있었다.
그래서 코드를 다시 천천히 보던 중 해당 RequestDto에서 아래처럼 Controller Layer에서 Validation이 이루어지고 있었던 것을 확인했했다.
@Getter
@Setter
public class UserRequest {
@NotNull(message="payment type은 반드시 입력되어야 합니다")
private PaymentType paymentType
}
위 코드 대로면 애초에 null 값이 들어올 수 없기 때문에, service layer에서 enum type에 대한 비교문에서 NPE가 발생하지 않는다. 사실 == 이냐 equals냐에 대한 문제는, 간단한 수정 문제로 사실 해당 코드 리뷰를 받고 바로 적용할 수 있었지만 나는 사유가 궁금했다. 오라클 문서에도 == 비교를 추천하고 있었고, 소나큐브에서도 == 비교로 룰이 정해져 있기 때문이다. 그래서 다음과 같이 궁금중을 담긴 반문을 해봤다.
# 나의 답변
말씀해주신 대로 해당 enum type 변수에 대해서는 controller 영역에서 request data를 dto class에 data binding 하는 과정에서 validation annotation으로 인해 null 여부가 체크되고 있습니다. 값에 대한 범위, null, 자료형, 패턴에 대한 검증은 controller 영역에서 이루어지고 부가적인 비즈니스 로직에 대한 검증은 service 영역에서 이루어지는 것으로 알고 있있어서, 저는 validatoin annotation으로 runtime 상태에서에 해당 enum 값의 NPE를 막고 비즈니스 로직에서는 == 비교를 통해 type mismatch와 NPE 문제를 잡고자 하였습니다.
이에 다시 A 개발자 분이 다음과 같이 답변해주셨다.
# A 개발자분의 답변
==는 주소 값을 비교 하기 때문에 NPE 발생하지 않습니다.
enum은 class입니다. enum이 정의되지 않았다면 NPE가 발생할 수 있습니다. PaymentType.REMITTANCE.equals(userRequest.getPaymentType()) 로하면 NPE도 피할 수 있습니다.
==를 무조건 사용하는 건 맞지 않습니다. 참고해서 사용해야 합니다
나는 더욱더 흥미로워졌다.🤔 그래서 간단하게 내 의견과 A 개발자분의 의견을 테스트 코드로 작성해보고 결과에 대해서 이야기를 나누어봤다.
# 나의 답변
생각해보니 == 는 주소 값을 비교하기 때문에 NPE가 발생하지 않는 게 맞네요!👍
PaymentType.REMITTANCE.equals(userRequest.getPaymentType()) 로하면 NPE도 피할 수는 있지만, 그 반대의 경우는 피할 수가 없습니다.
== 연산자의 경우는 구문의 순서에 상관없이 NPE가 발생하지 않습니다.
아래 다양한 케이스로 한번 테스트를 돌려봤습니다.
1. equals의 순서가 바뀌는 경우
2) == operator
3) equals method
이 코멘트 이후로, 오프라인으로 한번 더 이야기를 나누어봤는데 A 개발자 분과 나 모두 간과한 사실이 있었다. Enum의 Equals메서드를 확인해보면 아래처럼 final 메서드로 구현되어있다.
어차피 equals 내부에서는 == 비교를 통해 주소 값을 비교하고 있었던 것이다. equals로 하던 == 비교를 하던 어차피 내부에선 == 비교를 한다. 그렇다면 == 비교를 적용한다면 컴파일 타임에는 타입 미스 매치를 잡고 런타임 시에는 NPE가 발생하지 않기에 두 마리 토끼를 모두 잡을 수 있다. 하지만 결국 equals로 모두 변경하기로 결정되었다. 이유는 명확하게 NPE 발생을 방지하고, 기존 서버 개발 구성원들 모두가 equals 비교 방식이 익숙했고 해당 방법에 동의를 했기 때문이다.
해당 결정에 대해서는 딱히 섭섭하다 또는 부당하다는 생각은 전혀~~~ 들지 않는다. 팀의 기술 표준은 가능한 구성원 모두의 의견을 반영해서 결정되어야 하고(이래야 코드 리뷰할 때 서로 놓치지 않는다), 모두가 하나의 제품(서버 또는 프로그램)을 만들기 때문에 모두가 납득되는 방향으로 굴러가야 한다고 생각하기 때문이다.
또한 해당 문제는 매우 사소한 문제이므로 내 입장을 관철시킬 필요도 없다고 생각했다. 그저 이런 리뷰 절차가 너무나도 그리웠고 많은 것을 생각하고 찾아보게 된 경험이 변태스럽지만 행복하고 즐거웠다.😊😚
3줄 요약:
1. enum 비교 시 equals로 해야 하나요 == 비교를 해야 하나요!?
2. 둘 다 상관없습니다. equals 비교는 내부적으로 == 비교를 하고 있고, enum 상수가 앞으로 오게 비교하도록 해야 NPE 발생을 막습니다.
3. 팀 내 표준을 정하고 따라가세요!
'Programming > Java' 카테고리의 다른 글
[인프런/호주 현직 자바 개발자가 묻고 답하는 영어 기술 면접] 강의 정리 (0) | 2021.12.26 |
---|---|
[백 투 더 베이직] 객체지향 SOLID 원칙 (0) | 2021.07.19 |
[삽질일기] Arrays.asList 와 ArrayList의 차이 (1) | 2021.07.17 |
[Java8] 함수형 인터페이스와 람다 표현식 (0) | 2021.06.26 |
객체, 클래스 그리고 인스턴스 (0) | 2021.06.15 |