본문 바로가기
  • 하고 싶은 일을 하자
dev

useCallback 심도있게 살펴보기

by 박빵떡 2023. 11. 9.
반응형

useCallback 은 함수를 cache 로 저장 해서 re-render 할 때 사용할 수 있도록 하는 React Hook 이다.

아 슈밤 글 다썼는데 코드로 테스트하다가 다 날라갔다... ㅠㅠ

loop 3중첩으로 1000*1000*1000 으로 돌리니까 브라우저 바로 뻑나네.............

 

ProductPage가 리렌더링 되면 handleSubmit 함수는 새롭게 생성될까?

아니면 동일한 로직의 함수이니 알아서 이전 렌더링 때 만든 handleSubmit 함수를 그대로 사용할까?

 

정답은 함수를 새롭게 생성한다. 자바스크립트에서 function () {} 혹은 () => {} 인 경우에는 함수를 새로 생성하기 때문이다.

 

이런식으로 함수를 useCallback 으로 감싸고 dependency array 에 react value 들을 넣어주면 된다.

그러면 dependecy array 에 있는 value 들이 바뀔 때만 handleSubmit 을 재생성하고

바뀌지 않을 때는 이전 렌더링 때 생성한 handleSubmit 을 그대로 사용하여 최적화가 이루어진다.

 

위의 예시에서 todos 가 dependency array 에 있다.

setTodos 에서 todos 를 사용하기 때문이다.

그래서 handleAddTodo 가 수행되면 todos 가 변경되고, handleAddTodo 도 재생성된다.

 

위의 코드를 updater function 을 사용해 개선할 수 있다.

setTodos(todos => [...todos, newTodo]); 의 todos는 state 의 todos 가 아니다.

setTodos(prevState => [...prevState, newTodo]); 로 바꿀 수 있다.

즉 마치 함수형 프로그래밍을 하듯 함수로 바꿔주면

handleAddTodo 내에서 todos 를 사용하지 않게 되고

dependency 에서 todos 를 제거할 수 있다.

따라서 handleAddTodo 가 실행되어 todos 가 변경되어도 handleAddTodo 는 재생성하지 않게 되고

기존의 기능은 그대로 수행하기 때문에 더 좋은 최적화를 할 수 있다.

(즉, dependecy array 에는 최대한 적은 value 들이 있는게 좋다.)

 

공식 문서에는 없는 또다른 방법

import { useCallback } from 'react';
import ShippingForm from './ShippingForm.js';

function handleSubmit(orderDetails, productId, referrer) {
  post('/product/' + productId + '/buy', {
    referrer,
    orderDetails,
  });
}

export default function ProductPage({ productId, referrer, theme }) {
  return (
    <div className={theme}>
      <ShippingForm onSubmit={handleSubmit} productId={productId} referrer={referrer} />
    </div>
  );
}

function post(url, data) {
  // Imagine this sends a request...
  console.log('POST /' + url);
  console.log(data);
}

위의 코드처럼 handleSubmit 을 ProductPage 외부로 빼는 방법 가능하다.

이 경우 ProductPage가 리렌더링 되더라도 handleSubmit 은 재생성하지 않는다.

다만 ShippingForm 에 productId 와 referrer 를 전달하고 있는데

props drilling 이 발생하는 단점이 있겠다.

 

공식 문서에는 없지만 하나를 추리할 수 있다.

useCallback을 이용해 함수를 재생성하는 것을 막아 최적화를 할 수 있다 -> 즉, 함수를 생성하는 것은 오버헤드이다.

위의 예시는 간단한 함수라 useCallback 을 쓰냐 안쓰냐에 따라 큰 차이가 없겠지만

극단적인 예로 10000줄 짜리 함수라면 성능을 훨씬 좋게 만들 수 있을 것이다.

(물론 그런 함수는 만들지 않아야 겠지만)

 

useCallback을 많이 사용하면 브라우저 캐시나 메모리를 많이 사용하게 되진 않을까?

useCallback이 함수를 cache 에 저장하니까 useCallback을 마구 남발하면 그만큼 브라우저의 메모리를 많이 사용하게 되니까 성능에 악영향을 미치진 않을까

그런데 이와 관련한 내용은 공식 문서에 없었다.

크게 상관은 없는 것 같다.

 

회사 코드에 useCallback 적용하기

회사에 "릴레이 특가" 페이지에 1초마다 남은 시간이 1초씩 줄어드는 코드가 있었다.

함수가 복잡하진 않았지만 1초마다 실행되기 때문에

자주 실행된다고 생각하여 useCallback 을 사용해 최적화를 해줬다.

 

React.dev 문서에 따르면, 느려지는 부분이 느껴진다면 React Developer Tools의 profiler 로 확인하여 useCallback 이나 memo 를 활용하라고 적혀있다. 추후에도 이 툴을 이용해 최적화 작업 진행 해보려고 한다.

 

반응형

댓글