[React] React Hooks(6) - useMemo

최예린·2022년 8월 7일
0

React

목록 보기
6/19

useMemo란?

컴포넌트 최적화를 위해서는 useMemo와 useCallback을 사용한다.

  • 메모이제이션memoization
    동일한 값을 리턴하는 함수를 반복 호출할 때 맨처음 계산한 값을 메모리에 저장해놓았다가 꺼내서 재사용하는 것을 말한다.(캐싱)

함수형 컴포넌트도 함수의 일종이기 때문에 렌더링 될 때마다 모든 내부 변수가 초기화 된다.

구조

const value = useMemo(() => {
  return calculate();
}, [item]);
  • 첫번째 인자: 콜백 함수 메모이제이션 해줄 값을 계산해서 리턴하는 함수.
  • 두번째 인자: 의존성 배열dependency array 요소의 값이 업데이트 될 때만 콜백함수를 다시 호출해서 메모이제이션 될 값을 업데이트해서 다시 메모이제이션을 해준다.
    ※ [] 빈 배열 컴포넌트가 마운트 되었을때만 값을 계산함

※ 무분별하게 사용하면 불필요한 값까지 메모이제이션하면 성능이 저하될 수 있다.

[예제1] 어려운 계산기와 쉬운 계산기

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

const hardCalculate = (number) => {
  console.log('어려운 계산!');
  for (let i = 0; i < 999999999; i++) {} // 생각하는 시간
  return number + 10000;
};

const easyCalculate = (number) => {
  console.log('짱 쉬운 계산!');
  return number + 1;
};

function App() {
  const [hardNumber, setHardNumber] = useState(1);
  const [easyNumber, setEasyNumber] = useState(1);
  
  // const hardSum = hardCalculate(hardNumber);
  const hardSum = useMemo(() => {
    return hardCalculate(hardNumber);
  }, [hardNumber]);
  const easySum = easyCalculate(easyNumber);
  
  return (
    <div>
    	<h3>어려운 계산기</h3>
    	<input
    		type="number"
    		value={hardNumber}
			onChange={(e) => setHardNumber(parseInt(e.target.value))}
        />
		<span> + 10000 = {hardSum}</span>

		<h3>쉬운 계산기</h3>
    	<input
    		type="number"
    		value={easyNumber}
			onChange={(e) => setEasyNumber(parseInt(e.target.value))}
        />
		<span> + 1 = {EasySum}</span>
    </div>
  );
}

export default App;

어려운 계산기에는 임의로 굉장히 긴 반복문 코드를 넣어놓았기때문에 어려운 계산기의 숫자를 1 증가시키면 1초 정도의 시간이 지난 뒤 값이 바뀐다. 하지만 쉬운 계산기의 숫자를 1 증가시켰을 때는 바로 계산이 완료될 것을 기대하지만 실제로는 똑같이 1초의 딜레이가 생긴다.

그 이유는 easyNumber라는 state의 값이 바뀌면 App 컴포넌트가 렌더링 되고 hardSum 변수가 초기화 되면서 hardCalculate() 함수가 같이 호출되기때문이다.

이를 막기위해 hardNumber가 바뀔때만 hardCalculate()가 실행되도록 useMemo를 사용한다. hardNumber가 바뀌지않으면 App 컴포넌트가 리렌더링 되더라도 변수가 초기화되지않고 메모리에 저장된 값을 계속 사용한다.


실제로 리액트에서는 변수를 선언하는데에 1초이상 걸리는 계산을 하는 경우는 거의 없다. useMemo가 잘쓰이는 상황은 아래 예제 2이다.

[예제2] 객체 할당

잘못된 코드

import React, { useEffect, useMemo, useState } from 'react';

function App() {
  const [number, setNumber] = useState(0);
  const [isKorea, setIsKorea] = useState(true);
  
  const location = {
      country: isKorea ? '한국' : '외국',
  };
  
  useEffect(() => {
    console.log('useEffect 호출');
    // 오래 걸리는 작업 ...
  }, [location]);
  
  return (
    <div>
    	<h2>하루에 몇끼 먹어요?</h2>
    	<input
    		type="number"
    		value={number}
			onChange={(e) => setNumber(e.target.value)}
        />
		<hr />
        <h2>어느 나라에 있어요?</h2>
		<p>나라: {location.country}</p>
		Mbutton onClick={() => setIsKorea(!isKorea)}>비행기 타자</button>
	</div>
  );
}

export default App;
    
  

string과 같은 원시(Primitive) 타입 변수인 경우에는 useEffect의 의존성 배열이 잘 동작하지만, 의존성 배열에 객체를 넣으면 location이 바뀌지않고 number만 바뀌었음에도 불구하고 useEffect가 불려진다.

이는 원시타입 변수가 바로 값이 저장되는 반면 객체 타입 변수는 메모리에 객체를 저장하고 그 저장 위치를 참조하기때문이다.


객체 타입은 겉으로는 똑같아 보여도 메모리상의 주소를 저장하고있기때문에 false를 반환한다.

number state가 바뀜 -> App 컴포넌트 리렌더링 -> location 재할당 -> 메모리상 다른 주소에 객체할당 -> useEffect의 의존성 배열 내 location이 변경되었다고 인식

결과적으로 실제로 location을 변경하지는 않았지만 location이 변경되었다고 인식한다.

이를 막기위해서는 App 컴포넌트가 렌더링 될 때 location을 다시 할당하지않도록 하면된다.

올바른 코드

import React, { useEffect, useMemo, useState } from 'react';

function App() {
  const [number, setNumber] = useState(0);
  const [isKorea, setIsKorea] = useState(true);
  
  const location = useMemo(() => {
	return {
      country: isKorea ? '한국' : '외국',
    };
  }, [isKorea]);
  
  useEffect(() => {
    console.log('useEffect 호출');
    // 오래 걸리는 작업 ...
  }, [location]);
  
  return (
    <div>
    	<h2>하루에 몇끼 먹어요?</h2>
    	<input
    		type="number"
    		value={number}
			onChange={(e) => setNumber(e.target.value)}
        />
		<hr />
        <h2>어느 나라에 있어요?</h2>
		<p>나라: {location.country}</p>
		Mbutton onClick={() => setIsKorea(!isKorea)}>비행기 타자</button>
	</div>
  );
}

export default App;
  • isKorea가 변경될 때만 useMemo를 통해 location을 다시 초기화해준다.
    즉, number가 바뀌어 다시 렌더링 될 때에는 location은 다시 할당되지않는다.
profile
경북대학교 글로벌소프트웨어융합전공/미디어아트연계전공

0개의 댓글