클럽 가입 신청을 관리자가 승인하는 기능을 만들던 중, 관리자가 본인의 클럽에 대해 승인하려 했는데, "유효하지 않은 클럽 관리자입니다" 라는 오류가 발생했다. 분명히 로그인한 사용자는 해당 클럽의 관리자였는데도 말이다.
아래는 클럽 관리자인지 검증하는 로직이다:
if (!club.getManager().equals(manager)) {
throw new GlobalException(ExceptionCode.UNAUTHORIZED_MANAGER);
}
로그를 찍어보면 다음과 같았다:
System.out.println("club.getManager() = " + club.getManager().getId());
System.out.println("requesting manager = " + manager.getId());
📋 출력 결과:
club.getManager() = 2
requesting manager = 2
두 ID는 분명히 같지만, equals는 false를 반환하여 예외가 발생하고 있었다.
JPA에서 equals()
를 오버라이드하지 않으면, 기본적으로 Object.equals()
가 사용된다. 이는 객체의 참조(주소)가 같은지를 비교하기 때문에, 같은 ID를 가진 Member라도 다른 영속성 컨텍스트에서 가져온 객체는 다르다고 판단한다.
즉, 다음과 같은 상황이 되는 것이다:
Member managerFromClub = club.getManager(); // 영속성 컨텍스트 A
Member currentUser = userDetails.getMember(); // 영속성 컨텍스트 B
managerFromClub.equals(currentUser); // false → 예외 발생
Member
엔티티에 equals()
와 hashCode()
를 오버라이드하여 ID 기준으로 논리적 동일성을 판단하도록 수정했다:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Member member = (Member) o;
return id != null && id.equals(member.id);
}
@Override
public int hashCode() {
return getClass().hashCode();
}
🔎 이렇게 하면 ID만 같아도 equals()
가 true로 판단되어 로직이 제대로 작동한다. 위 코드는 JPA에서 권장하는 패턴이며, Hibernate 공식 문서에서도 소개된 방식이다.