React Hooks - useMemo&useCallback

jisooo·2022년 9월 28일

Hook이란?
React 공식문서에서는 다음과 같이 규정하고 있다.
"Hook은 React 16.8에 새로 추가된 기능입니다. Hook은 class를 작성하지 않고도 state와 다른 React의 기능들을 사용할 수 있게 해줍니다."
즉 함수형 컴포넌트에서 상태 값 및 다른 여러 기능을 사용하기 편리하게 해주는 메소드를 의미한다. Hook은 class가 아닌 function으로만 React를 사용할 수 있기 때문에 클래스형 컴포넌트에서는 동작하지 않는다.

Hoook 사용 규칙

1. 리액트 함수의 최상위에서만 호출해야 한다.
반복분이나 조건문,중첩된 함수 내에서 Hook을 실행하면 예상한 대로 동작하지 않을 우려가 있다. useState나 useEffect 같은 Hook들이 여러 번 사용될 수 있는데, React는 이 Hook을 호출되는 순서대로 저장을 해놓게 된다.
그런데 조건문, 반복문 안에서 Hook을 호출하게 되면 호출되는 순서대로 저장을 하기 어려워지고, 결국 예기치 못한 버그를 초래하게 될 수 있다.

2. 오직 리액트 함수 내에서만 사용되어야 한다.
리액트 함수형 컴포넌트나 커스텀 Hook이 아닌 다른 일반 JavaScript 함수 안에서 호출해서는 안 된다는 의미이다.

useMemo & useCallback
React Hook은 함수 컴포넌트가 상태를 조작하고 및 최적화 기능을 사용할 수 있게끔 하는 메소드이다. 그 중 렌더링 최적화를 위한 Hook도 존재하는데, useCallback과 useMemo가 바로 그 역할을 한다.

useMemo는 특정 값(value)를 재사용하고자 할 때 사용하는 Hook이다.

/* useMemo를 사용하기 전에는 꼭 import해서 불러와야 한다. */
import { useMemo } from "react";

function Calculator({value}){

	const result = useMemo(() => calculate(value), [value]);

	return <>
      <div>
					{result}
      </div>
  </>;
}

value 는 일종의 값으로서 렌더링을 할 때마다 이 value값이 계속 바뀌는 게 아니라고 가정해보면, 이 값을 어딘가에 저장을 해뒀다가 다시 꺼내서 쓸 수만 있다면 굳이 calculate 함수를 호출할 필요도 없을 것이다. 이때 사용하는 것이 바로 useMemo이다.
위 예시와 같이 useMemo를 호출하여 calculate를 감싸주면, value값이 동일할 경우에는 이전 렌더링의 value값을 그대로 재활용할 수 있게 된다.

useCallback
useMemo는 값의 재사용을 위해 사용하는 Hook이라면, useCallback은 함수의 재사용을 위해 사용하는 Hook이다.

/* useCallback을 사용하기 전에는 꼭 import해서 불러와야 한다. */
import React, { useCallback } from "react";

function Calculator({x, y}){

	const add = useCallback(() => x + y, [x, y]);

	return <>
      <div>
					{add()}
      </div>
  </>;
}

해당 컴포넌트가 리렌더링 되더라도 그 함수가 의존하고 있는 값인 x와 y가 바뀌지 않는다고 가정할 때, 함수 또한 메모리 어딘가에 저장해 뒀다가 다시 꺼내서 쓸 수 있을 것이다.
이때 useCallback Hook을 사용하면 x와 y값이 동일하다면 다음 렌더링 때 이 함수를 다시 사용하게 된다. useCallback을 사용하는 것은 큰 의미가 없거나 오히려 손해인 경우도 있다.
그러면 언제 사용하는 게 좋을까?
자식 컴포넌트의 props로 함수를 전달해줄 때 이 useCallback을 사용하기가 좋다.

useCallback과 참조 동등성
useCallback은 참조 동등성에 의존한다. React는 기본적으로 JavaScript의 문법을 따라가고, JavaScript에서 함수는 객체이다. 객체는 메모리에 저장할 때 값을 저장하는 게 아니라 값의 주소를 저장하기 때문에, 반환하는 값이 같을 지라도 일치연산자로 비교했을 때 false가 출력된다.

function doubleFactory(){
    return (a) => 2 * a;
}
  
const double1 = doubleFactory();
const double2 = doubleFactory();
  
double1(8); // 16
double2(8); // 16
  
double1 === double2;  // fals

double1과 double2는 같은 함수를 할당했음에도 메모리 주소 값이 다르기 때문에 같다고 보지 않는다. 두개의 함수는 동일한 코드를 공유하더라도 메모리 주소가 다르기 때문에, 메모리 주소에 의한 참조 비교 시 다른 함수로 본다.
그러나 useCallback을 이용해 다시 사용하면 함수의 메모리 주소 값을 저장했다가 다시 사용한다는 것과 같다고 볼 수 있다. 따라서 React 컴포넌트 함수 내에서 다른 함수의 인자로 넘기거나 자식 컴포넌트의 prop으로 넘길 때 예상치 못한 성능 문제를 막을 수 있다.

0개의 댓글