A라는 문제를 풀고 그 답을 외웠다, 기억했다라는 행위 자체
에 있음연산 최적화
를 위해 이 문제에 대한 답을 이전에 기억해 두었다
는 뜻이런 Memoization 기법을 리액트 프로그래밍을 진행하면서 어떤 부분에 어떻게 적용해야 하는지 알아보기
✔️ App 컴포넌트에 지금 현재 dataState가 가지고 있는 일기들 중
기분이 좋은 일기가 현재 몇 개인지
기분이 나쁜 일기가 현재 몇개인지
기분 좋은 일기의 비율은 어떻게 되는지
src/App.js
function App() {
const [data, setData] = useState([]);
const getDiaryAnalysis = () => {
console.log("일기 분석 시작"); // 콘솔에 출력
// 1) 기분 좋은 일기가 몇 개인지 세는 상수
// 내장함수 filter 적용 : 이 배열의 원소 중에 감정점수가 3 이상인 것만 추리고 그 길이를 구하면 개수가 나옴
const goodCount = data.filter((it) => it.emotion >= 3).length;
// 2) 기분 나쁜 일기가 몇 개 인지 세는 상수
// 전체 개수 빼기 기분 좋은 일기
const badCount = data.lenght - goodCount;
// 3) 기분 좋은 일기 비율을 구하는 상수
// 전체 개수 분의 기분 좋은 일기 곱하기 100
const goodRatio = (goodCount / data.length) * 100;
// 1~3)의 데이터를 객체로 리턴해주기
return {goodCount, badCount, goodRatio};
// 만들어진 지역함수를 리턴 전에 호출
// 객체로 리턴하니까 똑같이 객체의 비구조화 할당으로 받기
// const {goodCount, badCount, goodRatio} = getDiaryAnalysis();
};
// DOM에 기분 좋은/나쁜 일기 개수, 기분 좋은 일기 비율 렌더링
return (
<div className="App">
<DiaryEditor onCreate={onCreate} />
<div>전체 일기 : {data.length}</div>
<div>기분 좋은 일기 개수 : {goodCount}</div>
<div>기분 나쁜 일기 개수 : {badCount}</div>
<div>기분 좋은 일기 비율 : {goodRatio}</div>
<DiaryList onEdit={onEdit} onRemove={onRemove} diaryList={data} />
</div>
);
}
✔️ 왜 두 번 동작했을까?
- App 컴포넌트가 처음 Mount될 때 dataState 값은 빈 배열임
- 그 순간 getDiaryAnalysis 함수를 한 번 호출하게 됨
- 그 다음 getData API 호출이 성공하고, setData가 이루어지게 되면서 data가 한 번 바뀌게 됨. 그러면 App이 다시 리렌더링됨. 그러면 모든 함수들이 재생성됨
- 그러면서
const {goodCount, badCount, goodRatio} = getDiaryAnalysis();
가 다시 생성되면서 호출이 되게 되므로 결과적으로 두 번 동작하게 됨.- 리렌더링이 일어난다는 의미 : 함수가 호출된다, 재실행
일기 수정하기 : 수정 시 dataState가 또 바뀌기 때문에 "일기 분석 시작"이 또 출력됨
일기 데이터를 수정하는 행위는 일기 개수/비율 계산에 아무런 영향을 미칠 수 없음.
감정점수를 수정하는 게 아닌 작성내용만 수정하는 건데도 App 컴포넌트가 리렌더링되어 함수가 재호출되고 있는 낭비
이런 리턴을 가진 함수를 Memoization을 통해 연산 최적화
를 위해 useMemo
활용하기
src/App.js
import { useMemo, useEffect, useRef, useState } from "react";
import "./App.css";
import DiaryEditor from "./DiaryEditor";
import DiaryList from "./DiaryList";
function App() {
const [data, setData] = useState([]);
// useMemo 연산최적화
const getDiaryAnalysis = useMemo(() => {
console.log("일기 분석 시작");
const goodCount = data.filter((it) => it.emotion >= 3).length;
const badCount = data.lenght - goodCount;
const goodRatio = (goodCount / data.length) * 100;
return {goodCount, badCount, goodRatio}; // return을 가진 함수를 최적화
}, [data.length]); // dependency array
const {goodCount, badCount, goodRatio} = getDiaryAnalysis; // 호출이 아닌 값으로 변경
}
[]
에 data.length
넣어놓게 되면 전체 일기가 변화할 때만 useMemo의 첫번째 인자인 콜백함수가 다시 수행되게 됨data.lenght
)가 변하지 않는 이상 똑같은 리턴을 계산하지 않고 반환한다는 의미const {goodCount, badCount, goodRatio} = getDiaryAnalysis;
는 useMemo로부터 값을 리턴받기 때문에 getDiaryAnalysis 함수를 호출하지 않고 값으로 사용해야 함.✔️ 연산 최적화 확인
- 임의의 일기 데이터 1개를 수정해도 전체 일기 수가 변경된 것이 아니기 때문에 콘솔에 "일기 분석 시작"이 출력되지 않음(다시 함수를 호출하지 않게 됨). 그런데 임의로 1개의 일기를 삭제하면 전체 일기 수가 바뀌어서 다시 "일기 분석 시작"이 출력되면서 연산 최적화가 일어난 것을 확인 가능.
⚡️ 정리
- 어떤 함수가 있고 그 함수가 어떤 값을 리턴하고 있을 때, 그 리턴까지의 연산을 최적화하고 싶다면
useMemo
훅을 사용해서 두번째 인자인 dependency array[]
에어떤 값이 변화할 때만 이 연산을 다시 수행할 것인지를 명시
해주면 이 함수를 값처럼 사용해서연산 최적화
가 가능하다.