useCallback

김동현·2022년 1월 25일
1

React

목록 보기
18/27
post-thumbnail
post-custom-banner

React.memo의 문제점

자바스크립트에서 객체 타입의 값은 평가될 때마다 "언제나 새로운 값을 생성"하기 때문에 객체 타입의 값을 props로 전달한다면 React.memo를 사용하더라도 컴포넌트가 언제나 재평가되므로 React.memo를 사용하는 의미가 없어집니다.

이를 해결하기 위해서는 react에서 제공하는 useCallback 훅을 제공합니다. useCallback 훅은 컴포넌트 내부에 정의되어 있는 함수 객체를 "리액트에 저장"하는 훅입니다.
react에 함수 객체를 저장하고, 컴포넌트가 재평가될 때 함수 정의도 재평가 되는데 이때 함수 객체를 무작정 재생성하지 않고 특정 조건 만족시 생성되도록 합니다.

컴포넌트 내부에 정의된 함수를 평가하여 생성된 함수 객체를 컴포넌트가 처음 실행될 때 react의 내부 저장소 어딘가에 "저장"하고, 컴포넌트가 재평가가 된다면 특정 조건에 부합하지 않을 때만 이전에 react 내 저장된 함수 객체를 "재사용"함으로써 함수 객체를 재생성하지 않게 됩니다. 이러한 동작을 useCallback 훅이 담당합니다.

useCallback

useCallback첫 번째 인수로 "재생성하고 싶지 않은 함수 객체"useCallback 훅의 인수로 전달하면서 호출합니다. 두 번째 인수로는 "dependencies 배열"을 전달합니다.

// 첫 번째 인수로는 함수를 전달하고, 
// 두 번째 인수로는 dependencies 배열을 전달합니다.
const func = useCallback(function, [...dep]);

useCallback 훅 호출시 첫 번째 인수로 전달한 함수 객체는 react 내부에 저장되고, 함수 객체를 반환합니다.

이후 컴포넌트가 재평가될 때 두 가지 경우로 동작이 나뉘게 됩니다.

  1. dependencies 배열의 요소값이 하나라도 이전값과 일치하지 않은 경우에만 함수를 재평가하여 함수 객체를 새로 생성하고, 새롭게 생성된 함수 객체를 이전 react 내 저장된 함수 객체와 교체하고 반환합니다.

  2. dependencies 배열의 요소값이 이전값과 모두 일치하는 경우 react에 저장된 함수 객체를 재사용

dependencies 배열

useCallback을 사용할 때 useEffect 훅과 유사하게 두 번째 인수로 "dependencies 배열"을 전달해야 합니다.

dependencies 배열에는 인수로 전달한 함수가 사용하는 상태 혹은 props를 "모두 포함"해주어야 합니다. 만약 넣지 않는다면 함수 내에서 해당 값들을 참조할 때 최신의 값을 보장할 수 없게됩니다. 단, 상태 변경 함수나, 컴포넌트 외부에서 선언한 변수, 내장 API 등은 추가하지 않아도 됩니다.

여기서 왜 dependencies 배열이 왜 필요한지 의문이 생길 수 있습니다. dependencies 배열을 사용함으로써 인수로 전달된 함수 내 사용하는 값들을 언제나 최신값을 갖도록 보장하기 위해서 사용합니다.

자바스크립트에서 함수는 "클로저"라는 것을 명심해야 합니다. 즉, useCallback 인수로 전달된 함수 또한 클로저로 상위 스코프인 컴포넌트 함수 내부 식별자에 접근할 수 있습니다. 만약 컴포넌트가 재평가되어 인수로 전달한 함수가 사용중인 값이 변경되더라도 이전 컴포넌트 함수의 값, 즉 변경 전 값을 참조하게 됩니다.

상위 스코프는 함수 객체가 생성된 이후 정적으로 결정되며 변경되지 않습니다. 그러므로 useCallback 훅의 인수로 전달된 함수의 경우에도 해당 함수가 평가될 때의 컴포넌트 함수를 상위 스코프로 갖게 되며, 이후 컴포넌트가 재평가되어 값이 변경된 경우에도 인수로 전달한 함수는 이전 컴포넌트 함수를 상위 스코프로서 갖고 있으므로 컴포넌트의 최신값을 보장받지 못하게 됩니다.

그러므로 인수로 전달한 함수 내에서 컴포넌트 내부에 선언된 식별자를 사용중이라면 해당 식별자가 가리키는 값이 변경될 때만 함수 객체를 재생성하도록 dependencies 배열의 요소로 식별자를 추가해주어야 합니다.
이후 컴포넌트가 재평가될 때 dependencies 배열의 요소값이 이전 값과 하나라도 일치하지 않는 경우에만 함수 객체를 새롭게 생성하도록 합니다.

profile
Frontend Dev
post-custom-banner

0개의 댓글