[최적화] - useCallback

Donggu(oo)·2023년 1월 20일
0

React

목록 보기
22/30
post-thumbnail

1. useCallback


  • useCallback은 useMemo를 조금 더 편리하게 사용할 수 있도록 만든 버전이다. 일반적인 값들은 useMemo를 통해서 메모하기 편리하다. 하지만 함수의 경우에는 useMemo를 사용해서 메모하게 되면 콜백 함수에서 또다른 함수를 리턴하는 형태가 되게 된다. 이는 동작상에는 이상이 없지만 코드 스타일에 따라 문법적으로 다소 보기가 불편해지는 단점이 있다. 따라서 이러한 동작을 간소화한 useCallback이란 함수를 제공하고 있다.

  • useCallback은 두 가지 인자를 받는데, 첫 번째 인자는 콜백 함수이며 값을 저장하는 useMemo와 달리 콜백 함수 자체를 메모이제이션 한다. 그리고 두 번째 인자로는 의존성 배열을 받는다. 두 번째 인자에 빈 배열([])을 전달하면 첫 렌더링시에만 메모이제이션을 수행하고 이후에는 항상 메모이제이션 된 콜백 함수를 꺼내와서 사용한다.

const memorizedFunction = useMemo(() => () => console.log("Hello World"), []);

const memorizedFunction = useCallback(() => console.log("Hello World"), []);
  • 두 번째 인자에 값(value)을 전달하면 배열안의 요소의 콜백 함수가 업데이트 될 때마다 콜백 함수를 다시 호출해서 메모이제이션 된 콜백 함수를 업데이트 한다.
  • 그러나 useCallback만 사용해서는 useMemo의 효과에 비해 최적화를 느낄 수는 없다. 왜냐하면 useCallback은 함수를 호출 하지 않는 Hook이 아니라, 메모리 어딘가에 함수를 꺼내서 호출하는 Hook이기 때문이다.

  • 따라서 단순히 컴포넌트 내에서 함수를 반복해서 생성하지 않기 위해서 useCallback을 사용하는 것은 큰 의미가 없거나 오히려 손해인 경우도 있다. 그래서 useCallback은 자식 컴포넌트의 props로 함수를 전달해줄 때 유용하다.

2. useCallback의 적용


  • 아래의 예시에서 Calculator 컴포넌트 내에는 add라는 함수가 선언이 되어 있는 상태다. 이 add 함수는 props로 넘어온 xy 값을 더해 <div> 태그에 값을 출력하고 있다. 이 함수는 해당 컴포넌트가 렌더링 될 때마다 새롭게 만들어질 것이다.
function Calculator({ x, y }) {

  const add = () => x + y;

  return (
    <>
      <div>
        {add()}
      </div>
    </>
  );
}
  • useMemo와 마찬가지로, 해당 컴포넌트가 리렌더링 되더라도 그 함수가 의존하고 있는 값인 xy가 바뀌지 않는다면, 함수 또한 메모리 어딘가에 저장해 뒀다가 다시 꺼내서 쓸 수 있을 것이다.

  • 이때, 아래와 같이 useCallback을 사용하면 그 함수가 의존하는 값들이 바뀌지 않는 한 기존 함수를 계속해서 반환한다. 즉 xy값이 동일하다면 다음 렌더링 때 이 함수를 다시 사용한다.

import React, { useCallback } from "react";

function Calculator({ x, y }) {

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

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

3. useCallback과 참조 동등성


  • useCallback은 참조 동등성에 의존한다. React는 JavaScript 언어로 만들어진 오픈소스 라이브러리이기 때문에 기본적으로 JavaScript의 문법을 따라간다.

  • JavaScript에서 함수는 객체다. 객체는 메모리에 저장할 때 값을 저장하는 게 아니라 값의 주소를 저장하기 때문에, 반환하는 값이 같을 지라도 일치연산자로 비교했을 때 false가 출력된다.

function doubleFactory(){
    return (a) => 2 * a;
}
  
const double1 = doubleFactory();
const double2 = doubleFactory();
  
double1(8); // 16
double2(8); // 16
  
double1 === double2;  // false
double1 === double1;  // true
  • double1double2는 같은 함수를 할당했음에도 메모리 주소 값이 다르기 때문에 같다고 보지 않는다. JavaScript에서 함수는 객체다. 따라서 두개의 함수는 동일한 코드를 공유하더라도 메모리 주소가 다르기 때문에, 메모리 주소에 의한 참조 비교 시 다른 함수로 본다.

  • 이는 React 또한 같다. React는 리렌더링 시 함수를 새로 만들어서 호출을 한다. 새로 만들어 호출된 함수는 기존의 함수와 같은 함수가 아니다. 그러나 useCallback을 이용해 함수 자체를 저장해서 다시 사용하면 함수의 메모리 주소 값을 저장했다가 다시 사용한다는 것과 같다고 볼 수 있다. 따라서 React 컴포넌트 함수 내에서 다른 함수의 인자로 넘기거나 자식 컴포넌트의 prop으로 넘길 때 예상치 못한 성능 문제를 막을 수 있다.

0개의 댓글