카테고리 없음

[React] React에서의 최적화 2: useCallback 훅 정리 (feat. 리랜더링 방지)

모찌바라기 2024. 4. 13. 13:39
728x90
반응형

 

 

 

useCallback이란?

 

useCallback은 리렌더링 간에 함수 정의를 캐싱해 주는 React Hook입니다. -- 리액트 공식문서

 

결론만 말하면 함수가 리랜더링 되는 것을 방지해주는 훅 중 하나이다..

 

 

레퍼런스

const cachedFn = useCallback(fn, dependencies)

 

 

매개변수 

  • fn: 캐싱할 함숫값입니다. 이 함수는 어떤 인자나 반환값도 가질 수 있습니다. React는 첫 렌더링에서
    이 함수를 반환합니다. (호출하는 것이 아닙니다!) 다음 렌더링에서 dependencies 값이 이전과 같다면
    React는 같은 함수를 다시 반환합니다. 반대로 dependencies 값이 변경되었다면 이번 렌더링에서
    전달한 함수를 반환하고 나중에 재사용할 수 있도록 이를 저장합니다. React는 함수를 호출하지 않습니다.
    이 함수는 호출 여부와 호출 시점을 개발자가 결정할 수 있도록 반환됩니다.

 

  • dependencies: fn 내에서 참조되는 모든 반응형 값의 목록입니다. 반응형 값은 props와 state, 그리고 컴포넌트 안에서 직접 선언된 모든 변수와 함수를 포함합니다. 린터가 React를 위한 설정으로 구성되어 있다면 모든 반응형 값이 의존성으로 올바르게 명시되어 있는지 검증합니다. 의존성 목록은 항목 수가 일정해야 하며 [dep1, dep2, dep3]처럼 인라인으로 작성해야 합니다. React는 Object.is 비교 알고리즘을 이용해 각 의존성을 이전 값과 비교합니다.

 

useCallback 훅 사용방법

 

useCallback 사용법은 간단하다. 그냥 리랜더링을 방지해주고 싶은 함수를 첫번째 인자로 넣어주면 된다..

 

 

일반함수를 useCallback훅으로 감싸는 방법

// useCallback 훅을 적용하기 전 함수
const changeNumber = () => {
   setNumber((number) => number + 1);
};

// useCallback 훅을 적용한 함수
const changeNumber = useCallback(() => {
    setNumber((number) => number + 1);
}, []);

 

위 코드를 보면 정말 간단하다. 다른 훅들처럼 그냥 첫번 째 인자값을 넣기만 하면 된다.

두번째 인자로는 빈 배열을 담아주었다. 즉 의존성 배열이 없다는 말이고, 첫 마운트시에만 랜더링 하겠다는 의미이다..

조금 더 자세히 보자면..

 

 

첫 마운트시에만 함수가 랜더링하고 싶은 경우

const changeNumber = useCallback(() => {
    setNumber((number) => number + 1);
}, []); //의존성 배열을 빈배열로 넣어줌..

 

위에서도 이야기 했지만, 의존성 배열을 비워두면 이 녀석은 첫 마운트시에만 랜더링한다..

useEffect 훅과 맥락은 비슷하다.. (useEffect도 의존성 배열이 빈배열인 경우에 첫마운트시에만 콜백함수 호출..)

 

 

함수가 변경 되는 경우에 재랜더링 하고 싶은 경우

const changeNumber = useCallback(() => {
    setNumber((number) => number + 1);
}, [number]);

 

위 코드에서는 의존성 배열에 number라는 state를 넣어주었다. 이제 changeNumber라는 함수는

number라는 state를 의존하고 있게 되고, 이 number라는 state가 변경이 되면 함수가 재랜더링 되게 된다..

 

 

 

샘플코드

 

샘플코드1

import { useState, useCallback } from "react";

const App = () => {
  const [number, setNumber] = useState(0);

  const changeNumber = useCallback(() => {
    setNumber((number) => number + 1);
  }, []);

  return (
    <>
      <div className="App">
        <h1>{number}</h1>
        <button onClick={changeNumber}>+</button>
      </div>
    </>
  );
};

export default App;

 

위처럼 코드를 작성하면 첫 마운트시에만 함수가 랜더링 되므로, 컴포넌트가 변경되더라도 함수는 랜더링 되지 않는다..

 

 

어떤 경우에 useCallback 훅을 사용할까?

 

위의 내용을 보면 함수 재랜더링 방지를 하는 경우에 useCallBack 훅을 사용한다고 했는데,

그런 이야기 말고 실제 개발할 때 어떤 경우에 많이 사용할까?

 

콜백함수를 자식컴포넌트의 props로 넘길 때, useCallback을 사용하는 게 일반적이지 않을까 싶다..

 

이전 react.memo에서도 이야기를 했지만, react.memo는 props를 비교할 때, 얕은 비교를 하기 때문에

함수와 같은 참조타입의 데이터는 부모 컴포넌트가 변경 될 때, 새로운 참조 데이터를 가지기 때문에

 

무조건 자식 컴포넌트가 리랜더링 된다고 했다.. 

 

그렇기 때문에 부모컴포넌트가 리랜더링 되더라도 함수가 랜더링 되지 않도록 useCallback 훅으로 감싼

함수를 props로 자식 컴포넌트에 넘겨주면, 설령 부모컴포넌트가 랜더링 되고, 콜백함수를 자식 컴포넌트에 

넘긴다 하더라도 자식 컴포넌트는 리랜더링 되지 않는다..

 

샘플코드

const ChildComponent = React.memo(({ onClick }) => {
  console.log("ChildComponent rendered");
  return <button onClick={onClick}>Click me</button>;
});

const ParentComponent = () => {
  const handleClick = useCallback(() => {
    console.log("Button clicked");
  }, []);

  console.log("ParentComponent rendered");

  return <ChildComponent onClick={handleClick} />;
};

 

 

결론 : 복잡한 로직을 가진 함수를 props로 자식컴포넌트에 넘기는 경우에 useCallback 훅을 사용한다..

 

 

단 너무 남용을 하는 것도 좋지 않다.. 단순 UI를 그려주는 단순 컴포넌트의 경우에는 오히려

useCallback이나 memo를사용하면 성능이 더 떨어질 수 있다..

 

훅 안에서 연산을 하기 때문에... 안의 로직이 많은 함수나 컴포넌트의 경우에만사용하도록 하자...

 

 

 

728x90
반응형