Redux의 shallowEqual이란?

Redux의 shallowEqual 이란??
불필요한 리렌더링을 방지하는 최적화 방법

Redux의 `useSelector`를 사용할 때, 상태 변경이 없어도 컴포넌트가 리렌더링된 적이 있나요?
이 문제를 해결하는 방법이 바로 `
shallowEqual`입니다.

안녕하세요, 여러분! React와 Redux를 사용하다 보면, 불필요한 리렌더링 문제를 마주할 때가 많습니다. 특히 Redux의 `useSelector`를 사용할 때, 객체나 배열이 상태로 저장되면 값이 변하지 않아도 리렌더링이 발생할 수 있습니다. 이런 문제를 해결하는 방법 중 하나가 바로 `shallowEqual`을 활용한 최적화 입니다. 이번 글에서는 shallowEqual 이 무엇인지, 어떻게 동작하는지, 그리고 언제 사용해야 하는지 자세히 알아보겠습니다!

1️⃣ shallowEqual 이란?

shallowEqual 은 Redux에서 useSelector를 사용할 때, 이전 상태와 현재 상태를 얕은 비교(shallow comparison)하여 불필요한 리렌더링을 방지하는 함수 입니다.

기본적으로 useSelector 는 Redux 상태(state)가 변경되면 항상 컴포넌트를 다시 렌더링 합니다. 하지만 상태가 실제로 변경되지 않았더라도, 새로운 객체(reference)가 생성되면 리렌더링이 발생 할 수 있습니다.

👉 shallowEqual 을 사용하면 객체 내부의 값이 동일할 경우, 같은 상태로 간주하여 불필요한 렌더링을 방지할 수 있습니다!


2️⃣ 왜 shallowEqual 이 필요한가?

Redux의 `useSelector`는 기본적으로 객체의 reference(참조 값)만 비교 합니다. 따라서 객체나 배열을 상태로 저장하면, 내부 값이 같아도 새로운 객체가 생성되면 리렌더링이 발생할 수 있습니다.

상황 문제
객체 상태 저장 새로운 객체(reference)가 생성되면, 내부 값이 같아도 리렌더링 발생
배열 상태 저장 새로운 배열이 생성되면, 이전 데이터와 동일해도 리렌더링 발생

👉 `shallowEqual`을 사용하면 객체 내부의 값이 같으면 동일한 상태로 간주 하여 불필요한 렌더링을 방지할 수 있습니다.

3️⃣ shallowEqual 의 동작 방식

Redux의 shallowEqual은 객체의 1단계 속성 값만 비교 하는 얕은 비교(Shallow Comparison)를 수행합니다. 즉, 객체의 참조 값(reference)이 달라도 내부 속성 값이 동일하면 같은 상태로 간주 합니다.

🔍  shallowEqual 비교 방식 예제

import { shallowEqual } from "react-redux";

const obj1 = { name: "홍길동", age: 30 };
const obj2 = { name: "홍길동", age: 30 };

console.log(obj1 === obj2); // false (다른 객체)
console.log(shallowEqual(obj1, obj2)); // true (속성 값이 같음)

👉 일반적인 `===` 비교에서는 `false`가 나오지만, 

👉 `shallowEqual`을 사용하면 객체의 속성 값이 동일하므로 `true`가 반환됩니다.


4️⃣ shallowEqual 의 한계

shallowEqual 은 1단계 속성 값만 비교하기 때문에, 깊은 비교(Deep Comparison)가 필요할 경우 적절하지 않습니다.

❌ 중첩된 객체 비교 문제

const obj1 = { user: { name: "홍길동" } };
const obj2 = { user: { name: "홍길동" } };

console.log(shallowEqual(obj1, obj2)); // false

✔️ `shallowEqual`은 객체의 최상위 속성 값만 비교하므로, 중첩된 객체의 내부 속성 값이 같아도 false를 반환합니다. 

✔️ 이 문제를 해결하려면 `reselect` 같은 라이브러리를 활용해 메모이제이션을 적용해야 합니다.

❌ 배열 비교 문제

const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3];

console.log(shallowEqual(arr1, arr2)); // false

✔️ shallowEqual 은 배열의 참조 값만 비교하기 때문에, 동일한 값의 배열이어도 false를 반환 할 수 있습니다. 

✔️ 이런 경우 Redux 상태를 관리할 때, 객체나 배열을 불변성을 유지하며 업데이트하는 것이 중요합니다!

❓ 자주 묻는 질문 (FAQ)

Q shallowEqual 을 사용하면 언제나 리렌더링이 방지되나요?

아니요. shallowEqual 은 객체의 1단계 속성 값만 비교하기 때문에, 중첩된 객체나 배열의 내부 값이 변경되면 여전히 리렌더링이 발생할 수 있습니다. 이런 경우 reselect와 같은 메모이제이션 기법을 적용하는 것이 좋습니다.

Q shallowEqual 대신 === 비교를 사용해도 되나요?

`===` 비교는 객체의 참조 값(reference)만 비교하기 때문에, 값이 동일하더라도 새로운 객체가 생성되면 다르게 판단 할 수 있습니다. shallowEqual은 1단계 속성 값을 비교하므로, 객체 내부 값이 동일할 경우 리렌더링을 방지할 수 있습니다.

Q shallowEqual이 모든 Redux 상태에 적용되는 건가요?

아니요. shallowEqual은 useSelector 훅을 사용할 때 두 번째 인자로 전달해야 적용됩니다. 기본적으로 useSelector 는 reference 비교(===)만 수행 합니다.

Q shallowEqual 을 사용하면 성능이 얼마나 향상되나요?

애플리케이션의 구조에 따라 다르지만, 리렌더링이 자주 발생하는 UI에서 성능 최적화 효과가 크며, 불필요한 렌더링 횟수를 줄일 수 있습니다. 특히, Redux 상태가 객체 또는 배열로 구성되어 있다면 shallowEqual 을 활용하는 것이 좋습니다.

📝 마무리

Redux의 shallowEqual은 불필요한 리렌더링을 방지하는 간단하면서도 강력한 최적화 도구입니다.

객체나 배열 상태가 변하지 않았음에도 불필요하게 렌더링이 발생하는 문제를 줄일 수 있습니다.

✅ shallowEqual은 1단계 속성 값만 비교 하는 얕은 비교 방식입니다. 

✅ 중첩된 객체나 배열을 비교할 때는 추가적인 최적화 기법(reselect 등)이 필요할 수 있습니다. 

✅ 성능 최적화가 필요한 프로젝트라면, shallowEqual을 적절히 활용하여 렌더링 성능을 향상시키는 것이 좋습니다!

React와 Redux를 사용하면서 성능 이슈를 경험한 적이 있나요? `shallowEqual`을 활용한 최적화가 도움이 되었나요? 여러분의 의견을 댓글로 공유해주세요! 🚀