다음과 같이 감정점수(emotion)에 따른 일기 비율을 계산하여 화면에 렌더링하는 함수 getDiaryAnalysis()
를 예제로 들어보겠습니다.
App.js
일부getDiaryAnalysis()
함수 선언부 const getDiaryAnalysis = ()=> {
console.log("일기 분석 시작!");
const goodCnt = data.filter((it)=> it.emotion >= 3).length;
const badCnt = data.length - goodCnt;
const goodRatio = (goodCnt / data.length) * 100;
return {goodCnt,badCnt,goodRatio};
};
const {goodCnt,badCnt,goodRatio} = getDiaryAnalysis();
return (
<div className="App">
<DiaryEditor onCreate={onCreate}/>
<div>전체 일기 : {data.length} </div>
<div>기분 좋은 일기 개수 : {goodCnt} </div>
<div>기분 나쁜 일기 개수: {badCnt}</div>
<div>기분 좋은 일기 비율: {goodRatio+'%'}</div>
<DiaryList onRemove = {onRemove} diaryList={data} onEdit={onEdit}/>
</div>
);
그리고 화면을 초기에 렌더링 했을때, getDiaryAnalysis()
의 콘솔이 몇번 호출되는지 확인합니다.
🤔 왜 2번 호출될까요?
⇒ 그 이유는 화면이 mount시, App컴포넌트의 useState에 빈 배열이 처음 렌더링되어 getDiaryAnalysis()
를 호출하고, 그리고 실제 API를 useEffect로 받아오면서 또 한번 함수가 호출이 되는 프로세스로 구동이 되기 때문에 2번 호출됩니다.
따라서 문제는 getDiaryAnalysis()
와 전혀 관련이 없는 일기 수정작업을 할때도 다음과 같이 해당 함수가 호출됩니다. (콘솔이 출력됌)
이는 불필요한 호출이며 메모리 낭비입니다. 따라서 이를 해결하기 위한 방안으로는 useMemo()
라는 리액트 훅 중 하나인 함수형 컴포넌트를 활용하는 것입니다.
memoization기법을 활용해서 이름에서도 useMemo()
를 볼 수 있습니다. memoization은 DP기법 중 하나로, 한번 연산한 값은 기억해두고, 불필요하게 다시 연산하지 않는다는것을 의미합니다.
useMemo()
로 리팩토링하기useMemo()
는 useEffect()
와 사용법은 동일합니다.
const getDiaryAnalysis = useMemo(()=> {
// console.log("일기 분석 시작!");
const goodCnt = data.filter((it)=> it.emotion >= 3).length; //기분이 좋은 값의 갯수
const badCnt = data.length - goodCnt;
const goodRatio = (goodCnt / data.length) * 100;
//위의 연산을 최적화 하고 싶다 -> useMemo활용
return {goodCnt,badCnt,goodRatio};
},[data.length]);
Dependency Array의 값이 바뀔때만 콜백함수를 호출함으로써, 불필요한 연산을 줄일 수 있습니다. 따라서 위의 코드는 state인 data의 length가 바뀔때만 해당 콜백함수를 호출합니다. (=일기 리스트가 추가 또는 삭제 될때만 해당 콜백함수 호출)
useMemo()
를 통해 불필요하게 낭비되는 메모리를 Dependency Array의 값에 따라 콜백함수 호출 여부를 따져 방지 및 최적화 할 수 있는 방법을 알아봤습니다.