최적화 적용(3) - 메모이제이션
📌시작하며
프로젝트에서 React.memo, useMemo, useCallback을 사용해서 메모를 통한 최적화 방식을 적용했다. 어떤 것들에 적용했는지 정리하고자 포스팅한다.😎
✅React.memo
React.memo는 컴포넌트가 받는 props가 변경되지 않는 한, 해당 컴포넌트가 재랜더링되는 것을 방지한다.
React Dev Tools를 이용해 state가 변경되었을 때 어떤 컴포넌트들이 재 렌더링 되는지 확인하고, 불필요하게 재 렌더링 되는 경우 memo 처리 해주었다.
예를 들어 루트 페이지의 음악 카드들의 경우 Tanstack Query로 받은 Data를 map 메소드를 이용해 각각의 musicData를 props로 전달받는데 Data가 변경되지 않는 경우 굳이 재렌더링 될 필요가 없으나, 지속적으로 재 렌더링 되는 것을 확인하였고, 이를 막고자 React.memo를 사용하였다.
여담이지만 컴포넌트 만들 때 이번 프로젝트에서는 export default function 컴포넌트
와 같은 함수 선언식으로 작성했는데, memo를 쓸 때는 const 컴포넌트 = () => {}
와 같은 함수 표현식을 사용한 다음, 마지막에 export default memo(컴포넌트)
와 같이 작성해야 해서 처음부터 표현식으로 작성할 걸 그랬나? 생각했다.😅
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
const MusicCard = ({ musicData }: { musicData: SupabaseType }) => {
const {
//구조분해할당
} = musicData
const lan = useRecoilValue(languageMode)
const route = useRouter()
//현재 보고 있는 버전 설정
const [selectLang, setSelectLang] =
useState < LangType > (kotitle ? "ko" : "jp")
return (
<div className="mt-20 w-full overflow-hidden rounded-lg bg-white shadow-sm">
<Link
href={`/musicpt/${musicData.id}`}
className="block aspect-[8/5] w-full cursor-pointer overflow-hidden "
>
<div
className={cn("relative aspect-square w-full", {
hidden: selectLang === "jp",
block: selectLang === "ko",
})}
>
{selectLang === "ko" &&
(kothumbnail ? (
<Image
src={kothumbnail}
fill={true}
sizes="(max-width: 768px) 100vw, (max-width: 1023px) 704px, 400px"
alt={"음악"}
loading="lazy"
placeholder="blur"
blurDataURL={kothumbnail}
/>
) : (
<DefaultImage />
))}
</div>
<div
className={cn("relative aspect-square w-full", {
hidden: selectLang === "ko",
block: selectLang === "jp",
})}
>
{selectLang === "jp" &&
(jpthumbnail ? (
<Image
src={jpthumbnail}
fill={true}
sizes="(max-width: 768px) 100vw, (max-width: 1023px) 704px, 400px"
alt={"음악"}
loading="lazy"
placeholder="blur"
blurDataURL={jpthumbnail}
/>
) : (
<DefaultImage />
))}
</div>
</Link>
<div className="h-[150px] p-5">
{/* 카드 윗 줄 */}
<div className="flex items-center">
<p className="font-medium text-black">
{selectLang === "ko" ? kosinger : jpsinger}
</p>
<div className="relative ml-auto ">
<LangButton
setSelectLang={setSelectLang}
kolyrics={kolyrics}
jplyrics={jplyrics}
/>
</div>
</div>
{/* 카드 제목 */}
<h1 className="mt-2 text-2xl font-medium text-black">
{selectLang === "ko" ? kotitle : jptitle}
</h1>
</div>
{/* 작성자 좋아요 */}
<div className="flex border-t border-music-basicgray px-5 py-3">
<div className="flex items-center gap-1">
<div>
<MdOutlineUpdate className="text-lg text-black" />
</div>
<div className="text-sm text-black ">{date.slice(0, 10)}</div>
</div>
<div className="ml-auto flex items-center gap-1 text-sm">
<div>
<LikeCount music={musicData} />
</div>
</div>
</div>
</div>
)
}
export default memo(MusicCard)
✅useMemo
useMemo는 주어진 함수를 호출하고 그 결과를 메모한다.
titleInfo는 lan 값의 변화에 따라 title과 description이 달라지는데, 다른 값들이 변경할 때마다 다시 렌더링 되어서, 아래와 같이 useMemo를 통해 값을 메모하고, 동일한 값일 경우 굳이 재렌더링 되지않게 만들어주었다.
1
2
3
4
5
6
const titleInfo = useMemo(() => {
return {
title: lan["edit-music-title"],
description: lan["edit-music-description"],
}
}, [lan])
✅useCallback
useCallback은 주로 함수를 prop으로 넘길 때 사용되고, 함수가 의존하는 값이 변경되었을 때만 새로운 함수가 생성된다.
setTitle은 lan값의 변화에 따라 setThisTitle이란 상태를 업데이트 하는 새로운 함수를 실행하는 것이므로, useCallback을이용해 메모화 해주었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const setTitle = useCallback(
(lyricsVer: string) => {
switch (lyricsVer) {
case "koVer":
setThisTitle(lan["music-title-korean"])
break
case "jpVer":
setThisTitle(lan["music-title-japanese"])
break
case "koCompare":
setThisTitle(lan["music-title-jpver-korean"])
break
case "jpCompare":
setThisTitle(lan["music-title-kover-japanese"])
break
default:
setThisTitle(lan["music-title-korean"])
}
},
[lan]
)