팀 프로젝트에서 롬복을 사용하기로 하면서 @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 를 정의해줘야 하겠다!
'우아한테크코스' 카테고리의 다른 글
Docker 컨테이너에서 실행 중인 MySQL 데이터베이스 자동 백업하기 (2) | 2023.10.06 |
---|---|
스탬프크러쉬 테이블 구조 개선기 (0) | 2023.09.30 |
5차 데모데이 회고 (0) | 2023.09.26 |
Tomcat 구현하기 (1) - Servlet (0) | 2023.09.04 |
API 문서화에 RestDocs + Swagger UI 적용하기 (0) | 2023.08.23 |