React

[React] filter의 무한루프와 useMemo

JungCw 2024. 11. 14. 10:21

데이터를 걸러내어 원하는 데이터로 변환시키기 위해 흔히 사용하는 메서드인 filter() 는

const filteredArr = arr.filter(word => word.length === 3);

이런식으로 사용한다.

 

단순히 이렇게만 사용한다면 아무런 문제가 되지 않지만, filter의 결과값이 상태나 props로 사용된다면 문제가 발생한다.

 

예를들면,

const filteredArr = arr.filter(word => word.length === 3);

useEffect(() => {
	...
}, [filteredArr]);

 

이런식으로 filteredArr이 의존성 배열로 존재하고, useEffect 내부의 내용이 상태를 변화시키는 내용일 경우에 무한루프가 발생한다.

 

일반적인 useEffect와 그 내부의 상태변화메서드가 존재한다면, 재랜더링 된 후에는 다시 호출될 일이 없다.

그런데 filter가 재랜더링으로 인해 다시 사용된다면 얘기가 조금 다르다.

 

먼저 의존성 배열을 살펴보면, useEffect의 []안에 존재하는 의존성 배열은 값자체를 비교하는 것이 아니라 해당 배열의 참조값을 토대로 비교를 한다는 특징이 있다.

반면에, filteredArr은 재랜더링 시에도 그 값은 같으나 filter()메서드의 특성상 반복된 결과값이어도 그 참조값은 서로 다르다.

그렇다보니 의존성 배열이 다르다고 판단을 해 useEffect훅을 재호출하는 상황이 발생하는 것이다.

 

이런 식으로 무한루프가 발생하는 경우에 두가지 방법이 있다.

하나는 참조값을 비교하는 방법인 '얕은 비교(shallow comparison)'가 아니라 '깊은 비교(deep comparison)'를 하는 것이고,

다른 하나는 filter()메서드처럼 참조값에 변화를 주는 원인을 차단하는 방법이다.

 

이 중에 좀 더 효율적인 방법은 후자라고 생각을 한다.

 

물론 비교 방식을 다르게 해야하는 경우도 존재하지만, 이 방식은 근본적인 문제 해결이 아니다.

또한, filter() 메서드처럼 값은 같으나 참조값이 다른 경우는 굳이 할 필요 없는 작업이라는 뜻이기도 하다.

이걸 다른 말로 하면 자원 낭비이다.

 

조건을 부여해 상태 변화로 인해 재랜더링 되더라도 다시 계산하지 않도록 useMemo를 사용하자.

 

const filteredArr = useMemo(() => arr.filter(word => word.length === 3),[arr]);

useEffect(() => {
	...
}, [filteredArr]);