본문 바로가기

우아한테크코스

롬복(lombok) 의 @EqualsAndHashCode 는 필드로 비교할까 getter 로 비교할까

팀 프로젝트에서 롬복을 사용하기로 하면서 @Getter, @RequiredArgsConstructor, @NoArgsConstructor 등의 어노테이션을 많은 엔티티에 붙이게 되었다. 

 

롬복은 Equals, HashCode 를 재정의해주는 @EqualsAndHashCode 도 지원해주기에 별다른 생각 없이 해당 메서드들의 재정의가 필요한 엔티티에 어노테이션을 붙이게 되었다.

그리고 코드 리뷰를 받았는데 롬복에서 제공해주는 @EqualsAndHashCode 는 값을 필드로 비교하는지 getter 로 비교하는지를 묻는 리뷰였다. 왜냐면 JPA 를 사용하게 되면서 지연 로딩과 프록시 객체의 개념을 학습하게 되었는데, 지연 로딩을 하면 연관 객체를 실제로 사용하는 시점(get() 을 해오는 시점) 에 데이터베이스에서 조회해오기 때문에 필드로 비교하면 제대로 동등성 비교가 되지 않을 수 있기 때문이다! 이 리뷰를 받고 나도 롬복의 @EqualsAndHashCode 가 어떻게 코드로 구성되는지 궁금해졌고 테스트해보기로 했다.

 

인텔리제이에서 생성해주는 @EqualsAndHashCode

가장 많이 사용하는 방식일 것 같은데 인텔리제이에서 생성해주는 @EqualsAndHashCode 는 필드 비교가 기본이다.

public class User {

    Long id;
    String name;
    String address;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(id, user.id) && Objects.equals(name, user.name) && Objects.equals(address, user.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, address);
    }
}

 

객체에 getter 가 구현되어 있고, Use getters during code generation 을 체크하면 getter 를 사용해 비교를 한다.

 

롬복의 @EqualsAndHashCode

테스트를 해보자. User 객체를 정의해준다. 그리고 getter 를 모든 User 객체는 동일한 값을 반환하도록 만들어준다.

@EqualsAndHashCode
public class User {

    Long id;
    String name;
    String address;

    public User(Long id, String name, String address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }

    public Long getId() {
        return 1L;
    }

    public String getName() {
        return "user";
    }

    public String getAddress() {
        return "address";
    }
}

 

테스트 코드를 작성해보면

class UserTest {

    @Test
    void 롬복의_EqualsAndHashCode_는_getter_비교일까() {
        User user1 = new User(1L, "user1", "address1");
        User user2 = new User(2L, "user2", "address2");

        assertThat(user1).isEqualTo(user2);
    }
}

 

getter 비교가 맞으면 테스트가 통과해야 할 것이다.

결과를 확인해본다.

통과했다.

그럼 롬복은 항상 getter 를 사용해서 비교를 할까?

 

lombok 패키지의 @EqualsAndHashCode 설명에 보면 기본으로는 필드 비교를 한다고 나와있다.

그리고 getter 가 존재한다면, getter 를 사용한다고 되어있다. getter 대신 바로 필드 비교를 하고 싶다면 doNotUseGetters() 를 true 로 설정하면 된다.

 

사실 의문이 생기게 된 원인은 JPA 의 지연 로딩때문이었는데 Entity 들은 사실 식별자인 id 만으로 동등성 비교를 하면 되기 때문에 필드 비교나 getter 비교를 할 필요가 없긴 하다. 

사실 테스트 코드에서 내용물이 같은지 검사를 많이 해보기 때문에 테스트 코드를 위해 Entity 에 @EqualsAndHashCode 를 붙이는건 굉장히 지양하고 usingRecursiveComparison() 등의 메서드를 많이 활용하기 때문에 프로젝트 코드에서도 확인해보면 롬복의 @EqualsAndHashCode 를 DTO 객체 이외에는 사용을 안했다. 

 

그럴일은 없을것 같지만 지연로딩을 사용하는 Entity 에서 동등성 비교를 하고 싶고 롬복의 @EqualsAndHashCode 를 사용하고 싶다면 우선 getter 를 정의해줘야 하겠다!