본문 바로가기
컴퓨터/Frontend

[React] 최적화(3) - useCalback, 함수형 업데이트

by stdFrog 2023. 3. 27.

지난 시간에 React.memo 훅을 이용한 고차 컴포넌트에 대해 알아봤다.

간단히 복습해 보면 React.memo는 불필요한 렌더링을 최소화하기 위해 사용되는 훅킹 함수로, 결과적으론 메모이징된 컴포넌트 자체를 반환했었다.

이렇게 반환된 컴포넌트는 다른 컴포넌트로 전달될 수 있으며, 이처럼 컴포넌트를 받는 컴포넌트를 고차 컴포넌트라 불렀다.

React.memo 훅은 앞에서 알아본 대로 굉장히 유용하지만 한 가지 단점을 갖고 있다.
그 특성상 비원시 타입(배열 또는 구조체 등)에 대해 얕은 비교를 수행한다는 점인데

즉, 실질적인 데이터가 아닌 주소를 비교하여 부모의 리렌더링으로부터 재생성되는 함수에 대해서는 정확한 예외처리를 하지 못한다는 것이다.

만약 위와 같은 상황에서 Props로 함수를 전달받는 컴포넌트가 있다면 React.memo 훅을 이용하더라도 불필요한 렌더링을 수행하게 될 것이다.

이번 글에서 소개할 useCallback이 이런 상황에서 사용되며 React.memo가 컴포넌트를 대상으로 한다면 useCallback은 함수를 대상으로 대상체 자체를 기억해 둔다고 볼 수 있다.

사용 방법은 간단하다 아래 예시를 보자.

 

const onButton = useCallback((Message, Score)=>{
	const newBox = {
		Message,
		Score
	};

	setData([newBox, ...Box]);
},[Box]);

 

위에 나와있는 onButton 함수가 최상위 컴포넌트에 속한 재생성 함수라고 가정하자.
최상위 컴포넌트에서는 단순히 하위 컴포넌트로 이 함수를 전달한다.

이 함수는 Box라는 상태 데이터에 변화가 있을 때마다 전달되는데 useCallback 훅을 이용하여 메모이징된 콜백 함수로 만들고 이를 하위 컴포넌트로 전달하면 최상위 컴포넌트가 렌더링 되어도 Box에 변화가 없다면 불필요한 렌더링을 수행하지 않을 것이다.

useCallback 훅은 이와 같이 React.memo가 가진 한계를 보완하기 위해 지원된다고 볼 수 있다.



사실 useCallback 자체를 이해하는 건 그리 어렵지 않다. 
다만, 위 예시처럼 최상위 컴포넌트의 상태 데이터가 하나의 함수에만 대응되는 경우가 그리 흔할까?

일반적으로 최상위 컴포넌트에서 생성한 상태 데이터는 하위 컴포넌트를 관리하는 전역적인 데이터로 사용된다.

즉, 이를 참조하는 객체나 함수가 어떤 형태로든 2개 이상은 존재한다는 뜻이다.

최상위 컴포넌트는 useState 훅으로 상태 데이터를 감시하고 있다가 변화가 생겼을 때 리렌더링 하는데 이때 이 데이터를 참조하고 있는 함수가 있다면 리렌더링과 동시에 재생성됨은 물론 Props로 이 함수를 전달받는 하위 컴포넌트들은 영문도 모른 채 렌더링을 수행하게 될 것이다.

위 예시에선 Box라는 상태 데이터에 접근하는 함수가 하나만 존재하기 때문에 굳이 사용하지 않아도 별 문제가 없다.

그런데 앞서 얘기한 것처럼 다른 함수에서도 Box의 값을 수정하거나 삭제할 수 있다면 어떻게 될까?
아래 예시를 보자.

 

const onReplace = (Message)=>{
	const ReplaceBox = Box.filter((a)=>a.Message !== Message);
	setData(ReplaceBox);
};


onReplace 함수 내부에서도 onButton과 똑같이 상태 데이터에 접근하여 값을 수정하고 있다.

위 함수가 동작하여 상태 데이터에 변화가 생기면 최상위 컴포넌트에서 이를 감지하고 다시 렌더링 할 것이다.
이미 말했듯 최상위 컴포넌트가 렌더링 됨에 따라 내부에 작성된 함수들 역시 재생성되고 이를 전달받는 하위 컴포넌트들은 꼼짝없이 렌더링을 수행해야 한다.

이는 결국 useCallback이 그 기능을 상실한다고 볼 수 있다.
때문에 이 훅킹 함수를 활용하는 것보다 상황에 대한 이해가 먼저 이루어져야 한다.

같은 상태 데이터를 참조하는 함수가 여럿 존재한다면 useCallback을 사용하되 첫 번째 예시에서처럼 두 번째 인자로 상태 데이터를 넘기기 보단 함수형 업데이트 구문을 사용해야 한다.

예시를 보자.

 

const onButton = useCallback((Message, Score)=>{
	const newBox = {
		Message,
		Score
	};

	setData((Box)=>[newBox, ...Box]);
},[]);


두 번째 인자를 비워 마운트 되는 시점을 한 번으로 제한하고  setData 함수에 콜백 함수를 전달했다.

이를 함수형 업데이트라 부르는데 위와 같이 종속(또는 의존) 배열을 제하고 싶을 때 즉, 의존성을 없게끔  만들되 상태 데이터의 값을 최신 상태로 유지해야 할 때 보통 이런 식으로 작성한다.

이리저리 말이 길었는데 결국 함수형 업데이트와 useCallback에 대한 내용이다.

반응형

댓글