Flutter에서 상태 관리를 하다 보면 Equatable이라는 클래스를 많이 보게 된다.
처음에는 단순히 “== 연산자를 쉽게 하게 해주는 도구인가?” 정도로 생각할 수 있지만,
실제로는 성능 최적화와 상태 관리의 핵심적인 역할을 한다.
이번 포스팅에서는 내가 직접 사용하면서 궁금했던 것들을 질문하고, 정리한 내용을 토대로
Equatable의 개념부터, 왜 필요한지, 어디에 어떻게 사용하는지까지 아주 쉽게 정리해본다.
var user1 = User(id: 1, name: 'A');
var user2 = User(id: 1, name: 'A');
print(user1 == user2); // false → 주소값이 다름
하지만 Equatable을 상속하고 props에 비교하고 싶은 값을 명시하면,
값이 같으면 true로 판단한다.
class User extends Equatable {
final int id;
final String name;
const User({required this.id, required this.name});
@override
List<Object?> get props => [id, name];
}
print(user1 == user2); // true → 값이 같으니 같다고 판단함
상태 기반 UI는 상태 비교가 필수
Flutter에서는 상태가 바뀌었을 때만 화면을 다시 그리는 구조다.
이때 이전 상태와 현재 상태가 같은지 비교하는데,
기본 객체 비교(주소 비교)만 있으면 값이 같아도 “다르다”고 인식되어 불필요한 리빌드가 일어난다.
• 값으로 비교 (Equatable 사용) → 같으면 리빌드 안함 → 성능 최적화
• 참조로 비교 (Equatable 없음) → 주소만 달라도 리빌드 → 불필요한 리빌드 발생
상태관리 (Bloc, Cubit 등)에서는 더더욱 필수
Bloc 같은 상태 관리에서는 상태가 변경되었는지 여부를 == 비교로 판단한다.
Equatable 없이 상태를 정의하면 항상 새로운 인스턴스가 생성되어 리빌드가 계속 일어난다.
→ 그래서 Bloc 상태에서는 거의 필수적으로 Equatable을 사용한다.
class User extends Equatable {
final int id;
final String name;
const User({required this.id, required this.name});
@override
List<Object?> get props => [id, name];
}
var user1 = User(id: 1, name: 'Alice');
var user2 = User(id: 1, name: 'Alice');
print(user1 == user2); // true
props 안에 들어가는 값으로 비교하기 때문에, 값이 같으면 같은 객체로 인식된다.
값이 같아야 정확한 상태 변경 감지와 불필요한 UI 리빌드를 방지할 수 있기 때문
이게 핵심이다.
상태 기반 UI는 상태 변경 여부를 정확히 아는 것이 가장 중요하다.
따라서 객체가 값으로 비교 가능해야 하고, Equatable이 이걸 도와준다.
Equatable은 편리하게 props로 비교 값을 선언하지만,
원칙적으로는 아래처럼 ==과 hashCode를 오버라이드해서 직접 비교 로직을 짤 수도 있다.
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is User &&
runtimeType == other.runtimeType &&
id == other.id &&
name == other.name;
@override
int get hashCode => id.hashCode ^ name.hashCode;
하지만 이 방법은 코드가 많아지고 실수할 여지가 커서,
Equatable을 사용하는 게 훨씬 간편하고 안전하다.