반응형

useCallback 은 useMemo와 비슷한 React-Hook이다. 값을 메모리에 저장해 두고 일반적으로 리렌더링 될 때에는 초기화되지 않고 값이 변경되는 것을 감지해서 값이 변경될 때만 함수를 실행시켜 주고 값을 업데이트해 준다.

 

차이점

useMemo(콜백함수, [ ]) 을 memoization (메모리에 저장)
useCallback(콜백함수, [ ]) 함수를 memoziation (메모리에 저장)

 

useCallback을 쓰는 이유 

 

자바스크립트에서 함수는 객체의 한 종류로 취급된다. 그래서 리렌더링 되면 함수가 초기화되면서 새로운 주소값을 할당받기 때문에 이전과 같은 모양의 함수라도 다른 함수로 취급된다. 그래서 함수를 저장하고 값이 변경되었을 때만 함수를 바꿔주는 hooks이 필요하다.

 

예제) 크기가 바뀌는 박스, 다크모드 

App.js

import { useState } from "react";
import Box from "./Box";
import "./styles.css";

export default function App() {
  const [size, setSize] = useState(100);
  const createBoxStyle = () => {
    return {
      width: `${size}px`,
      height: `${size}px`,
      backgroundColor: "turquoise"
    };
  };
  return (
    <div>
      <input
        type="number"
        value={size}
        onChange={(e) => {
          setSize(e.target.value);
        }}
      />
      <Box createBoxStyle={createBoxStyle} />
    </div>
  );
}

Box.js

import React, { useEffect, useState } from "react";

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

  useEffect(() => {
    console.log("👍👍박스 키우기");
    setStyle(createBoxStyle()); //스타일에 함수가반환하는 스타일값을 넣어줌
  }, [createBoxStyle]); //createboxstyle함수가 변경될때만 실행

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

export default Box;

이렇게 인풋태그의 숫자가 변경되면 박스의 크기가 변경되고 useEffect로 createBox함수가 바뀔 때마다 '박스 키우기'가 콘솔에 뜨게 해 줬다. 지금은 이 코드가 잘 작동하지만 상태값을 하나 더 추가하면 문제가 생긴다. 

 

App.js (dark상태값추가 버튼태그추가)

import { useState } from "react";
import Box from "./Box";
import "./styles.css";

export default function App() {
  const [size, setSize] = useState(100);
  const [dark, setDark] = useState(false);
  const createBoxStyle = () => {
    return {
      width: `${size}px`,
      height: `${size}px`,
      backgroundColor: "turquoise"
    };
  };
  const style = {
    backgroundColor: dark ? "#333" : "#fff",
    color: dark ? "#fff" : "#333"
  };
  return (
    <div style={style}>
      <div>
        <input
          type="number"
          value={size}
          onChange={(e) => {
            setSize(e.target.value);
          }}
        />
        <Box createBoxStyle={createBoxStyle} />
        <button
          onClick={() => {
            setDark(!dark);
          }}
        >
          모드변경
        </button>
      </div>
    </div>
  );
}

이렇게 dark라는 상태값을 하나 추가해 주고 버튼을 누르면 상태값이 변경된다. 모드변경모드를 누를 때마다 상관없는 박스 키우기가 콘솔에 뜨게 되는데 이유는 dark상태값이 변경되면서 컴포넌트가 리렌더링 되고 createBox함수도 리렌더링 되면서 같은 모양의 함수이지만 다른 주소를 가지게 되면서 Box컴포넌트에서 useEffect가 다른 함수라고 인식하게 되어 실행되어서 그렇다. 그래서 이를 막기 위해서는 함숫값이 변경되지 않게 해줘야 한다. 사이즈가 변경될때만 함수를 다시호출하고 변경되지않을 때는 저장되어있는 함수를 그대로 이용해서 같은주소의 함수가 보내지게 해줘야한다.

 

useCallback을 이용해서 함수를 저장해 준다.

 

App.js

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

export default function App() {
  const [size, setSize] = useState(100);
  const [dark, setDark] = useState(false);
  const createBoxStyle = useCallback(() => { //박스스타일을 만들어주는 함수를 저장 
    return {								//size상태가 변경되면 실행
      width: `${size}px`,
      height: `${size}px`,
      backgroundColor: "turquoise"
    };
  }, [size]);

  const style = {
    backgroundColor: dark ? "#333" : "#fff",
    color: dark ? "#fff" : "#333"
  };
  return (
    <div style={style}>
      <div>
        <input
          type="number"
          value={size}
          onChange={(e) => {
            setSize(e.target.value);
          }}
        />
        <Box createBoxStyle={createBoxStyle} />
        <button
          onClick={() => {
            setDark(!dark);
          }}
        >
          모드변경
        </button>
      </div>
    </div>
  );
}

이렇게 써주면 useCallback이 boxstyle을 주던 함수를 저장하게 되고 size가 변경될 때만 함수가 다시 만들어진다.

사이즈가 변경되지 않았을 때는 원래 있던 함수를 box컴포넌트에 보내주고 주소가 이전과 같기 때문에 useEffect에서도 다시 실행되지 않는다.

 

 

저번 useMemo와 같이 useCallback도 값을 저장해 뒀다가 쓰는 방식이기 때문에 많이 쓰면 자원을 낭비하게 된다. 꼭 필요할 때 적절히 쓸 수 있도록 해야 한다. 아직은 언제 어떤 방식으로 써야 적절하게 쓰는 건지 감이 잘 오지는 않는다. 나중에 포트폴리오를 하면서 쓰다가 보면 감이 오겠지.

반응형

+ Recent posts