"React useMemo" : 메모이제이션을 통한 성능 최적화

그냥차차·2024년 4월 11일

리엑트

목록 보기
3/10

1. 소개

  • React 앱을 개발하면서 성능 최적화는 항상 고려해야 할 중요한 측면임.
  • 이때 useMemo 훅은 메모이제이션을 통해 성능을 향상시킬 수 있는 강력한 Hook임
  • 이번 글에서는 React의 useMemo 훅에 대해 알아보고, 그 특징과 장점에 대해 시작해보겠음

2. 특징

  • 메모이제이션: useMemo 훅은 이전에 계산된 값을 기억하여 동일한 계산을 반복하지 않고 이전에 계산된 값을 재사용하고, 이를 통해 불필요한 계산을 방지하여 성능을 향상시킴
  • 의존성 배열: useMemo 훅은 두 번째 매개변수로 의존성 배열을 받고, 이 배열에 포함된 값들이 변경될 때에만 메모이제이션된 값이 다시 계산됨

3. 장점

  • 성능 향상: useMemo를 사용하면 렌더링 시간을 줄일 수 있음! 특히 계산 비용이 높거나 자주 변경되지 않는 값들을 메모이제이션하여 불필요한 계산을 최소화할 수 있음
  • 코드 가독성: useMemo를 사용하면 계산된 값을 쉽게 추적할 수 있음, 이전에 계산된 값을 재사용하는 것이 명시적으로 표현되므로 코드의 의도를 명확하게 전달가능함
  • 렌더링 최적화: useMemo를 적절히 활용하면 불필요한 렌더링을 방지할 수 있고, 메모이제이션된 값이 변경되지 않는 한 해당 값이 사용된 컴포넌트는 다시 렌더링되지 않음

4. 예시코드

    1. useMemo를 사용하기전 코드 : useMemo를 사용하기전에는 위와같이 불필요한 렌더링을 해서 hardCalculate, easyCalculate 함수를 실행하면 App 컴포넌트가 전체 렌더링이 되면서 불필요한값이 콘솔에 찍히게됨
import { useMemo, useState } from "react";
import "./App.css";
const hardCalculate = (number) => {
  console.log("어려움 시간이 오래걸림 쓸데없는 반복을 돌기때문");
  // 스테이트에 값을 게속 넣고 비교하기때문에 렌더링을 반복문 만큼 게속하게되고
  // 즉각적으로 값을 변화시키는게 아니기 때문에 딜레이 값이 있음
  for (let i = 0; i < 9999999; i++) {
    return number + 10000;
  }
};
const easyCalculate = (number) => {
  // 마찬가지로 쉬운 계산기를 만들어도 App컴포넌트 안에 있어서
  // 전체적으로 렌더링이 되기때문에 hardCalculate도 동작을 해서
  // 쉬운계산기여도 딜레이를 가지게됨 이때 useMemo를 사용하면 좋음
  console.log("쉬움");
  return number + 1;
};
function App() {
  const [hardNumber, setHardNumber] = useState(1);
  const [easyNumber, setEasyNumber] = useState(1);
  const hardSum = hardCalculate(hardNumber);
  const easySum = easyCalculate(easyNumber);
  return (
    <div>
      <h3>어려운 계산기</h3>
      <input
        type="number"
        value={hardNumber}
        onChange={(e) => setHardNumber(e.target.value)}
      />
      <span> + 10000 = {hardSum}</span>
      <h3>쉬운 계산기</h3>
      <input
        type="number"
        value={easyNumber}
        onChange={(e) => setEasyNumber(e.target.value)}
      />
      <span> + 10000 = {easySum}</span>
    </div>
  );
}
export default App;
    1. useMemo를 사용한 후 코드 :
      useMemo를 사용한 후 에는 1번과 같은 불필요한 렌더링을 하지않고 사용하는 값만 렌더링하고, 의존성 배열안에 있는값이 변화하지 않으면 렌더링을 하지않고, 그전에 메모리에 저장된 값을 보여줌
  const hardSum = useMemo(() => {
    // use Memo는 두가지의 인자를 받음, 콜백함수, 의존성배열
    // 아래와같은 코드로 작성하면 hardNumber가 변경이 될때만
    // hardCalculate 함수가 돌기때문에 불필요한 렌더링을 방지할수있고
    // hardNumber가 변경되지않으면 이전에 가지고있던 값을 메모리에 저장해두었다가 사용함
    return hardCalculate(hardNumber);
  }, [hardNumber]);

5. 객체타입 사용시 useMemo

  • 원시타입 사용시 useEffect를 사용하면 아래 예시코드와 같이 해당된 원시타입의 변수에 대해서만 렌더링이 발생됨
  • 하지만 객체타입으로 useEffect 사용시 전체컴포넌트가 렌더링이 발생하는데 그 이유는 자바스크립트의 타입에 따라 렌더링이 되기 때문임, 원시타입은 변수 그대로의 값을 비교하지만 객체타입의 경우 객체안의 변수를 - 그대로 저장하는것이 아닌 메모리에 값이 할당이되고, 메모리의 주소가 객체안에 할딩이됨
    그래서 같은 원시타입을 비교하면 true가 나오지만
  • 같은 객체타입을 비교하면 메모리안에 할당된 주소가 다르기 때문에 같은변수처럼 보이더라도 false가 나오게됨
  • 이럴때 객체타입은 useMemo를 사용하여 불필요한 렌더링을 줄임
import React, { useEffect, useMemo, useState } from "react";
export default function App() {
  const [number, setNumber] = useState(0);
  const [isKoran, setKorea] = useState(true);
  const location = useMemo(() => {
    // 이렇게 useMemo를 사용하여 객체타입은 불필요한 렌더링을 방지하면
    // 햄최면 Input을 클릭해도 객체타입은 렌더링을 하지않게됨
    return {
      country: isKoran ? "한국" : "외국",
    };
  }, []);
  useEffect(() => {
    console.log("useEffect 호출함");
    // 의존성배열에 원시타입이아닌 객체타입이 들어가면
    // 햄최몇의 input값이 바뀌어도 렌더링이되어 콘솔이 찍히게됨

    // 그 이유는 자바스크립트가 동작하는 원리때문인데
    // 원시타입의 경우 값을 그대로 가져오지만
    // 객체타입의 경우 메모리값이 할당되고 그 메모리안에 할당이되고
    // 그객체안의 변수에는 메모리의 값이아닌 메모리의 주소가 할당됨

    //그예로 같은 원시데이터를 비교하면 true가 나오지만
    // 같은 객체타입을 비교하면 false가 나옴
    // 그이유는 객체안의 변수에는 메모리의 주소가 다르기때문임
  }, [location]);
  return (
    <div>
      <h2>햄최몇?</h2>
      <input
        value={number}
        type="number"
        onChange={(e) => {
          setNumber(e.target.value);
        }}
      />
      <hr />
      <h2>어디사냐?</h2>
      <p>여기살고 있는데 왜? 뭐? {location.country}</p>
      <button onClick={() => setKorea(!isKoran)}> 아니그냥</button>
    </div>
  );
}

6. 개념 추가

  • ㄱ. Call by value (값에 의한 호출):
    이 방식은 함수에 인자를 전달할 때 값 자체를 복사하여 전달합니다. 함수 내부에서 인자의 값이 변경되어도 호출된 쪽에는 영향을 주지 않습니다.
    호출하는 변수의 값만을 복사하여 함수 내부로 전달되기 때문에, 함수 내부에서 해당 값을 변경하더라도 호출된 곳의 변수 값에는 영향을 주지 않습니다.
    대부분의 언어에서 함수의 인자 전달 방식이 이 방식으로 이루어집니다.
  • ㄴ. Call by reference (참조에 의한 호출)
    이 방식은 함수에 인자를 전달할 때 변수의 참조(메모리 주소)를 전달합니다. 따라서 함수 내부에서 해당 참조를 통해 변수 값을 직접 변경할 수 있음.
    함수에 인자로 변수를 전달할 때, 변수의 메모리 주소가 전달되므로 함수 내에서 변수 값을 변경하면 호출한 곳에서도 변경된 값을 확인할 수 있습니다.
    이 방식은 몇몇 언어에서 사용되며, 일반적으로 포인터나 참조를 이용하여 구현됩니다.
profile
개발작

0개의 댓글