[React] useMemo์ useCallback๋ก ๋ฆฌ์กํธ ๋ ๋๋ง ์ฑ๋ฅ ์ต์ ํํ๊ธฐ(Feat. React.memo)
์๋ ํ์ธ์. ๋ฆฌ์กํธ๋ฅผ ๋ค๋ฃจ๋ค ๋ณด๋ฉด ๋ ๋๋ง ์ฑ๋ฅ ์ต์ ํ์ ๋ํ ์ด์๋ฅผ ์ ํ๊ธฐ ๋ง๋ จ์ ๋๋ค.
๋ฆฌ์กํธ์์๋ ๋ ๋๋ง ์ฑ๋ฅ ์ต์ ํ์ ๊ดํ ๋ํ์ ์ธ ํ
์ผ๋ก useMemo
์ useCallback
์ด ์์ต๋๋ค.
์ ๋ ๊ฐ์ธ์ ์ผ๋ก useMemo์ useCallback์ด ๋ญ๊ฐ ๋ค๋ฅธ์ง, ์ด๋ค ์ํฉ์์ ์จ์ผ ๋ ๋๋ง ์ฑ๋ฅ ์ต์ ํ๋ฅผ ํ ์ ์์์ง ๊ต์ฅํ ๋ง์ด ํท๊ฐ๋ ธ์๋๋ฐ์~
์ค๋์ useMemo
์ useCallback
์ ์ฐจ์ด์ ์ ๋ํด ์์ธํ ์์๋ณด๊ณ ์ด ๋์ ์ฌ์ฉ๋ฒ์ ๊ดํด์๋ ๊ฐ๋จํ๊ฒ ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
โ๏ธ useMemo
useMemo
์์ Memo์ ์๋ฏธ๋ ์ฐ๋ฆฌ๊ฐ ์ด๋ค ์๊ฐ์ด ๋ ๋๋ง๋ค ์์ฑํ๋ ๋ฉ๋ชจ๋ผ๋ ๋ป์ด ์๋๋๋ค.
์ฌ๊ธฐ์ Memo๋ผ๋ ๊ฑด Memoization์ ์ถ์ฝํ Memo๋ผ๋ ๋ป์ ๋๋ค.
Memoization, ๋ฉ๋ชจ์ด์ ์ด์ ์ด๋ ์ปดํจํฐ ํ๋ก๊ทธ๋จ์ด ๋์ผํ ์ฐ์ฐ์ ๋ฐ๋ณตํด์ผ ํ ๋ ๊ธฐ์กด์ ์ฐ์ฐํ๋ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅํด ๋ฌ์ ๋์ผํ ๊ณ์ฐ์ ๋ฐ๋ณต ์ํ์ ์์ ํจ์จ์ ์ผ๋ก ๋์ํ ์ ์๊ฒ๋ ํ์ฌ ์คํ ์๋๋ฅผ ๋์ด๋ ๊ธฐ์ ์ด๋ผ๊ณ ํด์.
๋ฆฌ์กํธ์์๋ useMemo
hook์ ์ฌ์ฉํด์ ์ด์ ์ ์ฐ์ฐํ ๊ฐ์ ์ฌ์ฌ์ฉํ๋ ๋ฉ๋ชจ์ด์ ์ด์
๊ธฐ์ ์ ์ ์ฉํ ์ ์์ต๋๋ค.
useMemo๋ ๋ฉ๋ชจ์ด์ ์ด์ ๋ ๊ฐ์ ๋ฐํํฉ๋๋ค.
์ฌ์ฉ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
const realRate = getComma(rate)
// useMemo ์ ์ฉ
const realRate = useMemo(() => getComma(rate), [rate])
useMemo๋ก ์ฌ์ฉํ๊ณ ์ ํ๋ ํจ์๋ฅผ ๊ฐ์ธ์ ์์ฑํฉ๋๋ค.
๋ ๋ฒ์งธ ์ธ์๋ ๋ฐฐ์ด์ ๋๋ค. useEffect๋ฅผ ์ฌ์ฉํ ๋ ์์กด์ฑ ๋ฐฐ์ด์ ์ฌ์ฉํ์ฃ ? ๋น์ทํ ๋งฅ๋ฝ์ผ๋ก ๋ฐฐ์ด์ ํจ์์ ์์กด์ฑ ๊ฐ์ ๋ฃ์ด ์ฌ์ฉํฉ๋๋ค.
์์กด์ฑ ๋ฐฐ์ด์ ์๋ ๊ฐ์ด ๋ณ๊ฒฝ๋์์ ๊ฒฝ์ฐ์๋ง ๊ฐ์ ๋ค์ ๊ณ์ฐํ๋ ๋ฐฉ์์ ๋๋ค.
์ด๋จ ๋ ์ฌ์ฉํ๋ฉด ์ข์๊น์? ์ดํด๋ฅผ ๋๊ธฐ ์ํด ์์ ์ฝ๋๋ฅผ ๋ง๋ค์ด ๋ณด์์ต๋๋ค~
๋ฉ์์ง๋ฅผ ์ ๋ ฅํ ์ ์๋ ๊ฐ๋จํ ์ธํ ์ฐฝ, ์ ์ก ๋ฒํผ์ด ์์ต๋๋ค.
์ ๋ ฅ์ ํ๊ฒ ๋๋ฉด ๋ฆฌ์คํธ ํ์์ผ๋ก ์๋์ ์ถ๋ ฅ์ด ๋ฉ๋๋ค.
๋ํ ์ ๋ ฅ๋ ๋ฆฌ์คํธ๋ค์ ๊ฐ์๋ฅผ ์ธ์ด ๋ํ๋ด ์ฃผ๋ ๋ฌธ์ฅ๋ ํจ๊ป ์์ต๋๋ค.
์ ์ฒด ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
import { useState, useMemo } from "react";
export default function App() {
const [item, setItem] = useState("");
const [itemList, setItemList] = useState([]);
const handleItem = (event) => {
setItem(event.target.value);
};
const handleSubmit = (event) => {
setItemList([...itemList, item]);
event.preventDefault();
setItem("");
};
const itemCount = (item) => {
console.log("item ๊ฐ์๋ฅผ ์
๋๋ค");
return item.length;
};
const len = itemCount(itemList);
return (
<div className="App">
<form onSubmit={handleSubmit}>
<input onChange={handleItem} value={item} />
<button type="submit">์ ์ก</button>
</form>
<ol>
{itemList.map((item, i) => (
<ul key={i}>{item}</ul>
))}
</ol>
<p>itemList ๊ฐ์๋ {len}๊ฐ</p>
</div>
);
}
์ฌ๊ธฐ์ ์ฃผ๋ชฉํด์ผ ํ ๋ถ๋ถ์ itemCount
ํจ์์ ์ด ํจ์๋ฅผ ์ฌ์ฉํ๋ ๋ถ๋ถ์
๋๋ค.
const itemCount = (item) => {
console.log("item ๊ฐ์๋ฅผ ์
๋๋ค");
return item.length;
};
const len = itemCount(itemList);
์ด ์ํ๋ก๋ผ๋ฉด ์ฝ์ ์ฐฝ์ ์ด๋ป๊ฒ ์ถ๋ ฅ๋๋์ง ํ์ธํด ๋ณด๊ฒ ์ต๋๋ค.
์ ๋ ฅ์ด ๋๊ธฐ ์ ์ด๋ผ ์ธํ ์ฐฝ์ value๋ง ๋ณ๊ฒฝ์ด ๋๋๋ฐ๋ itemCount ํจ์๊ฐ ์คํ๋๋ฉด์ ์ฝ์ ์ฐฝ์ ์ถ๋ ฅ์ด ๋ฉ๋๋ค.
์ด๋ฐ ๊ฒฝ์ฐ์ useMemo๋ฅผ ์ฌ์ฉํ์ฌ ๋ ๋๋ง์ต์ ํ๋ฅผ ํ ์ ์์ต๋๋ค.
const len = useMemo(() => itemCount(itemList), [itemList]);
์์ ์ค๋ช ํ๋ ๋ฐฉ์๋๋ก useMemo๋ฅผ ์ฌ์ฉํ ํ, ์ฝ์ ์ฐฝ์๋ itemList๊ฐ ๋ณ๊ฒฝ๋์์ ๋๋ง ๋ฉ์์ง๊ฐ ์ถ๋ ฅ๋๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
โ๏ธ useCallback
useCallback
์ useMemo์๋ ์ฝ๊ฐ ๋ค๋ฅด๊ฒ ๋ฉ๋ชจ์ด์ ์ด์
๋ ์ฝ๋ฐฑ์ ๋ฐํํฉ๋๋ค.
์ฝ๊ฒ ๋งํด ๋ณด์๋ฉด, useMemo๋ ๊ฐ์ ์ฐ์ฐํ ๋ ์ฌ์ฉํ๋ค๋ฉด useCallback์ ๋ฉ๋ชจ์ด์ ์ด์ ์ ํจ์์ ์ ์ฉํ๊ณ ์ถ์ ๋ ์ฌ์ฉํฉ๋๋ค.
์ฌ์ฉ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
const callback = () => {
doSomething(a, b);
}
// useCallback ์ ์ฉ ํ
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
์์ ๊ฐ์ด ํจ์๋ฅผ useCallback
์ผ๋ก ๊ฐ์ธ ์ฃผ๊ณ , ์์์ ์ฌ์ฉ๋๋ ์์กด์ฑ ๊ฐ๋ค์ ๋ฐฐ์ด ์์ ์์ฑํด ์ฃผ๋ฉด ๋ฉ๋๋ค.
useMemo์ ์ฌ์ฉ๋ฒ์ ๋น์ทํ์ฃ ?
useCallback
์ ๋ณดํต ์ด๋ฒคํธ ํธ๋ค๋ฌ ํจ์๋ API๋ฅผ ์์ฒญํ๋ ํจ์๋ฅผ ์์ฑํ ๋ ์ฃผ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
๋ฆฌ์กํธ ๊ณต์ ๋ฌธ์์ ๋ฐ๋ฅด๋ฉด ํ์ ์ปดํฌ๋ํธ๊ฐ React.memo()
๊ฐ์ ๊ฒ์ผ๋ก ์ต์ ํ๋์ด ์๊ณ , ๊ทธ ํ์ ์ปดํฌ๋ํธ์๊ฒ callback ํจ์๋ฅผ props๋ก ๋๊ธธ ๋ ์์ ์ปดํฌ๋ํธ์์ useCallback์ผ๋ก ํจ์๋ฅผ ์ ์ธํ๋ ๊ฒ์ด ์ ์ฉํ๋ค๊ณ ํฉ๋๋ค.
๊ทธ ์ด์ ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
์์ ์ปดํฌ๋ํธ์์ callback ํจ์๋ฅผ ์ฌ์ ์ธํ๋ค๋ฉด, ๋์ผํ ํจ์์ผ์ง๋ผ๋ props๋ก callback ํจ์๋ฅผ ๋๊ฒจ ๋ฐ๋ ํ์ ์ปดํฌ๋ํธ ์ ์ฅ์์๋ props๊ฐ ๋ณ๊ฒฝ๋์๋ค๊ณ ์ธ์ํ๊ฒ ๋ฉ๋๋ค. React.memo๋ก ํจ์ํ ์ปดํฌ๋ํธ ์์ฒด๋ฅผ ๊ฐ์ผ๋ค๋ฉด ๋๊ฒจ ๋ฐ๋ props๊ฐ ๋ณ๊ฒฝ๋์ง ์์์ ๋๋ ์์ ์ปดํฌ๋ํธ๊ฐ ๋ฉ๋ชจ์ด์ ์ด์ ๋ ํจ์ํ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฏ๋ก, ์ด์ ์ ๋ ๋๋งํ ๊ฒฐ๊ณผ๋ฅผ ์ฌ์ฉํ ์ ์๋ค๊ณ ํ๋ค์.
useCallback์ ์์ธํ ์ฌ์ฉ๋ฒ๋ ์์ ์ฝ๋๋ก ํ์ธํด ๋ด ์๋ค!
์์์ useMemo์ ์์๋ก ๋ค์๋ ๊ฐ์ ์ฝ๋๋ฅผ ์์ฉํ์ต๋๋ค. ๊ธฐ์กด ์ฝ๋์์ itemList์ ์ถ๋ ฅ ๋ถ๋ถ์ ๋ฐ๋ก ItemList ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌํด ๋์์ด์.
React Developer Tools๋ผ๋ ํฌ๋กฌ ์ต์คํ ์ ์ ์ฌ์ฉํด์ ๋ ๋๋ง๋๋ ์ปดํฌ๋ํธ๋ฅผ ํ์ธํด ๋ณผ ์ ์์ต๋๋ค. ํ์ธํด ๋ณด๋ฉด, ์ ๋ ฅ์ฐฝ์ ๋ฉ์์ง๋ฅผ ์น๋ ๊ฒ๋ง์ผ๋ก๋ ๊ทธ ์ธ์ ๋ถ๋ถ๋ ๊ฐ์ด ๋ฆฌ๋ ๋๋ง์ด ๋๋ ๋ชจ์ต์ ํ์ธํ ์ ์์ด์.
์ด ๋ถ๋ถ์ React.memo์ useCallback์ ์ฌ์ฉํด์ ์ต์ ํํด ๋ณด๋๋ก ํ ๊ฒ์.
// App.js
import { useState, useMemo } from "react";
import ItemList from "./ItemList";
export default function App() {
const [item, setItem] = useState("");
const [itemList, setItemList] = useState([]);
const handleItem = (event) => {
setItem(event.target.value);
};
const handleSubmit = (event) => {
setItemList([...itemList, item]);
event.preventDefault();
setItem("");
};
const itemCount = (item) => {
console.log("item ๊ฐ์๋ฅผ ์
๋๋ค");
return item.length;
};
const len = useMemo(() => itemCount(itemList), [itemList]);
const modifyItem = (e) => {
if (e.target.style.color !== "red") {
e.target.style.color = "red";
} else {
e.target.style.color = "black";
}
};
return (
<div className="App">
<form onSubmit={handleSubmit}>
<input onChange={handleItem} value={item} />
<button type="submit">์ ์ก</button>
</form>
<ItemList items={itemList} modifyItem={modifyItem} />
<p>itemList ๊ฐ์๋ {len}๊ฐ</p>
</div>
);
}
ItemList์ li๋ค์ ํด๋ฆญํ๋ฉด ์์์ ๋ณ๊ฒฝํ ์ ์๋๋ก modifyItem
์ด๋ผ๋ ๋ฉ์๋๋ฅผ ์ถ๊ฐ๋ก ๊ตฌํํ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ๋ฉ์๋๋ฅผ ItemList์๊ฒ ์ ๋ฌํด ์ฃผ๋๋ก ํ ๊ฒ์.
// ItemList.js
import React from "react";
function ItemList({ items, modifyItem }) {
return (
<div>
<ol>
{items.map((item, i) => (
<ul key={i}>
<p onClick={modifyItem}>{item}</p>
</ul>
))}
</ol>
</div>
);
}
export default React.memo(ItemList);
ItemList์์๋ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ฌ๊ธฐ์ React.memo์ ์ฌ์ฉ๋ฒ์ ์ ์ฝ๋์์์ฒ๋ผ export ํด ์ฃผ๋ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ธ์ฃผ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค.
const modifyItem = useCallback((e) => {
if (e.target.style.color !== "red") {
e.target.style.color = "red";
} else {
e.target.style.color = "black";
}
}, []);
useCallback
์ ์ฌ์ฉ์ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ๋ ค๋ ํจ์๋ฅผ useCallback
์ผ๋ก ๊ฐ์ธ ์ฃผ๋ฉด ๋ฉ๋๋ค.
useMemo์์ ์์กด์ฑ ๋ฐฐ์ด์ ์ถ๊ฐํด ์ค ๊ฒ๊ณผ ๋๊ฐ์ด useCallback์์๋ ์ฌ์ฉํด ์ฃผ๋ฉด ๋ฉ๋๋ค.
์์์์๋ ์ด๋ฒคํธ์ ์์กด๋ ํจ์์ด๊ธฐ ๋๋ฌธ์ ์์กด์ฑ ๋ฐฐ์ด์ ๊ฐ์ ๋ฐ๋ก ๋ฃ์ง ์์๋๋ฐ์. ์์์ ์๋ํ๋ ํจ์๊ฐ ์์กดํด์ ์ฌ์ฉ๋๋ ๊ฐ์ด ์๋ค๋ฉด ์์กด์ฑ ๋ฐฐ์ด์ ์ถ๊ฐํด ์ฃผ๋ฉด ๋๊ฒ ์ต๋๋ค.
์ ๋ ฅ์ ์๋ฃํ๊ณ ์ ์ก์ ํ์ ๋๋ง ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค. ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง ๋ฐฉ์ง๋ฅผ ํ ์ ์๋ค์!
๊ฐ์ธ์ ์ผ๋ก ์ฒ์ ์ ํ์ ๋ ์กฐ๊ธ ํท๊ฐ๋ ธ๋ useMemo์ useCallback์ ์ ๋ฆฌํด ๋ณด์์ต๋๋ค. ์ ๋ฆฌ๋ฅผ ํ๋ฉด์, ๊ทธ๋ฆฌ๊ณ ์ด๋จ ๋ ์ฌ์ฉํ๋์ง ์์ธํ ์์๋ณด๋ฉด์ ๊ฐ๋ ์ ๋ํด ๋จธ๋ฆฟ์์์๋ ์๋ฆฌ๊ฐ ์กํ ๊ฒ ๊ฐ์์. ์ด ๊ธ์ ๋ณด์๋ ๋ถ๋ค๊ป๋ ๋์์ด ๋์์ผ๋ฉด ์ข๊ฒ ์ต๋๋ค. โบ๏ธ
๐ฉ ์ฐธ๊ณ
๋ฒจ๋กํผํธ์ ํจ๊ปํ๋ ๋ชจ๋ ๋ฆฌ์กํธ