본문 바로가기
개발 공부/React

[React] useMemo, useCallback는 언제 써야할까? useEffect vs useMemo, useCallback

by 호랭개발자 2022. 5. 8.
반응형

프로그램의 사이즈가 커질수록 성능은 점점 더 중요해진다. 이러한 성능을 위해 React에서 사용하는 것이 useMemo, useCallback이다.

👀 useMemo란?

useMemo는 memoization된 값을 반환한다. 

useMemo(() => callbackFn, [dependencies])

useMemo는 함수(callbackFn)를 호출하고 이 함수에서 리턴된 값을 리턴한다. useMemo를 호출할 때마다 dependencies를 먼저 확인한다. 이때 dependencies에서 바뀐 값이 없다면 캐싱된 값(이미 계산된 값)을 리턴하고, 바뀐 값이 존재한다면 다시 함수를 호출하고 새롭게 연산된 값을 리턴한다.

만약 useMemo에서 dependencies 배열이 없다면 매 렌더링마다 콜백 함수가 실행된다.

useMemo와 useEffect의 차이점은?

useMemo는 함수와 dependencies 배열을 가진다는 점에서 useEffect와 비슷하다. 그러나 useEffect는 side-effect를 위한 훅인 것에 비해 useMemo는 함수 내에서 side-effect가 존재하면 안 된다. 즉, useMemo의 콜백 함수는 순수 함수여야 한다.

👀 useCallback이란?

useCallback은 memoization된 함수를 반환한다.

useCallback(() => callbackFn, [dependencies])

useCallback은 함수(callbackFn)를 memoization하고 이 함수를 리턴한다. useCallback을 호출할 때마다 dependencies를 먼저 확인한다. 의존성이 변경된 경우에만 memoization된 함수를 변경한다. 

useMemo와 useCallback은 값을 저장하느냐 함수를 저장하느냐의 차이이고 동작하는 원리는 같다.

👀 useMemo와 useCallback은 왜 필요할까?

하위 컴포넌트에 함수가 inline으로 전달되거나, 컴포넌트 내에서 함수가 생성되는 경우에는 리렌더링이 발생할 때마다 연산이 발생하게 된다. 따라서 불필요한 연산을 줄이기 위해 useMemo와 useCallback을 사용한다.

export function MyComponent({a, b, c, d}){
  const added = a+b
  const subtracted = c-d
 
  return (
    <div>
      <span> {`더한 숫자 : ${added}`} </span>
      <span> {`뺀 숫자 : ${subtracted}`} </span>
    </div>
  )
}

위와 같은 컴포넌트가 있다고 하자. MyComponent에 전달되는 c의 값이 바뀌면 리렌더링이 일어나므로 added와 subtracted는 모두 다시 계산될 것이다. 그런데 여기서 added의 경우 a, b의 값이 바뀌지 않았으므로 다시 계산이 발생하지 않는 것이 효율적이다. 따라서 useMemo를 사용하여 아래와 같이 작성할 수 있다.

import {useMemo} from 'react'

export function MyComponent({a, b, c, d}){
  const added = useMemo(()=> a+b, [a, b])
  const subtracted = useMemo(()=> c-d, [c,d])
 
  return (
    <div>
      <span> {`더한 숫자 : ${added}`} </span>
      <span> {`뺀 숫자 : ${subtracted}`} </span>
    </div>
  )
}

added와 subtracted를 useMemo를 사용하여 값을 memoization 해주었다. 이제 added는 a나 b의 값이 변경되었을 때, subtracted는 c와 d의 값이 변경되었을 때에만 다시 계산된다. 

👀 useMemo와 useCallback은 언제 써야할까?

useMemo가 프로그램의 성능을 높여준다고 해서 항상 사용하는 것이 능사는 아니다. useMemo와 useCallback는 보통 expensive한 연산이 필요한 경우 사용된다고 한다.

Performance optimizations are not free. They ALWAYS come with a cost but do NOT always come with a benefit to offset that cost.

By. Kent C. Dodds

모든 연산에는 비용이 든다. useMemo와 useCallback을 사용할 때 '비용이 든다'는 것은 값 또는 함수를 기억하기 위해 추가적인 메모리가 필요하고, dependencies가 변경되었는지 확인하는 연산이 필요하다는 것 등을 말한다. 

아래의 글에서는 `Referentialy equality`와 `Computationally expensive calcuations` 가 useMemo와 useCallback을 사용하기 위한 두 가지 조건이라고 말한다.

https://kentcdodds.com/blog/usememo-and-usecallback

 

When to useMemo and useCallback

Performance optimizations ALWAYS come with a cost but do NOT always come with a benefit. Let's talk about the costs and benefits of useMemo and useCallback.

kentcdodds.com

위에서 들었던 예시의 경우, 단순히 덧셈과 뺄셈 연산이 전부이다. 따라서 useMemo를 사용하면서 부담해야 할 비용과 연산을 함으로써 부담해야할 비용을 비교할 경우 연산을 하는 데 드는 비용이 더 적다고 판단할 수 있다. 이런 경우에는 사실 useMemo를 사용하기보다는 그냥 연산을 진행하는 것이 더 효율적이라고 볼 수 있다.

이와 같이 성능 최적화를 위해서 useMemo와 useCallback을 사용할 때에는 비용을 잘 따져보고 상황에 맞게 선택적으로 사용해야 한다.

반응형

댓글