POTATO THAT WANT TO BE HUMAN

[React]useCallback 과 useMemo 사용하기 본문

FRONTEND/React

[React]useCallback 과 useMemo 사용하기

녜힝 2022. 10. 6. 17:30
반응형

리액트에서 렌더링 성능 최적화를 위해 사용되는 hook 중에는 useCallbackuseMemo가 있다.

이에 대해 자세히 알아보기 전에 우선 리액트의 렌더링과 메모이제이션에 대해 알아보자.

 

 

🔍 리액트의 렌더링

리액트에서 컴포넌트를 리렌더링하게 되는 조건에는 3가지가 있다.

  1.  자신의 state가 변경될 때
  2. 부모 컴포넌트로부터 전달받은 props가 변경될 때
  3. 부모  컴포넌트가 리렌더링 될 때

 

컴포넌트가 렌더링된다는 것은 누군가가 그 컴포넌트를 호출해 실행되는 것을 의미한다.

컴포넌트가 실행될 때마다 내부에 선언되어 있던 변수나 함수 등의 표현식도 매번 다시 선언되어 사용된다.

 

많이 사용되는 함수형 컴포넌트는 렌더링Component 함수 호출모든 내부 변수 초기화의 순서를 거친다. 컴포넌트가 렌더링 될 때마다 초기화되는 변수나 함수가 무거운 일을 하는 함수라면 굉장히 비효율적일 것이다. 이를 막기 위해 메모이제이션을 사용한다.

 

🔍 메모이제이션

메모이제이션이란 연산의 결괏값을 메모리에 저장해두고 이전 값과 결과가 동일할 때 재사용하는 것을 말한다.

리액트에서 제공하는 메모이제이션에는 React.memo(컴포넌트), useCallback(( ) => { ··· }, [ ]), useMemo(( ) => { ··· }, [ ]) 가 있다. 그렇다면 이 메소드들은 어떤 식으로 메모이제이션을 하고 있을까?

 

React.memo() 는 props의 값으로 변경을 확인하고

useCallback()useMemo()는 dependency 배열 내부의 값으로 변경사항을 확인한다.

 

그럼 이제 본격적으로 useCallbackuseMemo에 대해 살펴보자.

 

🔍 useCallback

useCallback함수 자체를 메모이제이션 한다.

자바스크립트에서 함수는 객체이다. 때문에 리렌더링이 일어날 때마다 새로운 함수가 생성된다. 

위에서 살펴본 컴포넌트의 리렌더링 조건 3가지 중 한 가지라도 발생한다면, 해당 컴포넌트는 리렌더링이 되고 컴포넌트 내부에 선언되어 있던 변수나 함수 또한 재선언된다. 이는 전체적인 성능에도 영향을 끼치기 때문에 useCallback을 사용해 자식 컴포넌트의 불필요한 리렌더링을 막아줄 수 있다.

 

const memoizedCallback = useCallback(() => {
    doSomething(a, b);
}, [a, b])

useCallback의 기본적인 형식이다.

첫 번째 인자로는 콜백 함수를 받는다.

두 번째 인자로는 dependencies 를 받는다. dependencies는 의존성 배열로, 작성한 값이 변경되었을 때에만 함수가 실행된다. 다른말로, 이 의존성 배열의 값이 변경되지 않는다면 이전에 생성한 함수의 참조 값을 반환해준다.

 

주의할 점으로는 의존성 값의 배열이 콜백에 인자로 전달되지는 않는다는 점이다. 하지만 개념적으로 의존성 값의 배열은 콜백 함수가 무엇일지를 표현하는 방법이기 때문에, 콜백 안에서 참조되는 모든 값은 의존성 값의 배열에 나타나야 한다.

 

예시를 통해 살펴보자.

const [show, setShow] = useState(true);

const showHandler = useCallback( () => {
    	console.log(show);
        setShow(!show);
	}, [ ] )

위 함수는 

 

🔍 useMemo

useMemo함수의 리턴 값을 메모이제이션 한다.

useMemo는 처음에 계산된 결괏값을 메모리에 저장하여 컴포넌트가 반복적으로 렌더링 되어도 계산될 함수를 호출하지 않고 이전에 계산된 결과 값을 메모리에서 꺼내와서 재사용할 수 있게 한다.

 

const memoizedValue = useMemo(() => computeExpensiveValue(a,b), [a, b]);

useMemo의 기본적인 형식이다.

첫 번째 인자로는 콜백 함수를 받는다. 

두 번째 인자로는 dependencies를 받는다. 이 dependencies에도 마찬가지로 의존성 배열이 들어간다. 의존성 배열의 값이 업데이트될 때만 콜백함수를 다시 호출하여 메모이제이션된 값을 업데이트하여 다시 메모이제이션을 해준다. 다른 말로, useCallback과 마찬가지로 의존성 배열의 값이 변경되지 않는다면 메모이제이션 된 값을 사용한다.

(만약 빈 배열을 넘겨주면 맨 처음 컴포넌트가 마운트되었을 때만 값을 계산하고 이후에는 항상 메모이제이션된 값을 꺼내와 사용한다.)

 

 

🔍 언제 사용해야하는가?

렌더링 최적화를 시키기 위해서 무분별적으로 useCallbackuseMemo를 사용하는 것은 옳지않다.

모든 추상화 및 최적화 코드에는 비용이 들기 마련인데, 이때 발생하는 비용을 상쇄시킬만한 비용절약이 있지 않으면 오히려 비용이 증가하기 때문이다. 최적화 관점에서 useCallbackuseMemo를 사용하기 위해서는 전후 성능을 비교 후에 사용하는 것이 옳다.

 

간략하게 살펴보자면 다음과 같다.

  • 리렌더링이 자주 일어나지 않는다면 굳이 사용할 필요가 없다. 오히려 메모리에 불필요하게 남아있을 뿐이다.
  • props나 state가 변경되는 경우가 대부분일 경우, 굳이 비교작업이 계속될 필요가 없기 때문에 사용하지 않는다.

 

이에 대해 자세히 설명한 블로그 글의 주소이다.

https://www.zigae.com/react-memo/

 

리액트 useCallback, useMemo 언제 사용 할까?

본글은 useCallback, useMemo에 대해 설명하는 글이 아님을 알린다.

www.zigae.com

https://yceffort.kr/2022/04/best-practice-useCallback-useMemo

 

Home

yceffort

yceffort.kr

 

 

반응형
Comments