새소식

프론트엔드 공부/React

useMemo & useCallback

  • -

React는 사용자 인터페이스 구축에 중점을 둔 JavaScript 라이브러리입니다. React의 주요 기능 중 하나는 불필요한 재렌더링을 줄여 렌더링 성능을 최적화하는 기능입니다. React는 메모이제이션이라는 프로세스를 통해 이를 달성합니다. 메모이제이션은 값비싼 함수 호출의 결과를 캐싱하고 동일한 입력이 다시 발생할 때 캐싱된 결과를 반환하는 기술입니다.

메모이제이션은 입력을 기반으로 함수의 결과를 캐싱하는 프로세스입니다. 동일한 입력이 함수에 다시 전달되면 함수를 다시 계산하는 대신 캐시된 결과가 반환됩니다. useMemo를 사용하면 함수의 결과를 메모하고 종속성이 변경될 때만 다시 계산할 수 있습니다


useMemo 이란?

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

useMemo는 함수와 종속성 배열이라는 두 가지 인수를 사용합니다. 함수는 메모이제이션하려는 비용이 많이 드는 작업을 나타내고 종속성 배열은 함수가 의존하는 입력을 나타냅니다.

프로그래밍 맥락에서 비용이 높다라는 말은 일반적으로 실행하는 데 상당한 양의 계산 리소스(예: 시간 또는 메모리)가 필요한 작업 또는 기능을 나타냅니다. 즉, 비용이 많이 드는 작업은 완료하는 데 오랜 시간이 걸리거나 많은 리소스를 소비하여 애플리케이션의 성능에 부정적인 영향을 줄 수 있는 작업입니다.

예를 들어 복잡한 계산을 수행하거나 네트워크 요청을 하는 함수가 있는 경우 비용이 많이 드는 작업으로 간주될 수 있습니다. useMemo를 사용하여 이 기능을 메모하면 불필요한 기능 재실행을 줄여 애플리케이션의 성능을 향상시킬 수 있습니다.

const cachedValue = useMemo(calculateValue, dependencies)
  • CalculateValue:
    캐시할 값을 계산하는 함수입니다. 이 값은 순수해야 하며 인수를 사용하지 않아야 하며 모든 유형의 값을 반환해야 합니다. React는 초기 렌더 중에 함수를 호출합니다. 다음 렌더링 시 React는 마지막 렌더링 이후 종속성이 변경되지 않은 경우 동일한 값을 다시 반환합니다. 그렇지 않으면 calculateValue를 호출하고 결과를 반환한 다음 나중에 다시 사용할 수 있도록 저장합니다.
  • dependencies(종속성):
    calculateValue 코드 내에서 참조되는 모든 반응 값의 목록입니다. 반응 값에는 요소, 상태 및 구성 요소 본체 내에 직접 선언된 모든 변수와 함수가 포함됩니다. 링터가 React용으로 구성된 경우 모든 반응 값이 종속성으로 올바르게 지정되었는지 확인합니다. 종속성 목록은 항목 수가 일정해야 하며 [dep1,dep2,dep3]와 같이 인라인으로 작성되어야 합니다. React는 Object.is 비교를 사용하여 각 종속성을 이전 값과 비교합니다.

useMemo가 호출되면 제공된 함수를 실행하고 결과를 캐시합니다. 동일한 입력이 다시 발생하면 useMemo는 함수를 다시 실행하는 대신 캐시된 결과를 반환합니다. 이를 통해 불필요한 재렌더링을 줄이고 React 구성 요소의 성능을 향상시킬 수 있습니다.


실습하기

아래 컴포넌트에서 실제로 연산 로직에 영향을 주는 값은 val1과 val2이다. 현재는 이름 상태가 변화하면 add 함수가 계속 같은 결괏값을 리턴함에도 불구하고 불필요하게 계속 호출되고 있기 때문에, useMemo를 이용하여 add 함수의 호출을 최소화할 수 있습니다. 이름을 입력할 때는 add 함수가 호출되지 않아야 최적화가 된 컴포넌트라고 볼 수 있습니다.

// const answer = add(val1, val2); 이 부분을 수정해보기
  const answer = useMemo(() => add(val1, val2), [val1, val2]);

위 실습 코드중 useMemo를 사용하면 콘솔로그에서 기존과 다르게 이름을 수정해도 찍히지 않는것을 볼 수 있습니다.


useCallback 이란?

useCallback은 특정 함수를 새로 만들지 않고 재사용하고 싶을 때 사용합니다. useCallback Hook을 사용하면 그 함수가 의존하는 값들이 바뀌지 않는 한 기존 함수를 계속해서 반환합니다.

React 공식 문서에서는 useCallback을 다음과 같이 말하고 있습니다.

메모제이션된 함수를 반한하는 하는 함수입니다.

인라인 콜백과 그것의 의존성 값의 배열을 전달하세요. useCallback은 콜백의 메모이제이션된 버전을 반환할 것입니다. 그 메모이제이션된 버전은 콜백의 의존성이 변경되었을 때에만 변경됩니다. 이것은, 불필요한 렌더링을 방지하기 위해 (예로 shouldComponentUpdate를 사용하여) 참조의 동일성에 의존적인 최적화된 자식 컴포넌트에 콜백을 전달할 때 유용합니다.

useCallback의 장점:

  • 불필요한 재렌더링을 피함으로써 앱의 성능을 향상시킵니다.
  • 종속성이 변경될 때만 변경되도록 함수를 메모화하여 함수 생성 횟수를 줄이고 성능을 향상시킵니다.
  • 렌더링할 때마다 함수가 재생성되지 않도록 하여 무한 루프를 방지하는 데 도움이 됩니다.

useCallback이 필요한 상황:

  • 함수를 하위 구성 요소에 prop으로 전달하면 해당 함수가 상위 구성 요소 내부에 생성됩니다. 이 경우 useCallback을 사용하여 함수를 메모화하면 자식 구성 요소의 불필요한 재렌더링을 피할 수 있습니다.
  • 함수가 useEffect 또는 useMemo와 같은 다른 후크에 대한 종속성으로 사용되며 렌더링할 때마다 함수가 변경되지 않도록 하려는 경우. 이 경우 useCallback을 사용하여 함수를 메모화하면 앱의 성능을 향상시킬 수 있습니다.

실습하기

// const getItems = () => {
  //   return [input + 10, input + 100];
  // };

  const getItems = useCallback(() => [input + 10, input + 100], [input]);

위 코드에서, useCallback을 사용하여 getItems 함수를 다시 작성하였습니다. useCallback은 두 번째 매개변수로 전달받은 배열이 변경될 때만 함수를 새로 생성하기 때문에, input 상태값이 변경될 때만 getItems 함수가 새로 생성됩니다. 이렇게 함으로써, 컴포넌트 성능을 최적화할 수 있습니다. 이로써 콘솔로그에서 인풋상태 변경 될때만 콘솔이 찍히고 다크모드변경 버튼을 눌러도 콘솔에는 나오지 않습니다.


참조동등성이란?

JavaScript에는 값 동등성과 참조 동등성이라는 두 가지 유형의 동등성이 있습니다. 값 동등성은 두 값이 동일한 값을 갖는 경우 동일함을 의미합니다. 예를 들어 '1'과 '1'은 같은 값입니다. 참조 동등성은 두 값이 메모리에서 동일한 개체를 참조하는 경우에만 같음을 의미합니다. 예를 들어 동일한 속성을 가진 두 개체를 만드는 경우 메모리에서 별도의 개체이기 때문에 참조가 동일하지 않습니다.

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

React에서 useCallback은 참조 동등성을 사용하여 메모된 함수를 다시 계산해야 하는지 여부를 결정합니다. useCallback을 호출하면 함수와 종속성 배열을 전달합니다. 종속성이 변경되면 useCallback은 새 함수 객체를 반환합니다. 종속성이 변경되지 않으면 useCallback은 이전과 동일한 함수 개체를 반환합니다.

import React, { useCallback, useState } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);

  // This function will be memoized using useCallback
  const handleClick = useCallback(() => {
    console.log('Clicked!');
  }, []);

  console.log(handleClick); // Logs: function handleClick() {console.log('Clicked!');}

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

예에서는 handleClick 함수를 메모하기 위해 useCallback을 사용했습니다. 지정된 종속성이 없기 때문에 함수는 한 번만 생성된 다음 후속 렌더링에서 재사용됩니다. handleClick의 값을 기록하면 모든 렌더링에서 동일한 함수 개체를 반환하는 것을 볼 수 있습니다. 이는 함수를 다시 만들어야 하는지 여부를 결정하는 데 참조동등성이 사용되기 때문입니다.


useCallback은 useMemo보다 최적화에 좋은가?

useCallback과 useMemo 모두 성능을 최적화하는 데 사용될 수 있지만, 각각의 목적이 다릅니다.

useCallback은 함수 인스턴스를 메모이제이션하여 불필요한 재렌더링을 발생시키지 않고 자식 컴포넌트로 전달할 수 있습니다. 이는 계산 비용이 많이 드는 함수를 사용하여 자식 컴포넌트의 프롭으로 사용할 때 유용합니다. useCallback을 사용하면 함수가 의존성이 변경될 때만 재계산되도록 보장할 수 있습니다.

반면에 useMemo는 값을 메모이제이션하여 다시 계산하지 않고 캐시하고 반환할 수 있습니다. 이는 컴포넌트 내에서 여러 곳에서 사용되는 계산 비용이 많이 드는 값이 있는 경우 유용합니다. useMemo를 사용하면 값이 의존성이 변경될 때만 재계산되도록 보장할 수 있습니다.

일반적으로, 자식 컴포넌트의 프롭으로 사용되는 함수를 다룰 때는 useCallback을 사용해야 합니다. 컴포넌트 내에서 여러 번 사용되는 값에 대해서는 useMemo를 사용해야 합니다. 그러나 이는 단순한 지침일 뿐이며, 때에 따라 useCallback과 useMemo을 함께 사용해야 하는 경우가 있습니다.


 

useCallback – React

The library for web and native user interfaces

react.dev

 

useCallback – React

The library for web and native user interfaces

react.dev

 

'프론트엔드 공부 > React' 카테고리의 다른 글

[React snippets] 컴포넌트 종류별 단축키(숏컷)  (0) 2023.04.14
Custom Hooks  (0) 2023.03.23
Redux Toolkit  (0) 2023.02.27
[React] 상태 관리 종합퀴즈  (0) 2023.02.27
Redux  (0) 2023.02.24
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.