티스토리 뷰

Pixabay로부터 입수된 Melk Hagelslag님의 이미지 입니다.

 

 팀 프로젝트를 진행하던 중, 다른 팀원분들이 useMemo, useCallback을 이용하시는 걸 보았다. 분명히 useMemo와 useCallback을 배우긴 했는데 거의 쓰질 않아서 잘 기억이 나지 않았다. 그래서 복습할 겸, 이를 기록하려고 한다.

 

1. useMemo

리액트에서 리렌더링될 때, 가끔씩 어떤 값이 변경되지 않았음에도 그 값도 덩달아 리렌더링되는 경우가 있다. 예를 들어, 다음과 같은 코드가 있다고 하자.

// App.js
import React, { useState } from "react";
import "./styles.css";
import { add } from "./add";

export default function App() {
  const [name, setName] = useState("");
  const [val1, setVal1] = useState(0);
  const [val2, setVal2] = useState(0);
  const answer = add(val1, val2);

  return (
    <div>
      <input
        className="name-input"
        placeholder="이름을 입력해주세요"
        value={name}
        type="text"
        onChange={(e) => setName(e.target.value)}
      />
      <input
        className="value-input"
        placeholder="숫자를 입력해주세요"
        value={val1}
        type="number"
        onChange={(e) => setVal1(Number(e.target.value))}
      />
      <input
        className="value-input"
        placeholder="숫자를 입력해주세요"
        value={val2}
        type="number"
        onChange={(e) => setVal2(Number(e.target.value))}
      />
      <div>{answer}</div>
    </div>
  );
}

// add.js
export const add = (num1, num2) => {
  console.log("숫자가 들어옵니다.");
  return Number(num1) + Number(num2);
};

위의 움짤은 해당 코드를 실행했을 때의 결과이다. 우선, 위의 코드는 숫자 두 개를 입력받는다. 그리고 입력한 숫자를 더한 값을 input 밑에 보여준다. 그리고 숫자가 변경될 때마다, add 함수가 실행되며 "숫자가 들어옵니다."라는 문자열이 출력된다. 숫자가 입력될 때 add 함수가 실행되는 것은 이상하지 않다. 하지만 이름을 입력할 경우, 이름뿐만이 아니라 다른 부분도 리렌더링되어 이름이 변경될 때마다 add 함수가 실행된다. 이는 이름을 입력할 때의 console을 보면 확인할 수 있다.

 하지만 이는 의도하지 않은 부분이기도 하며 성능을 저하시킬 수도 있다. 위의 코드는 간단한 코드이기에 크게 체감되지 않지만 코드 양이 방대해질 경우 문제가 될 수 있다. 그렇기에 add 함수를 숫자가 변경될 때만 불러와야 한다. 그리고 이는 useMemo Hook을 통해 해결할 수 있다. useMemo는 아래와 같이 이용할 수 있다.

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

const answer = useMemo(() => add(val1, val2), [val1, val2]);

useMemo는 연산의 결과를 기억하고 해당 값이 변경되지 않을 경우 동일한 값을 재사용한다. 이 때, 무슨 값이 변경될지는 [val1, val2]처럼 의존성 배열에 담아둔다. 이를 통해 함수의 결과를 캐싱하거나 불필요한 연산을 줄이는데 활용한다. 위의 코드에서는 answer에 add의 결과값이 저장된다. 그리고 val1, val2의 값이 변경될 때만 add 함수를 실행하고 그렇지 않을 경우에는 저장되어 있던 값을 이용한다.

관련 개념: Memoization(메모이제이션)
메모이제이션은 기존에 수행한 연산의 결과값을 메모리에 저장한 후, 동일한 입력이 들어올 경우 이를 재활용하는 기법이다. 

 

2. useCallback

 위의 useMemo는 메모이제이션 기법을 이용해 값을 재활용하는 Hook이었다. 그리고 함수를 재사용하는 Hook 역시 존재한다.

// App.js
import { useState } from "react";
import "./styles.css";
import List from "./List";

export default function App() {
  const [input, setInput] = useState(1);
  const [light, setLight] = useState(true);

  const theme = {
    backgroundColor: light ? "White" : "grey",
    color: light ? "grey" : "white"
  };

  const getItems = () => {
    return [input + 10, input + 100];
  };

  const handleChange = (event) => {
    if (Number(event.target.value)) {
      setInput(Number(event.target.value));
    }
  };

  return (
    <>
      <div style={theme} className="wall-paper">
        <input
          type="number"
          className="input"
          value={input}
          onChange={handleChange}
        />
        <button
          className={(light ? "light" : "dark") + " button"}
          onClick={() => setLight((prevLight) => !prevLight)}
        >
          {light ? "dark mode" : "light mode"}
        </button>
        <List getItems={getItems} />
      </div>
    </>
  );
}

// List.js
import { useState, useEffect, useCallback } from "react";

function List({ getItems }) {
  /* Initial state of the items */
  const [items, setItems] = useState([]);

  /* This hook sets the value of items if 
     getItems object changes */
  useEffect(() => {
    console.log("아이템을 가져옵니다.");
    setItems(getItems());
  }, [getItems]);

  /* Maps the items to a list */
  return (
    <div>
      {items.map((item) => (
        <div key={item}>{item}</div>
      ))}
    </div>
  );
}

export default List;

 위의 코드는 숫자를 입력할 경우, input 밑에 입력한 숫자 + 10, 입력한 숫자 + 100이라는 숫자 두 개를 보여주는 코드이다. 또한 다크 모드와 라이트 모드를 전환할 수 있는 기능도 존재한다. 숫자를 변경할 경우, getItems가 실행되며 방금 말한 두 개의 숫자를 반환한다. 그리고 이를 List 컴포넌트에서 렌더링하며 "아이템을 가져옵니다." 라는 문자열을 출력한다. 하지만 모드를 전환할 때도 List가 다시 렌더링된다. useMemo의 예시와 마찬가지로 의도하지 않은 사항이며 성능을 저하시킬 우려가 있다. 하지만 이번에는 값이 아닌 함수(getItems)가 문제이기에 useMemo를 적용할 수 없을 것 같다. 이 때, useCallback이라는 Hook을 이용할 수 있다.

 

import { useCallback } from "react";

const getItems = useCallback(() => { 
	return [input + 10, input + 100]; 
}, [input]);

위의 코드는 getItems에 useCallback을 적용한 코드이다. useCallback은 콜백 함수를 메모이제이션하여 불필요한 함수의 재생성을 방지한다. 그리고 의존성 배열에 콜백 함수 내에서 참조하는 변수가 변경되었을 때만 새로운 함수를 생성할 수 있다. 위의 코드에서는 input이 변경될 때만 getItems를 재생성할 것이다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함