JaeWon's Devlog
article thumbnail
반응형

최근에 회사 동료가 코드 검사를 하겠다며 내가 작성한 코드를 보고 리뷰를 했다.

그러던 중 공통 상수로 선언한 클래스와 Enum 클래스를 사용한 것에 대해 이야기를 하였는데, Enum 을 사용하는데 왜 == 로 비교하지 않고, equals 메소드를 사용해서 비교하는가에 대해 이야기를 하게 되었다.

 

이 문제에 대해서 간단하게 고민해보고 생각한 것을 포스팅해보려고 한다.


1. 코드 상황

- 간단하게 구현된 코드 상태를 작성해보자면 아래와 같습니다.(이해를 돕기 위한 예제 코드입니다.)

  • Controller
@RestController
public class MemberController {

    @PostMapping("")
    public String memberTest(@RequestBody RequestMember member) {

        if(MemberEnum.MEMBER.equals(member.getMemberEnum())) {
            ...
        }
        if(MemberEnum.QUIT_MEMBER.equals(member.getMemberEnum())) {
            ...
        }

        return null;
    }
}
  • Enum
public enum MemberEnum {
    MEMBER("01"),
    QUIT_MEMBER("02"),
    TEMP_MEMBER("03")
    ;

    private String code;

    MemberEnum(String code) {
        this.code = code;
    }
}

2. Enum 값을 비교하기(== vs equals)


Java 에서 Enum 은 클래스 인스턴스가 JVM 내에 하나만 존재하도록 100% 보장되는 싱글톤 객체입니다.(JLS, 8.9 Enum Types)

2-1. 공통점

- 간단하게 테스트 코드를 통해서 확인해보겠습니다.

@Test
@DisplayName("Enum 기본 테스트")
void Enum_Test() {

    MemberEnum test = MemberEnum.MEMBER;

    assertThat(test == MemberEnum.MEMBER).isEqualTo(true);
    assertThat(test.equals(MemberEnum.MEMBER)).isEqualTo(true);

    assertThat(MemberEnum.MEMBER == test).isEqualTo(true);
    assertThat(MemberEnum.MEMBER.equals(test)).isEqualTo(true);
}

- 테스트를 수행해보면 모두  통과하는 것을 확인할 수 있는데, 이는 equals 메소드를 확인해보면 알 수 있습니다.

- 아래 그림에서와 같이 equals 메소드도 결국 == 연산자를 통해서 비교하고 있는 것을 확인할 수 있습니다.

2-2. 차이점

  • Null Safe(== 비교는 NPE를 발생시키지 않는다.)
@Test
@DisplayName("Enum 비교 Null 체크 테스트")
void Enum_NPE_Test() {

    MemberEnum test = null;

    System.out.println(test == MemberEnum.MEMBER);
    System.out.println(test.equals(MemberEnum.MEMBER)); // NPE 발생

    System.out.println(MemberEnum.MEMBER == test);
    System.out.println(MemberEnum.MEMBER.equals(test));
}

- == 비교는 NPE(NullPointerException) 을 발생시키지 않습니다.

- eqauls 메소드를 사용하여 비교하면 Runtime(런타임)에 NPE가 발생할 수 있습니다.(But, equals 메소드에서도 Enum 을 먼저 앞에 두고 비교하면 NPE 에서 벗어날 수 있습니다.)


위에 설명에서와 같이 NPE를 방지하는 것 뿐만 아니라 상수값, Enum 과 같은 변하지 않는 값들을 앞에 작성해서 비교하는 것이 좋습니다.
  • == 비교는 Compile(컴파일)에서부터 타입 미스 매치를 잡아준다.(이미지 참고)
@Test
@DisplayName("Enum 비교 Type 체크 테스트")
void Enum_Type_Test() {

    System.out.println(BookEnum.BOOK == MemberEnum.MEMBER);
    System.out.println(BookEnum.BOOK.equals(MemberEnum.MEMBER));

    System.out.println(MemberEnum.MEMBER == BookEnum.BOOK);
    System.out.println(MemberEnum.MEMBER.equals(BookEnum.BOOK));
}

- 위의 그림에서 확인하실 수 있듯이, == 비교를 사용하면 실수로 잘못된 타입 비교를 작성하여도 컴파일 과정에서 확인이 되지만 equals 메소드를 사용하게 되면 그대로 컴파일이 되어 버립니다.

3. 마무리

- 극단적이지만, 아래 예제를 확인해보겠습니다.


실제론 아래와 같이 코드를 작성하면 안됩니다!!!
@RestController
public class MemberController {

    @PostMapping("")
    public String memberTest(@RequestBody RequestMember member) {

        if(MemberEnum.MEMBER.name().equals(member.getMemberEnum())) {
            ...
        } else if(MemberEnum.QUIT_MEMBER.name().equals(member.getMemberEnum())) {
            ...
        } else {
            // 만약 Delete 코드가 있다면...??
        }

        return null;
    }
}

- Enum 타입과 Enum의 코드를 비교하니 어느 if 에도 걸리지 않습니다.

- Enum을 비교해서 알맞은 if 문을 통해서 로직을 수행하고자 하였지만, 실제론 else 문을 타게 되고 Delete 로직이 수행되면서 중요한 데이터가 삭제되는 경우가 발생할 수도 있습니다.

- 만약 == 비교를 사용하였다면 컴파일 자체가 되지 않으면서 실수를 캐치할 수 있었을 것이라 생각합니다.

- 사실 이 문제는 정답이 딱 정해져있는 문제는 아니라고 생각합니다. 이 문제에 대해서는 스택오버플로우에서도 다양한 이야기를 하고 있습니다.(https://stackoverflow.com/questions/1750435/comparing-java-enum-members-or-equals)

- 그렇지만, 컴파일 단계에서 실수를 확인할 수 있는 == 비교가 조금 더 유용하지 않을까라는 생각을 가지게 되었습니다.


참고

- https://johnmarc.tistory.com/152

- https://castlejune.tistory.com/34

반응형
profile

JaeWon's Devlog

@Wonol

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!