정의
메모리 주소가 달라도, 의미적으로 같은 값/상태를 나타내면 같다고 보는 객체입니다.이때 필요한 기준이 동일성(Identity)과 동등성(Equality)입니다. 그리고 “동등 객체”는 동등성 기준을 명확히 정의한 객체를 의미합니다.
왜 중요한가?
동등성 정의가 흔들리면 Set/Map 중복 처리, 캐시 키 충돌, JPA 엔티티 비교 버그, 테스트 불안정 같은 실무 문제가 터집니다.
1. 동일성(Identity) vs 동등성(Equality)
- 동등성(Equality): “값/의미가 같은가?” (다른 인스턴스여도 같다고 볼 수 있는가?)
| 구분 | 의미 | 예시 |
| 동일성 | 같은 참조(주소)의 인스턴스인가 | Java에서 == |
| 동등성 | 값/의미가 같은가(업무 규칙 기준) | Java에서 equals() |
2. 백엔드에서 “동등 객체”가 필요한 순간
- ✔ 중복 제거: Set에 담았는데 중복이 안 없어짐(혹은 과도하게 없어짐)
- ✔ 키 조회: HashMap 키로 넣었는데 다시 찾을 수 없음
- ✔ 캐시: 캐시 키/값의 “같음” 기준이 흔들리면 히트율/정확도가 깨짐
- ✔ JPA 엔티티: 동일성/동등성 혼용 시 영속성 컨텍스트와 충돌
- ✔ 테스트: assertThat(a).isEqualTo(b)가 의미대로 동작하지 않음
equals / hashCode 계약(Contract)
equals가 같으면 hashCode도 반드시 같아야 합니다.특히 HashSet / HashMap은 hashCode로 “버킷”을 찾고, equals로 “최종 동등성”을 확인합니다.
필수 규칙
1) a.equals(b) == true 이면 a.hashCode() == b.hashCode() 이어야 한다
2) equals는 반사성/대칭성/추이성/일관성을 만족해야 한다
3) equals에 사용한 필드는 hashCode에도 동일하게 반영해야 한다
3. 동등성 기준 설계: “무엇을 기준으로 같다고 할까?”
| 대상 | 동등성 기준(예) | 주의점 |
| Value Object | 모든 값(필드) 기반 | 불변(immutable) 설계가 유리 |
| Entity | 식별자(ID) 기반 | 영속화 전(아이디 없음) 케이스 주의 |
| DTO/Request | 요청 의미 기준(필드 일부) | 민감정보/시간 필드 포함 여부 신중 |
4. 실무에서 자주 터지는 함정 5가지
- ✔ mutable 필드를 equals/hashCode에 포함 → Set/Map에 넣은 뒤 값이 바뀌면 “찾을 수 없는 키”가 됨
- ✔ 엔티티의 ID 기반 equals인데 “영속화 전”에는 ID가 없음 → equals가 흔들림
- ✔ 프록시(JPA Proxy) 때문에 클래스 비교 방식(getClass) 선택을 잘못함
- ✔ 시간/랜덤/요청ID 같은 변동 필드를 포함해 “동등성”이 매번 달라짐
- ✔ 캐시 키에 equals/hashCode가 불안정한 객체를 사용 → 캐시 히트율/정확도 붕괴
"동등 객체를 잘 만든다는 것은, ‘무엇이 같은가’를 코드에 명시하는 일입니다."
단순한 유틸 구현이 아니라, 도메인 규칙을 안정적으로 고정하는 설계입니다.
백엔드 실전 가이드
“값 객체는 전체 값 기반”, “엔티티는 식별자 기반”을 기본 원칙으로 잡습니다.- ✔ Value Object는 가능하면 불변(immutable)으로 만들고, 필드 기반 equals/hashCode
- ✔ Entity는 기본적으로 ID 기반(단, 영속화 전 ID 없음 케이스 설계 필요)
- ✔ HashMap/HashSet 키로는 “변하지 않는 기준”만 사용
- ✔ 캐시 키는 문자열/프리미티브/불변 VO 등 안정적인 형태 권장