useCallback

김민기·2022년 12월 4일
0

React

목록 보기
7/8

useMemo와 useCallback

useCallback 또한 useMemo와 같이 Memoization을 사용해서 리액트 컴포넌트 성능 최적화를 만든다는 점은 동일하다.

단 useMemo와 달리 인자로 넘겨주는 콜백 함수 그 자체를 Memoization 한다는 점이 다르다.

자바스크립트에서 함수는 객체다.

const func = (num) => {
  return num + 1;
}

이 코드는 func라는 변수에 (num) => {} 함수 객체를 할당한다는 것이다.

이전에도 얘기했듯이 리액트 컴포넌트는 렌더링 될 때마다 함수 컴포넌트를 호출하고, 함수가 호출될 때마다 내부에 있는 모든 변수들은 초기화된다. 함수 또한 객체이기 때문에 func 변수에 새롭게 생성된 함수 객체가 할당된다.

렌더링 될때마다 함수 객체가 새롭게 생성된다면 컴포넌트 성능에 악영향을 미칠 수 있다. useCallback을 사용하면 함수 객체가 새롭게 생성될 필요 없이 캐싱된 함수 객체를 그대로 사용할 수 있다.

useCallback

useCallback 또한 두 개의 인자를 전달 받는다. 첫 번째로 메모이제이션 해줄 콜백 함수를 받고 두 번째로 의존성 배열을 받는다. 의존성 배열에 있는 값이 변경될 때만 함수 객체를 새롭게 생성하고 그렇지 않다면 이전에 생성된 함수 객체를 그대로 사용한다.

function App() {
  const [number, setNumber] = useState(0);
  
  const someFunciton = useCallback(() => {
	return number;
  },[]);
  ...
}

someFunction은 처음 렌더링 될 때 생성된 함수 객체를 갖고 있다. 만약 number 가 변경되더라도 someFunction이 반환하는 number는 항상 0이 된다. 따라서 의존성 배열에 number를 추가해줘야 number가 변경될 때 새롭게 생성된 함수 객체는 변경된 number 값을 반환한다.

사용하기

Box라는 컴포넌트를 만들고 이 컴포넌트는 style 객체를 props로 전달 받는다.

// Box
import { useEffect, useState } from "react";

const Box = ({ createBoxStyle }) => {
  const [style, setStyle] = useState({});

  useEffect(() => {
    console.log("Box Size change");
    setStyle(createBoxStyle());
  }, [createBoxStyle]);

  return <div style={style} />;
};

export default Box;

Box 컴포넌트 내부에서 style 상태는 초기에는 비어있는 객체이지만 useEffect에 의해서 Prop으로 전달받는 createBoxStyle이 변경될 때 style 상태를 변경한다.

App 컴포넌트에서 Box 컴포넌트를 사용한다.

// App
import { useCallback, useState } from "react";
import Box from "./Box";

function App() {
  const [size, setSize] = useState(100);
  const [isDark, setIsDark] = useState(false);

  const createBoxStyle = useCallback(() => {
    return {
      backgroundColor: "pink",
      width: `${size}px`,
      height: `${size}px`
    };
  }, [size]);

  return (
    <div style={{ background: isDark ? "black" : "white" }}>
      <input
        type="number"
        value={size}
        onChange={(e) => setSize(e.target.value)}
      />
      <button onClick={() => setIsDark(!isDark)}>Change Theme</button>
      <Box createBoxStyle={createBoxStyle} />
    </div>
  );
}

export default App;

App 컴포넌트 내부에서 createBoxStyle 이라는 변수는 useCallback을 사용해서 size가 변경될 때만 새로운 함수 객체를 할당 받는다. 만약 useCallback을 사용하지 않는다면 size 뿐만 아니라 isDark 라는 상태가 변경되어도 createBoxStyle은 새로운 함수 객체를 할당 받는다.

useCallback을 사용했기 때문에 isDark 상태가 변경되더라도 Box 컴포넌트에 전달되는 prop인 createBoxStyle은 변경되지 않는다. 따라서 Box 컴포넌트는 다시 렌더링 되지 않게 된다.

이런 방법으로 useCallback을 사용하면 컴포넌트의 불필요한 렌더링을 막을 수 있게 되고 성능에 도움을 줄 수 있다.

마무리

useMemo와 같이 useCallback은 메모이제이션을 사용해서 컴포넌트의 성능 최적화를 이룰 수 있다는 것은 이미 알고 있었지만 실질적으로 어떻게 사용해야하는지는 잘 모르고 있었다. 또한 잘못 사용할 경우 오히려 성능에 악영향을 준다고 알고 있어서 정말로 큰 프로젝트가 아닌 이상 사용할 경우가 거의 없을거라 생각했다.

하지만 간단한 예제들을 찾아보면서 useMemo와 useCallback이 어떤 식으로 동작하는지 알게되었고 앞으로 리액트를 개발하면서 성능을 한번 더 고려할 수 있게 되었다

0개의 댓글