8-4 useMemo
- 함수 컴포넌트 내부에서 발생하는 연산을 최적화 시키는 Hook이다.
- 리액트에서 컴포넌트의 렌더링은 state업데이트, 부모컴포넌트 업데이트 등으로 인해 수시로 일어날 수 있다.
이러한 경우에 불필요한 중복 연산이 발생할 수 있으며 memoization을 활용한 useMemo로 연산을 최적화 시킬 수 있다. - Memoizatioin 이란?
기존에 수행하던 연산의 결과물을 어딘가에 저장해두고 동일한 값이 들어오면 재활용하는 기법이다.
랜더링마다 호출되는 컴포넌트 함수
일반적으로 React의 함수형 컴포넌트는 다음과 같은 구조로 작성이 됩니다.
function MyComponent(props) {
// 어떤 로직 (JavaScript)
return; /* 어떤 화면 (JSX) */
}
function MyComponent({ x, y }) {
const z = compute(x, y);
return <div>{z}</div>;
}
이렇게 작성된 컴포넌트 함수는 React에서 랜더링(rendering)이 일어날 때마다 호출이 된다. 컴포넌트 함수가 호출이 되면 그 안에 자바스크립트 로직들이 수행되고, 이를 기반으로 JSX로 마크업된 UI가 리턴되는 기본 구조를 가지고 있다.
React에서 컴포넌트의 랜더링은 한 번 일어나고 끝이 아니라 수시로 계속 일어날 수 있다.
대표적인 예로 컴포넌트의 자신의 상태 변경(state update)이 일어날 수 있고, 아니면 부모 컴포넌트의 상태 변경이 일어나 랜더링되야 하는 경우도 있다. 사용자가 브라우저에서 새로고침을 할 때도 컴포넌트의 재 랜더링은 불가피 하다.
만약에,
함수가 내부적으로 복잡한 연산을 수행하기 때문에 결과값을 리턴하는 데 오랜 시간이 걸린다면?
컴포넌트가 재랜더링 할 때마다 함수가 호출되므로 사용자가 홈페이지 사용시 불편을 느낄 수 있을 것이다.
useMemo 개념
렌더링이 일어날 때마다 compute함수의 인자로 넘어오는 x,y값이 항상 변하는 게 아니라면 compute함수를 계속 호출할 필요가 없다. useMemo를 활용하여 memoization기법을 적용하여 개선할 수 있다.
** momoization
렌더링이 발생했을 때, 이전 랜더링과 현재 랜더링 간에 x와 y 값이 동일한 경우, 다시 함수를 호출을 하여 z 값을 구하는 대신, 기존에 메모리의 어딘가에 저장해두었던 z 값을 그대로 사용하는 것입니다.
function MyComponent({ x, y }) {
const z = useMemo(() => compute(x, y), [x, y]);
return <div>{z}</div>;
useMemo 함수는 2개의 인자를 받는데
첫번째는 (x , y)는 결과값을 생성해주는 팩토리 함수(=기존에 호출하던 함수)이고, 두번째는 [ x , y ]는 기존 결과값 재활용 여부의 기준이되는 입력값 배열이다. ( 두번째 인자인 배열의 요소 값이 업데이트 될 때만 콜백함수를 호출 )
useMemo적용
import { useState } from "react";
const getAverage = numbers =>{
console.log('평균값 계산중')
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a,b) => a + b)
return sum / numbers.length;
}
const UseMemoEx = ()=>{
const [list, setList] = useState([]);
const [number, setNumber] = useState('');
const handleOnChange = (e)=>{
setNumber(e.target.value)
}
const handleOnClick = (e)=>{
const nextList = list.concat(parseInt(number))
setList(nextList)
setNumber('')
}
return (
<>
<input value={number} onChange={handleOnChange}/>
<button onClick={handleOnClick}>등록</button>
<ul>
{list.map((value,idx)=>(
<li key={idx}>{value}</li>
))}
</ul>
<div>
<b>평균값 : </b>{getAverage(list)}
</div>
</>
)
}
export default UseMemoEx;
평균값을 계산할 때만 getAverage함수가 실행되어야 하는데, input에 값을 입력할 때도 getAverage 함수가 실행된다.
그 이유는,
input에 onChange함수가 실행 되면 컴포넌트가 재랜더링 되면서 업데이트가 발생한다. 동시에 getAverage의 인수가 되는 list의 값이 초기화되면서 getAverag함수가 호출된다. 컴포넌트의 재랜더링이 필요할 때마다 함수가 호출되므로 이는 비효율적이다.
useMemo를 사용하여 작업을 최적화 할 수 있다.
useMemo는 렌더링하는 과정에서 특정 값이 바뀌었을 때만 연산을 실행하고, 원하는 값이 바뀌지 않았으면 이전에 연산했던 결과를 다시 사용할 수 있게 해준다.
import { useState, useMemo } from "react";
const getAverage = numbers =>{
console.log('평균값 계산중')
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a,b) => a + b)
return sum / numbers.length;
}
const UseMemoEx2 = ()=>{
const [list, setList] = useState([]);
const [number, setNumber] = useState('');
const handleOnChange = (e)=>{
setNumber(e.target.value)
}
const handleOnClick = (e)=>{
const nextList = list.concat(parseInt(number))
setList(nextList)
setNumber('')
}
const avg = useMemo(()=> getAverage(list), [list])
return (
<>
<input value={number} onChange={handleOnChange}/>
<button onClick={handleOnClick}>등록</button>
<ul>
{list.map((value,idx)=>(
<li key={idx}>{value}</li>
))}
</ul>
<div>
<b>평균값 : </b>{avg}
</div>
</>
)
}
export default UseMemoEx2;
변수 avg에 useMemo를 사용하였다.
결과적으로 getAverage함수에 들어가는 list의 내용이 추가될 때만 getAverage함수가 호출된다.
= input 입력창에 숫자를 입력할 때는 getAverage함수가 작동하지 않는다
!!!
useMemo는 성능 최적화를 위해 사용할 수는 있지만, 가장 좋은 방법은 아닐 수 있음을 기억하세요!
최대한 useMemo를 사용하지 않고도 동작할 수 있도록 코드를 작성해볼 것(react.공식문서)
참고
'개발공부_Blog > React.js' 카테고리의 다른 글
immer_불변성 유지 (0) | 2022.08.21 |
---|---|
config.json (0) | 2022.08.15 |
useReducer, React-hooks(3) (0) | 2022.08.10 |
useEffect, React-Hooks(2) (0) | 2022.08.10 |
useState, 가장 기본적인 React-Hooks (0) | 2022.08.10 |
댓글