어떤 부분이 연산을 낭비시키고 있는가?
성능상의 문제 찾아 최적화
하기컴포넌트 연산 최적화
는 웹서비스의 성능
을 결정할 수 있는 가장 중요한 작업✔️ 작성한 코드가 정상적으로 잘 작동하는가 분석하는 방법
정적 분석
: IDE에서 코드를 한 줄 한 줄씩 보면서 코드들의 상태만 보고 최적화가 안되어 있는지 판단하여 분석하는 과정동적 분석
: React Developer Tools, 크롬 개발자도구와 같은 도구의 힘을 빌려 어떤 부분이 낭비되고 있는지 문제되는 부분을 찾아 분석하는 과정
ㄴ React Developer Tools의Highlight updates render
기능을 활용해컴포넌트의 state를 변화시켰을 때 어떤 컴포넌트들이 리렌더링되는지
로 분석하기
<div>
<MyHeader
headText={headText}
leftChild={<MyButton text={"<"} onClick={decreaseMonth} />}
rightChild={<MyButton text={">"} onClick={increaseMonth} />}
/>
<DiaryList diaryList={data} />
</div>
setCurDate state를 변화
시킴Home 컴포넌트의 state가 변화
되어 다시 렌더링이 일어나고 DiaryList에 state가 변화하면서 data를 받아옴현재 성능상의 문제 : 하위 요소인
ControlMenu가 다시 렌더링 될 필요가 없어도 리렌더링
되는 것
React.memo
를 활용한 연산 최적화 : React.memo로 컴포넌트를 감싸서 인자로 전달하면 강화된 컴포넌트로 돌려주는 고차 컴포넌트(HOC) 돌려주는데, 전달받은 인자(prop)이 값이 바뀌지 않으면 리렌더링이 되지 않게 memoizaition
해주는 기법React.memo로 ControlMenu를 감싸고 이전/다음 버튼으로 월을 변경하면 ControlMenu의 깜빡임이 사라진 것을 확인할 수 있음
const ControlMenu = React.memo(({value, onChange, optionList}) => {
// mount되었을 때 잘 되었는지 useEffect로 확인
useEffect(()=>{
console.log("Control Menu");
});
return (
<select className="ControlMenu" value={value} onChange={(e)=>onChange(e.target.value)}>
{optionList.map((it, index) => (
<option key={index} value={it.value}>
{it.name}
</option>
))}
</select>
)
});
// List
const DiaryList = ({diaryList}) => {
// sort
const [sortType, setSortType] = useState("latest");
// filter
const [filter, setFilter] = useState("all");
...
return (
<ControlMenu value={sortType} onChange={setSortType} optionList={sortOptionList} />
<ControlMenu value={filter} onChange={setFilter} optionList={filterOptionList} />
React.memo
를 적용import React from "react";
export default React.memo(DiaryItem);
const DiaryEditor = ({isEdit, originData}) => {
const [emotion, setEmotion] = useState(3);
const [date, setDate] = useState(getStringDate(new Date()));
const [content, setContent] = useState(); // state 변화
...
<section>
<h4>오늘의 감정</h4>
<div className="inputBox emotionListWrapper">
{emotionList.map((it)=>(
<EmotionItem key={it.emotion_id} {...it} onClick={handleClickEmote} isSelected={it.emotion_id === emotion} />
// <div key={it.emotion_id}>{it.emotion_descript}</div>
))}
</div>
</section>
React.memo
를 적용하여 EmotionItem이 리렌더링되지 않도록 변경하여 최적화import React from "react";
const EmotionItem = ({
emotion_id,
emotion_img,
emotion_descript,
onClick,
isSelected
}) => {
return (
<div
className={[
"EmotionItem",
isSelected ? `EmotionItem_on${emotion_id}` : `EmotionItem_off`].join(" ")}
onClick={()=>onClick(emotion_id)}
>
<img src={emotion_img} alt={emotion_descript} />
<span>{emotion_descript}</span>
</div>
);
};
export default React.memo(EmotionItem);
useState를 통해서 전달받은 상태 변화 함수가 아니거나 useCallback으로 묶어놓은 함수가 아니라면 기본적으로 컴포넌트가 렌더링될 때 다시 생성되어 React.memo의 강화된 컴포넌트(HOC)가 되어도 렌더링을 발생시킨다
<section>
<h4>오늘의 감정</h4>
<div className="inputBox emotionListWrapper">
{emotionList.map((it)=>(
<EmotionItem key={it.emotion_id} {...it} onClick={handleClickEmote} isSelected={it.emotion_id === emotion} />
// <div key={it.emotion_id}>{it.emotion_descript}</div>
))}
</div>
</section>
useCallback을 적용해 memoization
적용 // memoization useCallback
const handleClickEmote = useCallback((emotion) => {
setEmotion(emotion);
},[]);
💬 밀린 포스팅 주말에 몰아쓰기