useMemo 와 useCallback에 대해서 알아보자

이인송·2021년 5월 28일
2

TodyaILearned : React

목록 보기
1/1
post-thumbnail

Index

  1. 들어가기 전에
  2. 왜 useMemo 를 사용할까?
  3. 예제 코드
  4. 예제에 useMemo 적용해보기
  5. 왜 useCallback 을 사용할까?
  6. 예제에 useMemo 적용해보기
  7. Reference

1. 들어가기 전에

2. 왜 useMemo 를 사용할까?


useMemo 는 메모이제이션된 값을 반환하게 합니다.
useEffect 와 유사하게 useMemo 는 의존성이 변경되었을 때에만 메모이제이션된 값만 다시 계산합니다.

한 가지 예시를 들어봅시다.
하위 컴포넌트가 상위 컴포넌트로부터 a 와 b 라는 두 개의 props 를 전달받습니다.
하위 컴포넌트는 이를 서로 다른 함수로 각각의 값을 가공한 새로운 값을 보여주는 역할을 합니다.
하위 컴포넌트는 props로 넘겨 받는 인자가 하나라도 변경될 때마다 렌더링되는데, 만약 props.a 만 변경 되었을 때 이전과 같은 값인 props.b도 다시 함수를 호출해서 재계산한다면 비효율적입니다.
useMemo 는 이러한 비효율성을 방지하기 위해, 변경되지 않은 것에 대해서는 메모이제이션된 값을 반환합니다.

하지만, 하위 컴포넌트내에서 각각의 값을 가공하는 함수를 처리하는 로직이 복잡하지 않다면 오히려 useMemo 를 사용하는 것이 더욱 비효율적일 수 있습니다.

3. 예제 코드

다음 예시는 다음과 같은 구조를 가집니다.

📦public
 ┗ 📜index.html
📦src
 ┣ 📜App.js
 ┣ 📂components
 ┃ ┗ 📜Info.js 
 ┃ 	 ┣ 📜GetColor.js 
 ┃   ┗ 📜GetDay.js
 ┗ 📜index.js

Info 컴포넌트는 GetColor 와 GetDay라는 하위 컴포넌트를 가집니다.
GetColor 는 영어로 색깔을 선택했을 때, 이를 한국어로 번역해서 반환해주는 컴포넌트입니다.
GetDay 는 영어로 요일을 선택했을 때, 이를 한국어로 번역해서 반환해주는 컴포넌트입니다.

먼저, useMemo 와 useCallback 을 사용하지 않고 사용해봅시다.
📌 와 🔍 이 체크된 부분은 코드가 곧 바뀔 곳입니다. 참고해주세요 :)

App.js
import React, { useState, useEffect, useMemo } from "react";
import Info from "./Info";

const App = () => {
  const [color, setColor] = useState("");
  const [season, setSeason] = useState("");
  
  🔍  🔍  🔍
  const onChangeHandler = (e) => {
    if (e.target.id === "color") setColor(e.target.value);
    else setSeason(e.target.value);
  };

  return (
    <div className='App'>
      <div>
        <label>
          What is your favorite color of rainbow ?
          <input id='color' value={color} onChange={onChangeHandler} />
        </label>
      </div>
      <div>
        What is your Season ?
        <label>
          <input
            type='radio'
            name='season'
            value='Spring'
            onChange={onChangeHandler}
          />
          Spring
        </label>
        <label>
          <input
            type='radio'
            name='season'
            value='Summer'
            onChange={onChangeHandler}
          />
          Summer
        </label>
        <label>
          <input
            type='radio'
            name='season'
            value='Fall'
            onChange={onChangeHandler}
          />
          Fall
        </label>
        <label>
          <input
            type='radio'
            name='season'
            value='Winter'
            onChange={onChangeHandler}
          />
          Winter
        </label>
      </div>
      <Info color={color} season={season} />
    </div>
  );
};

export default App;
Info.js
import React from "react";

const getColorKor = (color) => {
  console.log("getColorKor");
  switch (color) {
    case "red":
      return "빨강";
    case "orange":
      return "주황";
    case "yellow":
      return "노랑";
    case "green":
      return "초록";
    case "blue":
      return "파랑";
    case "navy":
      return "남";
    case "purple":
      return "보라";
    default:
      return "레인보우";
  }
};

const getSeason = (season) => {
  console.log("getSeason");
  switch (season) {
    case "Spring":
      return "봄";
    case "Summer":
      return "여름";
    case "Fall":
      return "가을";
    case "Winter":
      return "겨울";
    default:
      return "아직 잘 모름";
  }
};

const Info = ({ color, season }) => {
  📌 📌 📌 
  const colorKor = getColorKor(color);
  const seasonKor = getSeason(season);

  return (
    <div className='info-wrapper'>
      <br></br>
      <span>
        제가 가장 좋아하는 색은 <b>{colorKor}</b> 이고,{" "}
      </span>
      <br></br>
      <span>
        좋아하는 계절은 <b>{seasonKor}</b> 입니다.
      </span>
    </div>
  );
};

export default Info;

다음과 같이 코드를 작성하고 실행해보면 아래와 같이
먼저 component 가 mount 가 되었을 때 동시에 1번 실행되고,
이후에 우리는 좋아하는 계절만 선택했음에도 불구하고,
getColor 이 getSeason 과 같이 실행됨을 알 수 있습니다.

4. 예제에 useMemo 적용해보기

이제 useMemo 를 사용해봅시다
아까 📌 이 체크된 부분을 바꿔봅시다.

  const colorKor = useMemo(() => getColorKor(color), [color]);
  const seasonKor = useMemo(() => getSeason(season), [season]);

위와 같이 코드를 바꾸고 실행해보면, Season이 선택되면 getSeason 만 호출되고, Color 가 선택되면
getColor 만 실행되는 것을 알 수 있습니다.

5. 왜 useCallback 을 사용할까?


useMemo 는 메모이제이션된 함수를 반환하게 합니다.
컴포넌트가 렌더링 될 때마다 내부에 선언되어 있던 표현식(변수, 또다른 함수 등)도 매번 다시 선언되어 사용됩니다.

즉, App.js 의 onChangeHandler 함수는 내부의 color, season 상태값이 변경될 때마다 재선언된다는 것을 의미합니다. 하지만 onChangeHandler 함수는 파라미터로 전달받은 이벤트 객체(e)의 target.id 값에 따라 setState를 실행해주기만 하면 되기 때문에, 첫 마운트 될 때 한 번만 선언하고 재사용하면 되지 않을까요?

6. 예제와 useCallback 적용하기

예제에서 🔍 이 체크된 부분을 바꿔봅시다.

const onChangeHandler = useCallback(e => {
    if (e.target.id === "color") setColor(e.target.value);
    else setMovie(e.target.value);
  }, []);

useCallback 은 이벤트 핸들러 함수나 api를 요청하는 함수를 주로 useCallback 으로 선언하는 것을 알 수 있었습니다.

그리고 useCallback 또한 useMemo 와 동일하게 내부의 callback 함수의 로직이 복잡한 계산이 아니라면, 크게 의미있는 최적화는 아닐 수 있습니다. 공식문서에서도 다음과 같은 내용이 있습니다.

즉, optimized child 인 하위 컴포넌트에게 callback 함수를 props 를 넘길 때
상위 컴포넌트에서 useCallback 으로 함수를 선언하는 것이 유용하다는 의미로 해석할 수 있습니다.

하위 컴포넌트가 React.memo() 같은 것으로 최적화되어야만 useCallback 으로 함수를 선언하는 것이
최적화에 적용된다는 것을 알 수 있습니다. 다음 게시글에서는 React.memo 와 useCallback 을 함께 사용하는 방법에 대해서 알아보겠습니다.

7. Reference

React 공식문서
useCallback 과 React.memo 를 통한 렌더링 최적화
useMemo 와 useCallback 사용하기

profile
프론트 엔드와 심리학을 공부하는 대학생입니다 :)

0개의 댓글