티스토리 뷰
🔎 useMemo ?
useMemo는 컴포넌트의 성능을 최적화시킬 수 있는 대표적인 react hooks 중 하나입니다.
useMemo는 Memoization을 뜻합니다.
❓ Memoization
Memoization(메모이제이션)은 기존에 수행한 연산의 결과값을 어딘가에 저장(캐싱)해 두고
동일한 입력이 들어오면 재활용하는 프로그래밍 기법을 말합니다.
→ 💡 연산의 결과값을 캐싱해 두고 재사용하기 위한 훅 함수!
❓ 언제 쓰는 걸까
함수형 컴포넌트는
렌더링 → Component 함수 호출 → 모든 내부 변수 초기화
순서를 거칩니다.
Component가 렌더링이 될 때마다 value라는 변수가 초기화됩니다.
따라서 calcuate 함수는 반복적으로 호출됩니다.
calcuate 함수가 cost가 높은 연산을 하는 함수라면?
재렌더링 될 때마다 계속해서 재선언이 되는 것은 굉장히 비효율적일 것입니다.
const calcuate = () => 10;
function Component() {
const value = calcuate();
return <div>{value}</div>;
}
export default Component;
useMemo를 사용하면
렌더링 → Component 함수 호출 → Memoize된 함수를 재사용하는 동작
으로 순서를 거칩니다.
useMemo를 사용해서 메모이제이션을 해주면 calculate 함수를 반복적으로 실행할 필요가 없어집니다.
useMemo는 처음에 계산된 결과값을 메모리에 저장해서 컴포넌트가 반복적으로 렌더링이 되어도
계속 calculate를 다시 호출하지 않고 이전에 이미 계산된 결과값을 캐싱해 두었다가 꺼내와서 재사용할 수 있게 해줍니다.
❓ useMemo 사용법
const value = useMemo(() => {}, [])
useMemo의 첫 번째 인자로 콜백 함수, 두 번째 인자로 의존성 배열을 받습니다.
두 번째 인자인 의존성 배열에 담긴 요소값을 감시하다가 그 값이 업데이트될 때만
콜백 함수를 다시 호출 → 메모이제이션된 값을 업데이트 → 다시 메모이제이션
하는 과정을 거치게 됩니다.
만약에 빈 배열([ ])을 넘겨주면 맨 처음 컴포넌트가 마운트 되었을 때만 값을 계산하고 이후에는 항상 메모이제이션된 값을 꺼내와서 사용합니다.
📌 알아둬야 할 점
값을 재사용하기 위해 따로 메모리를 소비해서 저장을 해놓는 것입니다.
그렇기 때문에 불필요한 값을 모두 메모이제이션 해버리면 성능이 나빠질 수 있기 때문에 필요할 때만 사용하도록 합니다.
📌예제 코드 1
import {useEffect, useMemo, useState} from 'react';
function Component() {
const [number, setNumber] = useState(0);
const [isKorea, setIsKorea] = useState(true);
const location = useMemo(() => {
return {country: isKorea ? '한국' : '일본'};
}, [isKorea]);
useEffect(() => {
console.log('useEffect...호출');
// cost가 높은 작업
}, [location]);
return (
<header>
<h2>하루에 몇 끼 먹어요?</h2>
<input type="number" value={number} onChange={(e) => setNumber(e.target.value)} />
<hr />
<h2>어느 나라에 있어요?</h2>
<p>나라: {location.country}</p>
<button onClick={() => setIsKorea(!isKorea)}>Update</button>
</header>
);
}
export default Component;
위 코드에서 isKorea가 바뀔 때마다 useMemo 내부에 있는 콜백함수를 불러오도록 코드를 작성했습니다.
그렇다면 isKorea가 바뀌면 location의 객체 내부 값이 달라지도록 했습니다.
useMemo를 실행하면서 location 값이 계속 바뀔텐데
그렇다면 useEffect의 의존성 배열에 location을 작성했기 때문에 useEffect 내부에 있는 콘솔이 실행되어야 할 것입니다.
그렇지만 useMemo는 연산된 값을 캐싱해 두었다가 꺼내 쓰는 것이기 때문에 메모리 주소가 바뀌지 않습니다.
따라서 눈으로 보았을 때는 값이 변한 것 같지만 내부적으로는 캐싱해두었다가 쓴 것이 됩니다.
실행 결과를 보게 되면 useMemo를 통해 location의 값이 달라지고 있음에도 useEffect는 실행되지 않았습니다.
반면 update 버튼을 통해서는 isKorea의 요소값을 바꾸고 있습니다.
이제 isKorea의 값이 바뀔 때마다 useMemo가 매번 재선언되고 재선언 되었다는 것은 메모리 주소가 매번 바뀌는 것이므로 이번에는 useEffect가 실행될 것입니다.
📌 예제 코드 2
import {useState} from 'react';
const hardCalculate = (number) => {
console.log('어려운 계산!');
for (let i = 0; i < 999999999; i++) {} // 생각하는 시간
return number + 10000;
};
function Calculator() {
const [hardNumber, setHardNumber] = useState(1);
const hardSum = hardCalculate(hardNumber);
return (
<div>
<h3>어려운 계산기</h3>
<input
type={'number'}
value={hardNumber}
onChange={(e) => setHardNumber(parseInt(e.target.value))}
/>
<span> + 10000 = {hardSum}</span>
</div>
);
}
export default Calculator;
실행을 해보면 화면에 렌더링이 될 때 약간의 딜레이를 느낄 수가 있습니다.
그 이유는 hardSum이라는 값이 초기화가 되려면 hardCalculate라는 함수에서 값을 리턴 받아와야 하는데
시간이 조금 걸리기 때문입니다.
그렇다면 이번에는 오래 걸리는 연산이 없는 쉬운 계산기 코드를 작성해 보겠습니다.
import {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 Calculator() {
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(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 Calculator;
실행 결과는 보면
easyCalculate에는 오래 걸리는 연산이 없음에도 딜레이가 있는 이유는 무엇일까요?
그 이유는 바로 Calculator가 함수형 컴포넌트이기 때문인데요!
우리가 쉬운 계산기에서 숫자를 증가시켜주면 easyNumber의 state값이 바뀌는 거죠?
그 말은 즉 Calculator 컴포넌트가 다시 렌더링이 된다는 말이죠.
그렇다면 이 컴포넌트 안에 있는 hardSum과 easySum 모두다 초기화가 됩니다.
그 말은 즉, hardCalculator 함수도 다시 불려진다는 말입니다.
따라서 어려운 계산기가 됐든 쉬운 계산기가 됐든 같은 컴포넌트 안에 존재하기 때문에
재렌더링되면 모두 다시 다 호출이 된다는 말입니다.
그렇다면 이제 다음과 같은 생각을 할 수 있겠는데요.
쉬운 계산기를 사용할 때는 어려운 계산기는 호출되지 않았으면 하는데...
이때 바로 useMemo를 사용해서
어떠한 조건이 만족되었을 때만 변수들이 초기화가 되도록 할 수 있습니다.
즉, 조건을 만족하지 않는다면 컴포넌트가 다시 렌더링 되어도 이전에 있는 값을 그대로 사용하게 해줍니다.
import {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 Calculator() {
const [hardNumber, setHardNumber] = useState(1);
const [easyNumber, setEasyNumber] = useState(1);
// const hardSum = hardCalculate(hardNumber);
const hardSum = useMemo(() => {
return hardCalculate(hardNumber);
}, [hardNumber]);
// herdNumber가 변경되어야만 콜백함수 호출 -> 값 다시 초기화
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 Calculator;
실행 결과를 보게 되면 우리가 의도했던 것처럼
쉬운 계산기를 호출할 때는 어려운 계산기가 호출되지 않았습니다.
즉, useMemo의 의존성 배열에 hardSum을 걸어두었기 때문에
hardSum의 state가 바뀌었을 때만! 내부에 있는 콜백함수를 실행하게 되는 것입니다.
'프론트엔드 > React' 카테고리의 다른 글
[React] 커스텀 Hooks - 중복되는 로직을 한줄로 처리하도록 (0) | 2023.02.13 |
---|---|
[React] useCallback - 함수를 재사용하자 (0) | 2023.02.13 |
[React] state 제대로 사용하자 - 얕은 복사 & 깊은 복사 (0) | 2023.02.11 |
[React] SOLID 원칙에 기초한 React 코드 작성법 (0) | 2023.02.11 |
[React] Outlet으로 레이아웃 구성하기, url에 따라 다른 레이아웃 적용하기 (0) | 2023.02.11 |
- Total
- Today
- Yesterday
- 디프만
- 프론트엔드
- react-query
- jest
- styled-components
- 리액트 훅
- TypeScript
- 프론트엔드 기초
- 파이썬
- JSP
- next.js
- HTML
- 프론트엔드 공부
- 머신러닝
- frontend
- 딥러닝
- 데이터분석
- CSS
- 인프런
- 타입스크립트
- rtl
- Python
- 자바스크립트 기초
- 프로젝트 회고
- react
- 리액트
- 자바스크립트
- 스타일 컴포넌트 styled-components
- testing
- 자바
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |