title |
---|
useMemo |
useMemo
๋ ์ฌ๋ ๋๋ง ์ฌ์ด์ ๊ณ์ฐ ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑํ ์ ์๊ฒ ํด์ฃผ๋ React Hook ์
๋๋ค.
const cachedValue = useMemo(calculateValue, dependencies)
์ปดํฌ๋ํธ์ ์ต์์ ๋ ๋ฒจ์ ์๋ 'useMemo'๋ฅผ ํธ์ถํ์ฌ ์ฌ๋ ๋๋ง ์ฌ์ด์ ๊ณ์ฐ์ ์บ์ฑํฉ๋๋ค.
import { useMemo } from 'react';
function TodoList({ todos, tab }) {
const visibleTodos = useMemo(
() => filterTodos(todos, tab),
[todos, tab]
);
// ...
}
์๋๋ก ์ด๋ํ์ฌ ๋ ๋ง์ ์์๋ฅผ ํ์ธํ์ธ์.
-
calculateValue
: ์บ์ฑํ๋ ค๋ ๊ฐ์ ๊ณ์ฐํ๋ ํจ์์ ๋๋ค. ์์ํด์ผ ํ๋ฉฐ ์ธ์๋ฅผ ๋ฐ์ง ์๊ณ , ๋ชจ๋ ํ์ ์ ๊ฐ์ ๋ฐํํ ์ ์์ด์ผ ํฉ๋๋ค. React๋ ์ด๊ธฐ ๋ ๋๋ง ์ค์ ํจ์๋ฅผ ํธ์ถํฉ๋๋ค. ๋ค์ ๋ ๋๋ง์์, React๋ ๋ง์ง๋ง ๋ ๋๋ง ์ดํdependencies
๊ฐ ๋ณ๊ฒฝ๋์ง ์์์ ๋ ๋์ผํ ๊ฐ์ ๋ค์ ๋ฐํํฉ๋๋ค. ๊ทธ๋ ์ง ์๋ค๋ฉดcalculateValue
๋ฅผ ํธ์ถํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ฉฐ, ๋์ค์ ์ฌ์ฌ์ฉํ ์ ์๋๋ก ์ ์ฅํฉ๋๋ค. -
dependencies
:calculateValue
์ฝ๋ ๋ด์์ ์ฐธ์กฐ๋ ๋ชจ๋ ๋ฐ์ํ ๊ฐ๋ค์ ๋ชฉ๋ก์ ๋๋ค. ๋ฐ์ํ ๊ฐ์๋ props, state์ ์ปดํฌ๋ํธ ๋ฐ๋์ ์ง์ ์ ์ธ๋ ๋ชจ๋ ๋ณ์์ ํจ์๊ฐ ํฌํจ๋ฉ๋๋ค. ๋ง์ฝ linter๊ฐ React์ฉ์ผ๋ก ์ค์ ๋ ๊ฒฝ์ฐ ๋ชจ๋ ๋ฐ์ํ ๊ฐ์ด ์์กด์ฑ์ผ๋ก ์ฌ๋ฐ๋ฅด๊ฒ ์ค์ ๋์๋์ง ํ์ธํ ์ ์์ต๋๋ค. ์์กด์ฑ ๋ชฉ๋ก์ ์ผ์ ํ ์์ ํญ๋ชฉ์ ๊ฐ์ ธ์ผ ํ๋ฉฐ,[dep1, dep2, dep3]
์ ๊ฐ์ด ์ธ๋ผ์ธ ํํ๋ก ์์ฑ๋ผ์ผ ํฉ๋๋ค. React๋Object.is
๋น๊ต๋ฅผ ํตํด ๊ฐ ์์กด์ฑ ๋ค์ ์ด์ ๊ฐ๊ณผ ๋น๊ตํฉ๋๋ค.
์ด๊ธฐ ๋ ๋๋ง์์ useMemo
๋ ์ธ์ ์์ด calculateValue
๋ฅผ ํธ์ถํ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํฉ๋๋ค.
๋ค์ ๋ ๋๋ง์์, ๋ง์ง๋ง ๋ ๋๋ง์์ ์ ์ฅ๋ ๊ฐ์ ๋ฐํํ๊ฑฐ๋(์ข
์์ฑ์ด ๋ณ๊ฒฝ๋์ง ์์ ๊ฒฝ์ฐ), calculateValue
๋ฅผ ๋ค์ ํธ์ถํ๊ณ ๋ฐํ๋ ๊ฐ์ ์ ์ฅํฉ๋๋ค.
useMemo
๋ Hook์ด๋ฏ๋ก ์ปดํฌ๋ํธ์ ์ต์์ ๋ ๋ฒจ ๋๋ ์์ฒด Hook์์๋ง ํธ์ถํ ์ ์์ต๋๋ค. ๋ฐ๋ณต๋ฌธ์ด๋ ์กฐ๊ฑด๋ฌธ ๋ด๋ถ์์๋ ํธ์ถํ ์ ์์ต๋๋ค. ๋ง์ผ ํธ์ถ์ด ํ์ํ๋ค๋ฉด ์ ์ปดํฌ๋ํธ๋ฅผ ์ถ์ถํ๊ณ ์ํ๋ฅผ ๊ทธ ์์ผ๋ก ์ฎ๊ฒจ์ผ ํฉ๋๋ค.- Strict Mode์์๋ , React๋ ์ค์๋ก ๋ฐ์ํ ์ค๋ฅ๋ฅผ ์ฐพ๊ธฐ ์ํด ๊ณ์ฐ ํจ์๋ฅผ ๋ ๋ฒ ํธ์ถํฉ๋๋ค. ์ด๋ ๊ฐ๋ฐ ํ๊ฒฝ์์๋ง ๋์ํ๋ ๋ฐฉ์์ด๋ฉฐ, ์ค์ ํ๋ก๋์ ํ๊ฒฝ์๋ ์ํฅ์ ๋ฏธ์น์ง ์์ต๋๋ค. (์๋ ๊ทธ๋์ผ ํ๋ ๊ฒ์ฒ๋ผ) ์ฐ์ฐ ํจ์๊ฐ ์์ํ๋ค๋ฉด, ๋ก์ง์๋ ์ํฅ์ ๋ฏธ์น์ง ์์ต๋๋ค. ํธ์ถ ๊ฒฐ๊ณผ ์ค ํ๋๋ ๋ฌด์๋ฉ๋๋ค.
- React๋ ์บ์ฑ ๋ ๊ฐ์ ๋ฒ๋ ค์ผ ํ ํน๋ณํ ์ด์ ๊ฐ ์๋ ํ ๋ฒ๋ฆฌ์ง ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ๊ฐ๋ฐ ๋จ๊ณ์์ ์ปดํฌ๋ํธ ํ์ผ์ ํธ์งํ ๋ React๋ ์บ์๋ฅผ ๋ฒ๋ฆฝ๋๋ค. ๊ฐ๋ฐ๊ณผ ํ๋ก๋์
ํ๊ฒฝ ๋ชจ๋์์๋ ์ปดํฌ๋ํธ๊ฐ ์ด๊ธฐ ๋ง์ดํธ ์ค์ ์ผ์ ์ค๋จ๋๋ฉด React๋ ์บ์๋ฅผ ๋ฒ๋ฆฝ๋๋ค. ์์ผ๋ก React๋ ์บ์๋ฅผ ๋ฒ๋ฆฌ๋ ๊ฒ์ ํ์ฉํ๋ ๋ ๋ง์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ์์ผ๋ก React์ ๊ฐ์ํ๋ ๋ชฉ๋ก์ ๋ํ ๊ธฐ๋ณธ์ ์ธ ์ง์์ด ์ถ๊ฐ๋๋ค๋ฉด ๊ฐ์ํ๋ ํ
์ด๋ธ ๋ทฐํฌํธ์์ ์คํฌ๋กค ๋๋ ํญ๋ชฉ์ ๋ํ ์บ์๋ฅผ ๋ฒ๋ฆฌ๋ ๊ฒ์ด ํฉ๋ฆฌ์ ์ผ ๊ฒ์
๋๋ค. ์ด๋ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํด
useMemo
์๋ง ์์กดํ๋ค๋ฉด ๊ด์ฐฎ์ ๊ฒ์ ๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ ์ํ ๋ณ์๋ ref๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ์ ํฉํ ์ ์์ต๋๋ค.
์ด์ ๊ฐ์ด ๋ฐํ๊ฐ์ ์บ์ฑํ๋ ๊ฒ์ memoization๋ผ๊ณ ํ๋ฉฐ, ์ด ํ
์ useMemo
๋ผ๊ณ ๋ถ๋ฅด๋ ์ด์ ์๋๋ค.
์ฌ๋ ๋๋ง ์ฌ์ด์ ๊ณ์ฐ์ ์บ์ฑํ๋ ค๋ฉด ์ปดํฌ๋ํธ์ ์ต์์ ๋ ๋ฒจ์์ useMemo
๋ฅผ ํธ์ถํ์ฌ ๊ณ์ฐ์ ๊ฐ์ธ๋ฉด ๋ฉ๋๋ค.
import { useMemo } from 'react';
function TodoList({ todos, tab, theme }) {
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
// ...
}
useMemo
์ ๋ ๊ฐ์ง๋ฅผ ์ ๋ฌํด์ผ ํฉ๋๋ค.
() =>
์ ๊ฐ์ด ์ธ์๋ฅผ ๋ฐ์ง ์๊ณ ๊ณ์ฐํ๋ ค๋ ๊ฐ์ ๋ฐํํ๋ ๊ณ์ฐ ํจ์ ์ ๋๋ค.- ๊ณ์ฐ ๋ด๋ถ์์ ์ฌ์ฉ๋๋ ์ปดํฌ๋ํธ ๋ด์ ๋ชจ๋ ๊ฐ์ ํฌํจํ๋ ์ข ์์ฑ ๋ชฉ๋ก ์ ๋๋ค.
์ด๊ธฐ ๋ ๋๋ง์์ useMemo
์์ ์ป์ ์ ์๋ ๊ฐ์ ๊ณ์ฐ ํจ์๋ฅผ ํธ์ถํ ๊ฒฐ๊ณผ๊ฐ ์
๋๋ค.
์ดํ ๋ชจ๋ ๋ ๋๋ง์์ React๋ ์ข
์์ฑ ๋ชฉ๋ก์ ๋ง์ง๋ง ๋ ๋๋ง ์ค์ ์ ๋ฌํ ์ข
์์ฑ ๋ชฉ๋ก๊ณผ ๋น๊ตํฉ๋๋ค. ๋ง์ผ (Object.is
์ ๋น๊ตํ์ ๋) ์ข
์์ฑ์ด ๋ณ๊ฒฝ๋์ง ์์๋ค๋ฉด, useMemo
๋ ์ด์ ์ ์ด๋ฏธ ๊ณ์ฐํด๋ ๊ฐ์ ๋ฐํํฉ๋๋ค. ๊ทธ๋ ์ง ์๋ค๋ฉด React๋ ๊ณ์ฐ์ ๋ค์ ์คํํ๊ณ ์๋ก์ด ๊ฐ์ ๋ฐํํฉ๋๋ค.
์ฆ, useMemo
๋ ์ข
์์ฑ์ด ๋ณ๊ฒฝ๋๊ธฐ ์ ๊น์ง ์ฌ๋ ๋๋ง ์ฌ์ด์ ๊ณ์ฐ ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑํฉ๋๋ค.
์ด ๊ธฐ๋ฅ์ด ์ธ์ ์ ์ฉํ์ง ์์๋ฅผ ํตํด ์ดํด๋ณด๊ฒ ์ต๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก React๋ ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ ๋๋งํ ๋๋ง๋ค ์ปดํฌ๋ํธ์ ์ ์ฒด ๋ณธ๋ฌธ์ ๋ค์ ์คํํฉ๋๋ค. ์๋ฅผ ๋ค์ด, TodoList
๊ฐ ์ํ๋ฅผ ์
๋ฐ์ดํธํ๊ฑฐ๋ ๋ถ๋ชจ๋ก๋ถํฐ ์๋ก์ด props๋ฅผ ๋ฐ์ผ๋ฉด filterTodos
ํจ์๊ฐ ๋ค์ ์คํ๋ฉ๋๋ค.
function TodoList({ todos, tab, theme }) {
const visibleTodos = filterTodos(todos, tab);
// ...
}
์ผ๋ฐ์ ์ผ๋ก ๋๋ถ๋ถ์ ๊ณ์ฐ์ ๋งค์ฐ ๋น ๋ฅด๊ธฐ ๋๋ฌธ์ ๋ฌธ์ ๊ฐ ๋์ง ์์ต๋๋ค. ๊ทธ๋ฌ๋ ํฐ ๋ฐฐ์ด์ ํํฐ๋ง ํน์ ๋ณํํ๊ฑฐ๋ ๋น์ฉ์ด ๋ง์ด ๋๋ ๊ณ์ฐ์ ์ํํ๋ ๊ฒฝ์ฐ, ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋์ง ์์๋ค๋ฉด ๊ณ์ฐ์ ์๋ตํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ๋ง์ฝ todos
๊ณผ tab
์ด ๋ง์ง๋ง ๋ ๋๋ง ๋์ ๋์ผํ ๊ฒฝ์ฐ, ์์ ์ธ๊ธํ ๊ฒ์ฒ๋ผ useMemo
๋ก ๊ณ์ฐ์ ๊ฐ์ธ๋ฉด ์ด์ ์ ๊ณ์ฐ๋ visibleTodos
๋ฅผ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ด๋ฌํ ์ ํ์ ์บ์ฑ์ ๋ฉ๋ชจ์ด์ ์ด์ ๋ผ๊ณ ํฉ๋๋ค.
์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํด์๋งuseMemo
๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค. ์ด ๊ธฐ๋ฅ์ด ์์ด์ ์ฝ๋๊ฐ ์๋ํ์ง ์๋๋ค๋ฉด ๊ทผ๋ณธ์ ์ธ ๋ฌธ์ ๋ฅผ ๋จผ์ ์ฐพ์์ ์์ ํ์ธ์. ๊ทธ ํ useMemo
๋ฅผ ์ฌ์ฉํ์ฌ ์ฑ๋ฅ์ ๊ฐ์ ํด์ผ ํฉ๋๋ค.
์ผ๋ฐ์ ์ผ๋ก ์์ฒ ๊ฐ์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ฑฐ๋ ๋ฐ๋ณตํ๋ ๊ฒฝ์ฐ๊ฐ ์๋๋ผ๋ฉด ๋น์ฉ์ด ๋ง์ด ๋ค์ง ์์ต๋๋ค. ์กฐ๊ธ ๋ ์ ํํ๊ฒ ํ์ธํ๊ณ ์ถ๋ค๋ฉด ์ฝ์ ๋ก๊ทธ๋ฅผ ์ถ๊ฐํ์ฌ ์ฝ๋์ ์์๋ ์๊ฐ์ ์ธก์ ํ ์ ์์ต๋๋ค.
console.time('filter array');
const visibleTodos = filterTodos(todos, tab);
console.timeEnd('filter array');
์ธก์ ํ๋ ค๋ ์ํธ์์ฉ(์์: Input์ ์
๋ ฅ)์ ์ํํฉ๋๋ค. ๊ทธ๋ฌ๋ฉด filter array: 0.15ms
์ ๊ฐ์ ๋ก๊ทธ๊ฐ ์ฝ์์ ํ์๋ฉ๋๋ค. ์ ์ฒด์ ์ผ๋ก ๊ธฐ๋ก๋ ์๊ฐ์ด ํด ๋(์์: 1ms
์ด์) ํด๋น ๊ณ์ฐ์ ๋ฉ๋ชจํด ๋๋ ๊ฒ์ด ์ข์ต๋๋ค. ๊ทธ๋ฐ ๋ค์ ์คํ์ ์ผ๋ก useMemo
๋ก ๊ณ์ฐ์ ๊ฐ์ธ์ ์ํธ์์ฉ์ ๋ํ ์ด ์๊ฐ์ด ๊ฐ์ํ๋์ง๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
console.time('filter array');
const visibleTodos = useMemo(() => {
return filterTodos(todos, tab); // todo์ tab์ด ๋ณ๊ฒฝ๋์ง ์์ ๊ฒฝ์ฐ ๊ฑด๋๋๋๋ค.
}, [todos, tab]);
console.timeEnd('filter array');
useMemo
๋ ์ฒ์ ๋ ๋๋ง์ ๋ ๋น ๋ฅด๊ฒ ๋ง๋ค์ง ์์ต๋๋ค. ์ด๋ ์
๋ฐ์ดํธ ์ ๋ถํ์ํ ์์
์ ๊ฑด๋๋ฐ๋ ๋ฐ ๋์์ด ๋ ๋ฟ์
๋๋ค.
์ปดํจํฐ๊ฐ ์ฌ์ฉ์์ ์ปดํจํฐ๋ณด๋ค ๋น ๋ฅผ ์ ์์ผ๋ฏ๋ก ์ธ์์ ์ผ๋ก ์๋๋ฅผ ๋ฎ์ถ์ด ์ฑ๋ฅ์ ํ ์คํธํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ์๋ฅผ๋ค์ด Chrome์ CPU ์ค๋กํ๋ง ์ต์ ์ ์ ๊ณตํฉ๋๋ค.
๊ฐ๋ฐํ๊ฒฝ์ ๊ฐ์ฅ ์ ํํ ๊ฒฐ๊ณผ๋ฅผ ์ ๊ณตํ์ง๋ ์์ต๋๋ค(์๋ฅผ ๋ค์ด Strict ๋ชจ๋๊ฐ ์ผ์ ธ ์๋ค๋ฉด ๊ฐ ์ปดํฌ๋ํธ๊ฐ ํ ๋ฒ์ด ์๋ ๋ ๋ฒ ๋ ๋๋ง ๋๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค). ๊ฐ์ฅ ์ ํํ ํ์ด๋ฐ์ ์ป์ผ๋ ค๋ฉด ํ๋ก๋์ ์ฉ ์ฑ์ ๋น๋ํ๊ณ ์ฌ์ฉ์๊ฐ ์ฌ์ฉํ๋ ๊ฒ๊ณผ ๋์ผํ ๊ธฐ๊ธฐ์์ ํ ์คํธํ์ธ์.
์ด ์ฌ์ดํธ์ ๊ฐ์ด ๋๋ถ๋ถ์ ์ํธ ์์ฉ์ด ๊ฑฐ์น ๊ฒฝ์ฐ(ํ์ด์ง ๋๋ ์ ์ฒด ์น์ ์ด ๊ต์ฒด๋๋ ๊ฒ๊ณผ ๊ฐ์ด) ๋ฉ๋ชจ์ด์ ์ด์ ์ด ํ์ํ์ง ์์ต๋๋ค. ๋ฐ๋ฉด, ์ฑ์ด ๊ทธ๋ฆผ ํธ์ง๊ธฐ์ ๋น์ทํ๊ณ ๋๋ถ๋ถ์ ์ํธ ์์ฉ์ด ์ธ๋ถํ๋ ๊ฒฝ์ฐ(๋ํ ์ด๋๊ณผ ๊ฐ์) ๋ฉ๋ชจ์ด์ ์ด์ ์ด ๋งค์ฐ ์ ์ฉํ ์ ์์ต๋๋ค.
useMemo
๋ก ์ต์ ํํ๋ ๊ฒ์ ๋ช๋ช ๊ฒฝ์ฐ์๋ง ์ ์ฉํฉ๋๋ค.
useMemo
์ ์ ๋ ฅํ๋ ๊ณ์ฐ์ด ๋์ ๋๊ฒ ๋๋ฆฌ๊ณ ์ข ์์ฑ์ด ๊ฑฐ์ ๋ณ๊ฒฝ๋์ง ์๋ ๊ฒฝ์ฐ.memo
๋ก ๊ฐ์ธ์ง ์ปดํฌ๋ํธ์ prop๋ก ์ ๋ฌํ ๊ฒฝ์ฐ. ๊ฐ์ด ๋ณ๊ฒฝ๋์ง ์์๋ค๋ฉด ๋ ๋๋ง์ ๊ฑด๋๋ฐ๊ณ ์ถ์ ๊ฒ์ ๋๋ค. ๋ฉ๋ชจ์ด์ ์ด์ ์ ์ฌ์ฉํ๋ฉด ์์กด์ฑ์ด ๋์ผํ์ง ์์ ๊ฒฝ์ฐ์๋ง ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ ๋๋งํ ์ ์์ต๋๋ค.- ์ ๋ฌํ ๊ฐ์ ๋์ค์ ์ผ๋ถ Hook์ ์ข
์์ฑ์ผ๋ก ์ด์ฉํ ๊ฒฝ์ฐ. ์๋ฅผ ๋ค์ด, ๋ค๋ฅธ
useMemo
์ ๊ณ์ฐ ๊ฐ์ด ์ฌ๊ธฐ์ ์ข ์๋์ด ์์ ์ ์์ต๋๋ค. ๋๋useEffect
์ ๊ฐ์ ์ข ์๋์ด ์์ ์ ์์ต๋๋ค.
์ด ์ธ๋ ๊ณ์ฐ์ useMemo
๋ก ๊ฐ์ธ๋ ๊ฒ์ ๋ํ ์ด๋์ด ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๊ทธ๋ ๊ฒ ํ๋ค๊ณ ํด์ ํฌ๊ฒ ๋ฌธ์ ๊ฐ ๋๋ ๊ฒ๋ ์๋๋ฏ๋ก ์ผ๋ถ ํ์์๋ ๊ฐ๋ณ ์ฌ๋ก์ ๋ํด ์๊ฐํ์ง ์๊ณ ๊ฐ๋ฅํ ํ ๋ง์ด ๋ฉ๋ชจํ๋ ๋ฐฉ์์ ์ ํํฉ๋๋ค. ์ด ์ ๊ทผ ๋ฐฉ์์ ๋จ์ ์ ์ฝ๋ ๊ฐ๋
์ฑ์ด ๋จ์ด์ง๋ค๋ ๊ฒ์
๋๋ค. ๋ํ, ๋ชจ๋ ๋ฉ๋ชจ์ด์ ์ด์
์ด ํจ๊ณผ์ ์ธ ๊ฒ์ ์๋๋๋ค. "ํญ์ ์๋ก์ด" ๋จ์ผ ๊ฐ๋ง์ผ๋ก๋ ์ ์ฒด ์ปดํฌ๋ํธ์ ๋ํ ๋ฉ๋ชจํ๊ฐ ๊นจ์ง ์ ์๊ธฐ ๋๋ฌธ์
๋๋ค.
์ค์ ๋ก ๋ช ๊ฐ์ง ์์น์ ์งํค๋ฉด ๋ง์ ๋ฉ๋ชจ์ด์ ์ด์ ์ ๋ถํ์ํ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค.
- ์ปดํฌ๋ํธ๊ฐ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ์๊ฐ์ ์ผ๋ก ๊ฐ์ ๋ JSX๋ฅผ ์์์ฒ๋ผ ๋ฐ์๋ค์ด๋๋ก ํ์ธ์. ์ด๋ ๊ฒ ํ๋ฉด ๊ฐ์ธ๋ ๊ตฌ์ฑ ์์๊ฐ ์์ ์ ์ํ๋ฅผ ์ ๋ฐ์ดํธํ๋๋ผ๋ React๋ ์์์ ๋ค์ ๋ ๋๋งํ ํ์๊ฐ ์์ต๋๋ค.
- ์ง์ญ ์ํ๋ฅผ ์ ํธํ๊ณ ํ์ ์ด์์ผ๋ก ์ํ๋ฅผ ์๋ก ์ฌ๋ฆฌ์ง ๋ง์ธ์. ์๋ฅผ ๋ค์ด, ํผ๊ณผ ๊ฐ์ด ์ผ์์ ์ธ ์ํ๋ ์ด๋ค ํญ๋ชฉ์ด ํธ๋ฆฌ์ ๋งจ ์์ ์์นํ๊ฑฐ๋, ์ ์ญ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์๊ฒ ํ์ง ๋ง์ธ์.
- ์์ํ ๋ ๋๋ง ๋ก์ง์ ์ ์งํ์ธ์. ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ ๋๋งํ ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ฑฐ๋ ๋์ ๋๋ ์๊ฐ์ ์ธ ๋ถ์์ฉ์ด ๋ฐ์ํ๋ฉด ์ปดํฌ๋ํธ์ ๋ฒ๊ทธ๊ฐ ์๋ ๊ฒ์ ๋๋ค! ๋ฉ๋ชจ์ด์ ์ด์ ์ ํ๋ ๋์ ๋ฒ๊ทธ๋ฅผ ์์ ํ์ธ์.
- ์ํ๋ฅผ ์ ๋ฐ์ดํธํ๋ ๋ถํ์ํ Effect๋ฅผ ํผํ์ธ์. React ์ฑ์ ๋๋ถ๋ถ์ ์ฑ๋ฅ ๋ฌธ์ ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ฐ๋ณต์ ์ผ๋ก ๋ ๋๋งํ๊ฒ ๋ง๋๋ Effect์ ์ ๋ฐ์ดํธ ์ฒด์ธ์ผ๋ก๋ถํฐ ๋ฐ์ํฉ๋๋ค.
- Effects์์ ๋ถํ์ํ ์ข ์์ฑ์ ์ ๊ฑฐํ์ธ์. ์๋ฅผ ๋ค์ด, ๋ฉ๋ชจ์ด์ ์ด์ ์ ํ๋ ๋์ ์ผ๋ถ ๊ฐ์ฒด๋ ํจ์๋ฅผ Effect ๋ด๋ถ ๋๋ ์ปดํฌ๋ํธ ์ธ๋ถ๋ก ์ด๋ํ๋ ๊ฒ์ด ๋ ๊ฐ๋จํ ๋๊ฐ ์์ต๋๋ค.
ํน์ ์ํธ์์ฉ์ด ์ฌ์ ํ ๋๋ฆฌ๊ฒ ๋๊ปด์ง๋ค๋ฉด React ๊ฐ๋ฐ์ ๋๊ตฌ ํ๋กํ์ผ๋ฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ค ์ปดํฌ๋ํธ๊ฐ ๋ฉ๋ชจ์ด์ ์ด์ ์ ํตํด ๊ฐ์ฅ ํฐ ์ด์ ์ ์ป์ ์ ์๋์ง ํ์ธํ๊ณ ํ์ํ๋ค๋ฉด ์ถ๊ฐํ์ธ์. ์ด๋ฌํ ์์น์ ์ปดํฌ๋ํธ๋ฅผ ๋ ์ฝ๊ฒ ๋๋ฒ๊น ํ๊ณ ์ดํดํ ์ ์๊ฒ ํด์ฃผ๋ฏ๋ก ์ด๋ค ๊ฒฝ์ฐ๋ ์ด ์์น์ ๋ฐ๋ฅด๋ ๊ฒ์ด ์ข์ต๋๋ค. ์ฅ๊ธฐ์ ์ผ๋ก ์ฐ๋ฆฌ๋ ์ด ๋ฌธ์ ๋ฅผ ์์ ํ ํด๊ฒฐํ๊ธฐ ์ํด ์๋์ ์ธ๋ถํ ๋ฉ๋ชจ์ด์ ์ด์ ์ ์ฐ๊ตฌํ๊ณ ์์ต๋๋ค.
์ด ์์ ์์๋ ๋ ๋๋ง ์ค์ ํธ์ถํ๋ ์๋ฐ์คํฌ๋ฆฝํธ ํจ์๊ฐ ์ค์ ๋ก ๋๋ฆด ๋ ์ด๋ค ์ผ์ด ๋ฐ์ํ๋์ง ํ์ธํ ์ ์๋๋ก filterTodos
์ ์ธ์์ ์ผ๋ก ๋๋ฆฌ๊ฒ ๋ง๋ค์์ต๋๋ค. ํญ์ ์ ํํ๊ณ ํ
๋ง๋ฅผ ํ ๊ธํด ๋ณด์ธ์.
ํญ์ ์ ํํ๋ฉด ๋๋ ค์ง filterTodos
๊ฐ ๋ค์ ์คํ๋๋ฏ๋ก ๋๋ฆฌ๊ฒ ๋๊ปด์ง๋๋ค. ์ด๋ tab
์ด ๋ณ๊ฒฝ๋์์ผ๋ฏ๋ก ์ ์ฒด ๊ณ์ฐ์ด ํ์์ ์ผ๋ก ๋ค์ ์คํ๋๊ธฐ ๋๋ฌธ์ ๋ํ๋๋ ํ์์
๋๋ค. (์ ๋ ๋ฒ ์คํ๋๋์ง ๊ถ๊ธํ๋ค๋ฉด ์ฌ๊ธฐ๋ฅผ ํด๋ฆญํด์ ์ค๋ช
์ ํ์ธํ์ธ์.)
ํ
๋ง๋ฅผ ์ ํํฉ๋๋ค. ์ธ์์ ์ธ ์๋ ์ ํ์๋ ๋ถ๊ตฌํ๊ณ ๋น ๋ฅธ ์ด์ ๋ useMemo
๋๋ถ์
๋๋ค! ๋๋ฆฐ ์๋์ filterTodos
๋ ๋ง์ง๋ง ๋ ๋๋ง ์ดํ (useMemo
์ ์ข
์์ฑ์ผ๋ก ์ ๋ฌํ)todos
์ tab
์ด ๋ชจ๋ ๋ณ๊ฒฝ๋์ง ์์๊ธฐ ๋๋ฌธ์ ํธ์ถ์ ๊ฑด๋๋ฐ์์ต๋๋ค.
import { useState } from 'react';
import { createTodos } from './utils.js';
import TodoList from './TodoList.js';
const todos = createTodos();
export default function App() {
const [tab, setTab] = useState('all');
const [isDark, setIsDark] = useState(false);
return (
<>
<button onClick={() => setTab('all')}>
All
</button>
<button onClick={() => setTab('active')}>
Active
</button>
<button onClick={() => setTab('completed')}>
Completed
</button>
<br />
<label>
<input
type="checkbox"
checked={isDark}
onChange={e => setIsDark(e.target.checked)}
/>
Dark mode
</label>
<hr />
<TodoList
todos={todos}
tab={tab}
theme={isDark ? 'dark' : 'light'}
/>
</>
);
}
import { useMemo } from 'react';
import { filterTodos } from './utils.js'
export default function TodoList({ todos, theme, tab }) {
const visibleTodos = useMemo(
() => filterTodos(todos, tab),
[todos, tab]
);
return (
<div className={theme}>
<p><b>Note: <code>filterTodos</code> is artificially slowed down!</b></p>
<ul>
{visibleTodos.map(todo => (
<li key={todo.id}>
{todo.completed ?
<s>{todo.text}</s> :
todo.text
}
</li>
))}
</ul>
</div>
);
}
export function createTodos() {
const todos = [];
for (let i = 0; i < 50; i++) {
todos.push({
id: i,
text: "Todo " + (i + 1),
completed: Math.random() > 0.5
});
}
return todos;
}
export function filterTodos(todos, tab) {
console.log('[ARTIFICIALLY SLOW] Filtering ' + todos.length + ' todos for "' + tab + '" tab.');
let startTime = performance.now();
while (performance.now() - startTime < 500) {
// ๋งค์ฐ ๋๋ฆฐ ์ฝ๋๋ฅผ ๊ตฌํํ๊ธฐ ์ํด 500ms ๋์ ์๋ฌด๊ฒ๋ ํ์ง ์์.
}
return todos.filter(todo => {
if (tab === 'all') {
return true;
} else if (tab === 'active') {
return !todo.completed;
} else if (tab === 'completed') {
return todo.completed;
}
});
}
label {
display: block;
margin-top: 10px;
}
.dark {
background-color: black;
color: white;
}
.light {
background-color: white;
color: black;
}
์ด ์์ ์์๋ ๋ ๋๋ง ์ค์ ํธ์ถํ๋ ์๋ฐ์คํฌ๋ฆฝํธ ํจ์๊ฐ ์ค์ ๋ก ๋๋ฆด ๋ ์ด๋ค ์ผ์ด ๋ฐ์ํ๋์ง ํ์ธํ ์ ์๋๋ก filterTodos
์ ์ธ์์ ์ผ๋ก ๋๋ฆฌ๊ฒ ๋ง๋ค์์ต๋๋ค. ํญ์ ์ ํํ๊ณ ํ
๋ง๋ฅผ ํ ๊ธํด ๋ณด์ธ์.
์ด์ ์์ ์ ๋ฌ๋ฆฌ ํ
๋ง ์ ํ๋ ์ด์ ๋๋ ค์ก์ต๋๋ค! ์ด ์์ ์๋ useMemo
ํธ์ถ์ด ์๊ธฐ ๋๋ฌธ์ ๋ ๋๋ง๋ง๋ค ๋๋ ค์ง filterTodos
๊ฐ ํธ์ถ๋๊ธฐ ๋๋ฌธ์
๋๋ค. ์ด๋ theme
๋ง ๋ณ๊ฒฝํ๋ ๊ฒฝ์ฐ์๋ ํธ์ถ๋ฉ๋๋ค.
import { useState } from 'react';
import { createTodos } from './utils.js';
import TodoList from './TodoList.js';
const todos = createTodos();
export default function App() {
const [tab, setTab] = useState('all');
const [isDark, setIsDark] = useState(false);
return (
<>
<button onClick={() => setTab('all')}>
All
</button>
<button onClick={() => setTab('active')}>
Active
</button>
<button onClick={() => setTab('completed')}>
Completed
</button>
<br />
<label>
<input
type="checkbox"
checked={isDark}
onChange={e => setIsDark(e.target.checked)}
/>
Dark mode
</label>
<hr />
<TodoList
todos={todos}
tab={tab}
theme={isDark ? 'dark' : 'light'}
/>
</>
);
}
import { filterTodos } from './utils.js'
export default function TodoList({ todos, theme, tab }) {
const visibleTodos = filterTodos(todos, tab);
return (
<div className={theme}>
<ul>
<p><b>Note: <code>filterTodos</code> is artificially slowed down!</b></p>
{visibleTodos.map(todo => (
<li key={todo.id}>
{todo.completed ?
<s>{todo.text}</s> :
todo.text
}
</li>
))}
</ul>
</div>
);
}
export function createTodos() {
const todos = [];
for (let i = 0; i < 50; i++) {
todos.push({
id: i,
text: "Todo " + (i + 1),
completed: Math.random() > 0.5
});
}
return todos;
}
export function filterTodos(todos, tab) {
console.log('[ARTIFICIALLY SLOW] Filtering ' + todos.length + ' todos for "' + tab + '" tab.');
let startTime = performance.now();
while (performance.now() - startTime < 500) {
// ๋งค์ฐ ๋๋ฆฐ ์ฝ๋๋ฅผ ๊ตฌํํ๊ธฐ ์ํด 500ms ๋์ ์๋ฌด๊ฒ๋ ํ์ง ์์.
}
return todos.filter(todo => {
if (tab === 'all') {
return true;
} else if (tab === 'active') {
return !todo.completed;
} else if (tab === 'completed') {
return todo.completed;
}
});
}
label {
display: block;
margin-top: 10px;
}
.dark {
background-color: black;
color: white;
}
.light {
background-color: white;
color: black;
}
๊ทธ๋ฌ๋ ๋ค์์ ์ธ์์ ์ผ๋ก ์๋ ์ ํ๋ ๋ถ๋ถ์ ์ ๊ฑฐํ๊ณ ๋๋จธ์ง๋ ๋์ผํ ์ฝ๋์
๋๋ค. useMemo
๋ฅผ ์ฌ์ฉํ์ง ์์ ๊ฒ์ด ์ฒด๊ฐ ๋์๋์?
import { useState } from 'react';
import { createTodos } from './utils.js';
import TodoList from './TodoList.js';
const todos = createTodos();
export default function App() {
const [tab, setTab] = useState('all');
const [isDark, setIsDark] = useState(false);
return (
<>
<button onClick={() => setTab('all')}>
All
</button>
<button onClick={() => setTab('active')}>
Active
</button>
<button onClick={() => setTab('completed')}>
Completed
</button>
<br />
<label>
<input
type="checkbox"
checked={isDark}
onChange={e => setIsDark(e.target.checked)}
/>
Dark mode
</label>
<hr />
<TodoList
todos={todos}
tab={tab}
theme={isDark ? 'dark' : 'light'}
/>
</>
);
}
import { filterTodos } from './utils.js'
export default function TodoList({ todos, theme, tab }) {
const visibleTodos = filterTodos(todos, tab);
return (
<div className={theme}>
<ul>
{visibleTodos.map(todo => (
<li key={todo.id}>
{todo.completed ?
<s>{todo.text}</s> :
todo.text
}
</li>
))}
</ul>
</div>
);
}
export function createTodos() {
const todos = [];
for (let i = 0; i < 50; i++) {
todos.push({
id: i,
text: "Todo " + (i + 1),
completed: Math.random() > 0.5
});
}
return todos;
}
export function filterTodos(todos, tab) {
console.log('Filtering ' + todos.length + ' todos for "' + tab + '" tab.');
return todos.filter(todo => {
if (tab === 'all') {
return true;
} else if (tab === 'active') {
return !todo.completed;
} else if (tab === 'completed') {
return todo.completed;
}
});
}
label {
display: block;
margin-top: 10px;
}
.dark {
background-color: black;
color: white;
}
.light {
background-color: white;
color: black;
}
๋ฉ๋ชจ์ด์ ์ด์ ์์ด๋ ์ฝ๋๊ฐ ์ ์๋ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. ์ํธ ์์ฉ์ด ์ถฉ๋ถ์ด ๋น ๋ฅด๋ค๋ฉด ๋ฉ๋ชจ์ด์ ์ด์ ์ ํ์ํ์ง ์์ ์๋ ์์ต๋๋ค.
utils.js
์์ ํ ์ผ์ ํญ๋ชฉ ์๋ฅผ ๋๋ ค๋ณด๊ณ ๋์์ด ์ด๋ป๊ฒ ๋ฐ๋๋์ง ํ์ธํ ์ ์์ต๋๋ค. ์ด ์ฐ์ฐ์ ์ฒ์์๋ ๋น์ฉ์ด ๋ง์ด ๋ค์ง ์์์ง๋ง ํ ์ผ์ ์๊ฐ ํฌ๊ฒ ์ฆ๊ฐํ๋ฉด ๋๋ถ๋ถ์ ์ค๋ฒํค์ด๊ฐ ํํฐ๋ง์ด ์๋ ์ฌ๋ ๋๋ง์ ๋ฐ์ํฉ๋๋ค. ์๋์์ useMemo
๋ก ์ฌ๋ ๋๋ง์ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด์ธ์.
๊ฒฝ์ฐ์ ๋ฐ๋ผ useMemo
๋ ํ์ ์ปดํฌ๋ํธ ์ฌ๋ ๋๋ง ์ฑ๋ฅ์ ์ต์ ํํ๋๋ฐ ๋์์ด ๋ ์๋ ์์ต๋๋ค. ์ด๋ฅผ ์ค๋ช
ํ๊ธฐ ์ํด TodoList
์ปดํฌ๋ํธ๊ฐ ์์ ์ปดํฌ๋ํธ์ธ List
์ visibleTodos
๋ฅผ prop๋ก ์ ๋ฌํ๋ค๊ณ ๊ฐ์ ํ๊ฒ ์ต๋๋ค.
export default function TodoList({ todos, tab, theme }) {
// ...
return (
<div className={theme}>
<List items={visibleTodos} />
</div>
);
}
theme
prop๋ฅผ ํ ๊ธํ๋ฉด ์ฑ์ด ์ ์ ๋ฉ์ถ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ JSX์์ <List />
๋ฅผ ์ ๊ฑฐํ๋ฉด ๋น ๋ฅด๊ฒ ๋๊ปด์ง๋๋ค. ์ด๋ List
์ปดํฌ๋ํธ๋ฅผ ์ต์ ํํ ๊ฐ์น๊ฐ ์๋ค๋ ๊ฒ์ ์๋ ค์ค๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก React๋ ์ปดํฌ๋ํธ๊ฐ ๋ค์ ๋ ๋๋ง ๋ ๋, ๋ชจ๋ ์์ ์ปดํฌ๋ํธ๋ฅผ ์ฌ๊ท์ ์ผ๋ก ๋ค์ ๋ ๋๋งํฉ๋๋ค. ๊ทธ๋ฌ๋ฏ๋ก TodoList
๊ฐ ๋ค๋ฅธ theme
๋ก ๋ค์ ๋ ๋๋ง ๋๋ฉด List
์ปดํฌ๋ํธ ๋ํ ๋ค์ ๋ ๋๋ง ๋ฉ๋๋ค. ๋ค์ ๋ ๋๋งํ๋ ๋ฐ ๋ง์ ๊ณ์ฐ์ด ํ์ํ์ง ์๋ ์ปดํฌ๋ํธ๋ ๊ด์ฐฎ์ง๋ง, ๋ค์ ๋ ๋๋งํ๋ ๊ฒ์ด ๋๋ฆฌ๋ค๋ ๊ฒ์ ํ์ธํ๋ค๋ฉด List
๋ฅผ memo
๋ฅผ ํตํด ๊ฐ์ธ์ props๊ฐ ๋ง์ง๋ง ๋ ๋๋ง ์์ ๊ณผ ๋์ผ ํ ๋ ๋ค์ ๋ ๋๋งํ๋ ๊ฒ์ ์๋ตํ ์ ์์ต๋๋ค.
import { memo } from 'react';
const List = memo(function List({ items }) {
// ...
});
์ด ๋ณ๊ฒฝ์ผ๋ก List
๋ ๋ชจ๋ props๊ฐ ๋ง์ง๋ง ๋ ๋๋ง ๋์ ๋์ผํ ๊ฒฝ์ฐ ๋ค์ ๋ ๋๋งํ์ง ์์ต๋๋ค. ์ฌ๊ธฐ์ ๊ณ์ฐ์ ์บ์ฑํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค! useMemo
์์ด visibleTodos
๋ฅผ ๊ณ์ฐํ๋ค๊ณ ๊ฐ์ ํด ๋ด
์๋ค.
export default function TodoList({ todos, tab, theme }) {
// ํ
๋ง๊ฐ ๋ณ๊ฒฝ๋ ๋ ๋ง๋ค ๋ค๋ฅธ ๋ฐฐ์ด์ด ํ์๋ฉ๋๋ค.
const visibleTodos = filterTodos(todos, tab);
return (
<div className={theme}>
{/* ... List์ props๋ ๋์ผํ์ง ์์ผ๋ฉฐ ๋งค๋ฒ ๋ค์ ๋ ๋๋ง ๋ฉ๋๋ค. */}
<List items={visibleTodos} />
</div>
);
}
์์ ์์์์ filterTodos
ํจ์๋ ํญ์ ๋ค๋ฅธ ๋ฐฐ์ด์ ์์ฑํฉ๋๋ค. ์ด๋ {}
๊ฐ์ฒด ๋ฆฌํฐ๋ด์ด ํญ์ ์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๊ฒ๊ณผ ์ ์ฌํฉ๋๋ค. ์ผ๋ฐ์ ์ผ๋ก ์ด๋ ๋ฌธ์ ๊ฐ ๋์ง ์์ง๋ง List
์ props๋ ๋์ผํ์ง ์์ผ๋ฉฐ memo
๋ฅผ ์ฌ์ฉํ ์ต์ ํ๊ฐ ์๋ํ์ง ์๋๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ์ด๋ฌํ ๊ฒฝ์ฐ useMemo
๊ฐ ์ ์ฉํฉ๋๋ค.
export default function TodoList({ todos, tab, theme }) {
// ์ฌ๋ ๋๋ง ์ฌ์ด์ ๊ณ์ฐ์ ์บ์ฑํ๋๋ก React์ ์ง์ํฉ๋๋ค...
const visibleTodos = useMemo(
() => filterTodos(todos, tab),
[todos, tab] // ...๋ฐ๋ผ์ ํด๋น ์ข
์์ฑ์ด ๋ณ๊ฒฝ๋์ง ์๋ ํ...
);
return (
<div className={theme}>
{/* ...List์ ๋์ผํ props๊ฐ ์ ๋ฌ๋์ด ์ฌ๋ ๋๋ง์ ์๋ตํ ์ ์์ต๋๋ค. */}
<List items={visibleTodos} />
</div>
);
}
visibleTodos
์ฐ์ฐ์ useMemo
๋ก ๊ฐ์ธ๋ฉด ๋ค์ ๋ ๋๋ง ๋ ๋๋ง๋ค ๊ฐ์ ๊ฐ์ ๊ฐ๊ฒ ํ ์ ์์ต๋๋ค (์ข
์์ฑ์ด ๋ณ๊ฒฝ๋๊ธฐ ์ ๊น์ง). ํน๋ณํ ์ด์ ๊ฐ ์๋ ํ ์ฐ์ฐ์ useMemo
๋ก ๊ฐ์ธ์ง ์์๋ ๋ฉ๋๋ค. ์ด ์์ ์์๋ memo
๋ก ๊ฐ์ธ์ง ์ปดํฌ๋ํธ์ ์ ๋ฌํ๋ฉด ์ฌ๋ ๋๋ง์ ๊ฑด๋๋ธ ์ ์๊ธฐ ๋๋ฌธ์
๋๋ค. ์ด ํ์ด์ง์์ ์์ธํ ์ค๋ช
ํ๋ useMemo
๋ฅผ ์ถ๊ฐํด์ผ ํ๋ ๋ช ๊ฐ์ง ๋ค๋ฅธ ์ด์ ๊ฐ ์์ต๋๋ค.
List
๋ฅผ memo
๋ก ๊ฐ์ธ๋ ๋์ , <List />
๋
ธ๋ ์์ฒด๋ฅผ useMemo
๋ก ๊ฐ์ธ๋ฉด ๋ฉ๋๋ค.
export default function TodoList({ todos, tab, theme }) {
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
const children = useMemo(() => <List items={visibleTodos} />, [visibleTodos]);
return (
<div className={theme}>
{children}
</div>
);
}
๋์ ๋ฐฉ์์ ๋์ผํฉ๋๋ค. visibleTodos
์ด ๋ณ๊ฒฝ๋์ง ์์ ๊ฒฝ์ฐ List
๋ ๋ค์ ๋ ๋๋ง ๋์ง ์์ต๋๋ค.
<List items={visibleTodos} />
์ ๊ฐ์ JSX ๋
ธ๋๋ { type: List, props: { items: visibleTodos } }
์ ๊ฐ์ ๊ฐ์ฒด์
๋๋ค. ์ด ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๊ฒ์ ๋งค์ฐ ์ ๋ ดํ์ง๋ง, React๋ ๊ทธ ๋ด์ฉ์ด ์ง๋๋ฒ๊ณผ ๋์ผํ์ง ์ ์ ์์ต๋๋ค. ๊ทธ๋์ ๊ธฐ๋ณธ์ ์ผ๋ก React๋ List
์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ ๋๋งํฉ๋๋ค.
ํ์ง๋ง React๊ฐ ์ด์ ๋ ๋๋ง๊ณผ ๋์ผํ JSX๋ฅผ ๋ฐ๊ฒฌํ๋ฉด ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ ๋๋งํ๋ ค๊ณ ์๋ํ์ง ์์ต๋๋ค. JSX ๋
ธ๋๋ ๋ถ๋ณํ๊ธฐ ๋๋ฌธ์
๋๋ค. JSX ๋
ธ๋ ๊ฐ์ฒด๋ ์๊ฐ์ด ์ง๋๋ ๋ณ๊ฒฝ๋์ง ์์ผ๋ฏ๋ก React๋ ์ฌ๋ ๋๋ง์ ์๋ตํด๋ ์์ ํ๋ค๋ ๊ฒ์ ์๊ณ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด๊ฒ์ด ๋์ํ๋ ค๋ฉด ๋
ธ๋๊ฐ ๋จ์ํ ์ฝ๋์ ์ผ๋ก ๋์ผํด ๋ณด์ด๋ ๊ฒ์ด ์๋ ์ค์ ๋ก ๋์ผํ ๊ฐ์ฒด์ฌ์ผ ํฉ๋๋ค. ์ด ์์ ์์๋ useMemo
๊ฐ ํด๋น ์ผ์ ์ํํฉ๋๋ค.
JSX ๋
ธ๋๋ฅผ useMemo
๋ก ์๋์ผ๋ก ๊ฐ์ธ๋ ๊ฒ์ ํธ๋ฆฌํ ๋ฐฉ๋ฒ์ ์๋๋๋ค. ์๋ฅผ ๋ค์ด, ์กฐ๊ฑด๋ถ๋ก๋ ์ด ์์
์ ์ํํ ์ ์์ต๋๋ค. ๊ทธ๋์ ๋ณดํต JSX ๋
ธ๋๋ฅผ ๊ฐ์ธ๋ ๋์ ์ปดํฌ๋ํธ๋ฅผ memo
๋ก ๊ฐ์๋๋ค.
์ด ์์ ์์๋ List
์ปดํฌ๋ํธ๋ฅผ ์ธ์์ ์ผ๋ก ๋๋ฆฌ๊ฒ ๋ง๋ค์ด ๋ ๋๋ง ์ค์ธ React ์ปดํฌ๋ํธ๊ฐ ์ค์ ๋ก ๋๋ ค์ง ๋ ์ด๋ค ์ผ์ด ๋ฐ์ํ๋ ์ง๋ฅผ ํ์ธํ ์ ์์ต๋๋ค. ํญ์ ์ ํํ๊ณ ํ
๋ง๋ฅผ ํ ๊ธํด ๋ณด์ธ์.
ํญ์ ์ ํํ๋ฉด ๋๋ ค์ง List
๊ฐ ๋ค์ ๋ ๋๋ง ๋๊ธฐ ๋๋ฌธ์ ๋๋ฆฌ๊ฒ ๋๊ปด์ง๋๋ค. ์ด๋ tab
์ด ๋ณ๊ฒฝ๋์์ผ๋ฏ๋ก ์ฌ์ฉ์์ ์๋ก์ด ์ ํ ์ฌํญ์ ํ๋ฉด์ ๋ฐ์ํด์ผ ํ๊ธฐ ๋๋ฌธ์ ์์๋๋ ํ์์
๋๋ค.
๋ค์์ผ๋ก ํ
๋ง๋ฅผ ํ ๊ธํด ๋ณด๊ฒ ์ต๋๋ค. ์ธ์์ ์ธ ์๋ ์ ํ์๋ ๋ถ๊ตฌํ๊ณ memo
์ ํจ๊ป ์ฌ์ฉ๋ useMemo
๋๋ถ์ ๋น ๋ฆ
๋๋ค! List
๋ ๋ง์ง๋ง ๋ ๋๋ง ์ดํ visibleItems
๋ฐฐ์ด์ด ๋ณ๊ฒฝ๋์ง ์์๊ธฐ ๋๋ฌธ์ ์ฌ๋ ๋๋ง์ ์๋ตํ์ต๋๋ค. (useMemo
์ ์ข
์์ฑ์ผ๋ก ์ ๋ฌ๋) todos
์ tab
์ด ๋ชจ๋ ๋ง์ง๋ง ๋ ๋๋ง ์ดํ ๋ณ๊ฒฝ๋์ง ์์์ผ๋ฏ๋ก visibleItems
๋ฐฐ์ด์ด ๋ณ๊ฒฝ๋์ง ์์์ต๋๋ค.
import { useState } from 'react';
import { createTodos } from './utils.js';
import TodoList from './TodoList.js';
const todos = createTodos();
export default function App() {
const [tab, setTab] = useState('all');
const [isDark, setIsDark] = useState(false);
return (
<>
<button onClick={() => setTab('all')}>
All
</button>
<button onClick={() => setTab('active')}>
Active
</button>
<button onClick={() => setTab('completed')}>
Completed
</button>
<br />
<label>
<input
type="checkbox"
checked={isDark}
onChange={e => setIsDark(e.target.checked)}
/>
Dark mode
</label>
<hr />
<TodoList
todos={todos}
tab={tab}
theme={isDark ? 'dark' : 'light'}
/>
</>
);
}
import { useMemo } from 'react';
import List from './List.js';
import { filterTodos } from './utils.js'
export default function TodoList({ todos, theme, tab }) {
const visibleTodos = useMemo(
() => filterTodos(todos, tab),
[todos, tab]
);
return (
<div className={theme}>
<p><b>Note: <code>List</code> is artificially slowed down!</b></p>
<List items={visibleTodos} />
</div>
);
}
import { memo } from 'react';
const List = memo(function List({ items }) {
console.log('[ARTIFICIALLY SLOW] Rendering <List /> with ' + items.length + ' items');
let startTime = performance.now();
while (performance.now() - startTime < 500) {
// ๋งค์ฐ ๋๋ฆฐ ์ฝ๋๋ฅผ ๊ตฌํํ๊ธฐ ์ํด 500ms ๋์ ์๋ฌด๊ฒ๋ ํ์ง ์์.
}
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.completed ?
<s>{item.text}</s> :
item.text
}
</li>
))}
</ul>
);
});
export default List;
export function createTodos() {
const todos = [];
for (let i = 0; i < 50; i++) {
todos.push({
id: i,
text: "Todo " + (i + 1),
completed: Math.random() > 0.5
});
}
return todos;
}
export function filterTodos(todos, tab) {
return todos.filter(todo => {
if (tab === 'all') {
return true;
} else if (tab === 'active') {
return !todo.completed;
} else if (tab === 'completed') {
return todo.completed;
}
});
}
label {
display: block;
margin-top: 10px;
}
.dark {
background-color: black;
color: white;
}
.light {
background-color: white;
color: black;
}
์ด ์์ ์์๋ List
์ปดํฌ๋ํธ๋ฅผ ์ธ์์ ์ผ๋ก ๋๋ฆฌ๊ฒ ๋ง๋ค์ด ๋ ๋๋ง ์ค์ธ React ์ปดํฌ๋ํธ๊ฐ ์ค์ ๋ก ๋๋ ค์ง ๋ ์ด๋ค ์ผ์ด ๋ฐ์ํ๋ ์ง๋ฅผ ํ์ธํ ์ ์์ต๋๋ค. ํญ์ ์ ํํ๊ณ ํ
๋ง๋ฅผ ํ ๊ธํด ๋ณด์ธ์.
์ด์ ์์ ์ ๋ค๋ฅด๊ฒ ์ด์ ๋ ํ
๋ง ์ ํ๋ ๋๋ ค์ก์ต๋๋ค! ์ด ๋ฒ์ ์๋ useMemo
ํธ์ถ์ด ์๊ธฐ ๋๋ฌธ์ visibleTodos
๋ ํญ์ ๋ค๋ฅธ ๋ฐฐ์ด์ด ๋๊ณ List
์ปดํฌ๋ํธ๋ ์ฌ๋ ๋๋ง์ ์๋ตํ ์ ์๊ธฐ ๋๋ฌธ์
๋๋ค.
import { useState } from 'react';
import { createTodos } from './utils.js';
import TodoList from './TodoList.js';
const todos = createTodos();
export default function App() {
const [tab, setTab] = useState('all');
const [isDark, setIsDark] = useState(false);
return (
<>
<button onClick={() => setTab('all')}>
All
</button>
<button onClick={() => setTab('active')}>
Active
</button>
<button onClick={() => setTab('completed')}>
Completed
</button>
<br />
<label>
<input
type="checkbox"
checked={isDark}
onChange={e => setIsDark(e.target.checked)}
/>
Dark mode
</label>
<hr />
<TodoList
todos={todos}
tab={tab}
theme={isDark ? 'dark' : 'light'}
/>
</>
);
}
import List from './List.js';
import { filterTodos } from './utils.js'
export default function TodoList({ todos, theme, tab }) {
const visibleTodos = filterTodos(todos, tab);
return (
<div className={theme}>
<p><b>Note: <code>List</code> is artificially slowed down!</b></p>
<List items={visibleTodos} />
</div>
);
}
import { memo } from 'react';
const List = memo(function List({ items }) {
console.log('[ARTIFICIALLY SLOW] Rendering <List /> with ' + items.length + ' items');
let startTime = performance.now();
while (performance.now() - startTime < 500) {
// ๋งค์ฐ ๋๋ฆฐ ์ฝ๋๋ฅผ ๊ตฌํํ๊ธฐ ์ํด 500ms ๋์ ์๋ฌด๊ฒ๋ ํ์ง ์์.
}
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.completed ?
<s>{item.text}</s> :
item.text
}
</li>
))}
</ul>
);
});
export default List;
export function createTodos() {
const todos = [];
for (let i = 0; i < 50; i++) {
todos.push({
id: i,
text: "Todo " + (i + 1),
completed: Math.random() > 0.5
});
}
return todos;
}
export function filterTodos(todos, tab) {
return todos.filter(todo => {
if (tab === 'all') {
return true;
} else if (tab === 'active') {
return !todo.completed;
} else if (tab === 'completed') {
return todo.completed;
}
});
}
label {
display: block;
margin-top: 10px;
}
.dark {
background-color: black;
color: white;
}
.light {
background-color: white;
color: black;
}
๊ทธ๋ฌ๋ ๋ค์์ ์ธ์์ ์ธ ์๋ ์ ํ๋ฅผ ์ ๊ฑฐํ ๋์ผํ ์ฝ๋์
๋๋ค. useMemo
๊ฐ ์๋ ๊ฒ์ด ์ฒด๊ฐ ๋์๋์?
import { useState } from 'react';
import { createTodos } from './utils.js';
import TodoList from './TodoList.js';
const todos = createTodos();
export default function App() {
const [tab, setTab] = useState('all');
const [isDark, setIsDark] = useState(false);
return (
<>
<button onClick={() => setTab('all')}>
All
</button>
<button onClick={() => setTab('active')}>
Active
</button>
<button onClick={() => setTab('completed')}>
Completed
</button>
<br />
<label>
<input
type="checkbox"
checked={isDark}
onChange={e => setIsDark(e.target.checked)}
/>
Dark mode
</label>
<hr />
<TodoList
todos={todos}
tab={tab}
theme={isDark ? 'dark' : 'light'}
/>
</>
);
}
import List from './List.js';
import { filterTodos } from './utils.js'
export default function TodoList({ todos, theme, tab }) {
const visibleTodos = filterTodos(todos, tab);
return (
<div className={theme}>
<List items={visibleTodos} />
</div>
);
}
import { memo } from 'react';
function List({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.completed ?
<s>{item.text}</s> :
item.text
}
</li>
))}
</ul>
);
}
export default memo(List);
export function createTodos() {
const todos = [];
for (let i = 0; i < 50; i++) {
todos.push({
id: i,
text: "Todo " + (i + 1),
completed: Math.random() > 0.5
});
}
return todos;
}
export function filterTodos(todos, tab) {
return todos.filter(todo => {
if (tab === 'all') {
return true;
} else if (tab === 'active') {
return !todo.completed;
} else if (tab === 'completed') {
return todo.completed;
}
});
}
label {
display: block;
margin-top: 10px;
}
.dark {
background-color: black;
color: white;
}
.light {
background-color: white;
color: black;
}
๋ฉ๋ชจ์ด์ ์ด์ ์์ด๋ ์ฝ๋๊ฐ ์ ๋์ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. ์ํธ ์์ฉ์ด ์ถฉ๋ถํ ๋น ๋ฅด๋ค๋ฉด ๋ฉ๋ชจ์ด์ ์ด์ ์ ํ ํ์๊ฐ ์์ต๋๋ค.
์ฑ์ ์๋๋ฅผ ์ค์ ๋ก ์ ํ์ํค๋ ์์ธ์ ํ์ค์ ์ผ๋ก ํ์ ํ๋ ค๋ฉด ํ๋ก๋์ ๋ชจ๋์์ React๋ฅผ ์คํํ๊ณ , React ๊ฐ๋ฐ์ ๋๊ตฌ๋ฅผ ๋นํ์ฑํํ๊ณ , ์ฑ ์ฌ์ฉ์๊ฐ ์ฌ์ฉํ๋ ๊ฒ๊ณผ ์ ์ฌํ ๊ธฐ๊ธฐ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค๋ ์ ์ ๋ช ์ฌํ์ธ์.
์ปดํฌ๋ํธ ๋ณธ๋ฌธ์์ ์ง์ ์์ฑ๋ ๊ฐ์ฒด์ ์์กดํ๋ ์ฐ์ฐ์ด ์๋ค๊ณ ๊ฐ์ ํ๊ฒ ์ต๋๋ค.
function Dropdown({ allItems, text }) {
const searchOptions = { matchMode: 'whole-word', text };
const visibleItems = useMemo(() => {
return searchItems(allItems, searchOptions);
}, [allItems, searchOptions]); // ๐ฉ ์ฃผ์: ์ปดํฌ๋ํธ ๋ณธ๋ฌธ์์ ์์ฑ๋ ๊ฐ์ฒด์ ๋ํ ์ข
์์ฑ
// ...
์ด๋ ๊ฒ ๊ฐ์ฒด์ ์์กดํ๋ ๊ฒ์ ๋ฉ๋ชจ์ด์ ์ด์
์ ๋ชฉ์ ์ ๋ฌด์ํ๊ฒ ํฉ๋๋ค. ์ปดํฌ๋ํธ๊ฐ ๋ค์ ๋ ๋๋ง ๋๋ฉด ์ปดํฌ๋ํธ ๋ณธ๋ฌธ ๋ด๋ถ์ ๋ชจ๋ ์ฝ๋๊ฐ ๋ค์ ์คํ๋๊ธฐ ๋๋ฌธ์
๋๋ค. searchOptions
๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ์ฝ๋๋ ๋ค์ ๋ ๋๋ง ๋ ๋๋ง๋ค ์คํ๋ฉ๋๋ค. searchOptions
์ useMemo
ํธ์ถ์ ์ข
์์ฑ์ด๊ณ ๋งค๋ฒ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์, React๋ ์ข
์์ฑ์ด ๋ค๋ฅธ ๊ฒ์ ์๊ณ searchItems
์ ๋งค๋ฒ ๋ค์ ๊ณ์ฐํฉ๋๋ค.
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด searchOptions
๊ฐ์ฒด ์์ฒด๋ฅผ ์ข
์์ฑ์ผ๋ก ์ ๋ฌํ๊ธฐ ์ ์ ๋ฉ๋ชจํด๋๋ฉด ๋ฉ๋๋ค.
function Dropdown({ allItems, text }) {
const searchOptions = useMemo(() => {
return { matchMode: 'whole-word', text };
}, [text]); // โ
text๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง ๋ณ๊ฒฝ
const visibleItems = useMemo(() => {
return searchItems(allItems, searchOptions);
}, [allItems, searchOptions]); // โ
allItems์ด๋ searchOptions์ด ๋ณ๊ฒฝ๋ ๋๋ง ๋ณ๊ฒฝ
// ...
์์ ์์ ์์ text
๊ฐ ๋ณ๊ฒฝ๋์ง ์์๋ค๋ฉด searchOptions
๊ฐ์ฒด๋ ๋ณ๊ฒฝ๋์ง ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ณด๋ค ๋ ๋์ ๋ฐฉ๋ฒ์ searchOptions
๋ฅผ useMemo
๊ณ์ฐ ํจ์์ ๋ด๋ถ์ ์ ์ธํ๋ ๊ฒ์
๋๋ค.
function Dropdown({ allItems, text }) {
const visibleItems = useMemo(() => {
const searchOptions = { matchMode: 'whole-word', text };
return searchItems(allItems, searchOptions);
}, [allItems, text]); // โ
allItems์ด๋ text๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง ๋ณ๊ฒฝ
// ...
์ด์ ์ฐ์ฐ์ text
์ ์ง์ ์ ์ผ๋ก ์์กดํฉ๋๋ค (๋ฌธ์์ด์ด๋ฏ๋ก "์ค์๋ก" ๋ฌ๋ผ์ง ์ ์์).
Form
์ปดํฌ๋ํธ๊ฐ memo
๋ก ๊ฐ์ธ์ ธ ์๊ณ ์ฌ๊ธฐ์ prop๋ก ํจ์๋ฅผ ์ ๋ฌํ๊ณ ์ถ๋ค๊ณ ๊ฐ์ ํด๋ด
์๋ค.
export default function ProductPage({ productId, referrer }) {
function handleSubmit(orderDetails) {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
}
return <Form onSubmit={handleSubmit} />;
}
{}
๊ฐ ๋ค๋ฅธ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๊ฒ์ฒ๋ผ function() {}
์ ๊ฐ์ ํจ์ ์ ์ธ๊ณผ () => {}
๊ฐ์ ํํ์์ ๋ค์ ๋ ๋๋ง ๋ ๋๋ง๋ค ๋ค๋ฅธ ํจ์๋ฅผ ์์ฑํฉ๋๋ค. ์๋ก์ด ํจ์๋ฅผ ๋ง๋๋ ๊ฒ ์์ฒด๋ ๋ฌธ์ ๊ฐ ๋์ง ์์ผ๋ฉฐ ํผํด์ผ ํ ์ผ์ด ์๋๋๋ค! ๊ทธ๋ฌ๋ Form
์ปดํฌ๋ํธ๊ฐ ๋ฉ๋ชจํ๋์ด ์๋ค๋ฉด props๊ฐ ๋ณ๊ฒฝ๋์ง ์์์ ๋ ๋ค์ ๋ ๋๋งํ๋ ๊ฒ์ ์๋ตํ๊ณ ์ถ์ ๊ฒ์
๋๋ค. ํญ์ ๋ค๋ฅธ prop์ ๋ฉ๋ชจ์ด์ ์ด์
์ ๋ชฉ์ ์ ๋ฌด์ํ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค.
useMemo
๋ก ํจ์๋ฅผ ๋ฉ๋ชจํ๋ ค๋ฉด ๊ณ์ฐ ํจ์์์ ๋ค๋ฅธ ํจ์๋ฅผ ๋ฐํํด์ผ ํฉ๋๋ค.
export default function Page({ productId, referrer }) {
const handleSubmit = useMemo(() => {
return (orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
};
}, [productId, referrer]);
return <Form onSubmit={handleSubmit} />;
}
์ ์์ ๋ ํฌ๋ฐํด ๋ณด์
๋๋ค! ํจ์๋ฅผ ๋ฉ๋ชจํ๋ ๊ฒ์ ์ถฉ๋ถํ ์ผ๋ฐ์ ์ด๊ธฐ ๋๋ฌธ์ React์๋ ์ด๋ฅผ ์ํ Hook์ด ๋ด์ฅ๋์ด ์์ต๋๋ค. useMemo
๋์ useCallback
์ผ๋ก ํจ์๋ฅผ ๊ฐ์ธ์ ์ค์ฒฉ๋ ํจ์๋ฅผ ์ถ๊ฐ๋ก ์์ฑํ์ง ์๋๋ก ํ์ธ์.
export default function Page({ productId, referrer }) {
const handleSubmit = useCallback((orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
}, [productId, referrer]);
return <Form onSubmit={handleSubmit} />;
}
์ ๋ ์์ ๋ ์์ ํ ๋์ผํ๊ฒ ๋์ํฉ๋๋ค. useCallback
์ ์ ์ผํ ์ฅ์ ์ ๋ด๋ถ์ ์ค์ฒฉ๋ ํจ์๋ฅผ ์ถ๊ฐ๋ก ์์ฑํ์ง ์์๋ ๋๋ค๋ ๊ฒ์
๋๋ค. ๊ทธ ์ธ์๋ ์๋ฌด๊ฒ๋ ํ์ง ์์ต๋๋ค. useCallback
์ ๋ํด ๋ ์ฝ์ด๋ณด์ธ์.
๋ ๋๋งํ ๋๋ง๋ค ๊ณ์ฐ์ด ๋ ๋ฒ ์คํ๋ฉ๋๋ค {/my-calculation-runs-twice-on-every-re-render/}
Strict ๋ชจ๋์์ React๋ ์ผ๋ถ ํจ์๋ฅผ ํ ๋ฒ์ด ์๋ ๋ ๋ฒ ํธ์ถํฉ๋๋ค.
function TodoList({ todos, tab }) {
// ์ด ์ปดํฌ๋ํธ ํจ์๋ ๋ ๋๋งํ ๋๋ง๋ค ๋ ๋ฒ ์คํ๋ฉ๋๋ค.
const visibleTodos = useMemo(() => {
// ์ข
์์ฑ ์ค ํ๋๋ผ๋ ๋ณ๊ฒฝ๋๋ฉด ์ด ๊ณ์ฐ์ ๋ ๋ฒ ์คํ๋ฉ๋๋ค.
return filterTodos(todos, tab);
}, [todos, tab]);
// ...
์ด๋ ์์๋๋ ํ์์ด๋ฉฐ ์ฝ๋๋ฅผ ์์์ํค์ง ์์ต๋๋ค.
์ด ๊ฐ๋ฐ ์ ์ฉ ๋์์ ์ปดํฌ๋ํธ๊ฐ ์์ํ๊ฒ ์ ์ง๋ ์ ์๋๋ก ๋์์ค๋๋ค. React๋ ํธ์ถ ๊ฒฐ๊ณผ ์ค ํ๋๋ฅผ ์ฌ์ฉํ๊ณ ๋ค๋ฅธ ํธ์ถ ๊ฒฐ๊ณผ๋ ๋ฌด์ํฉ๋๋ค. ์ปดํฌ๋ํธ์ ๊ณ์ฐ ํจ์๊ฐ ์์ํ๋ค๋ฉด ๋ก์ง์ ์ํฅ์ ๋ฏธ์น์ง ์์ ๊ฒ์ ๋๋ค. ๊ทธ๋ฌ๋ ์ค์๋ก ๋ฐ์ํ๋ ๋ถ์ํ ๊ฒฝ์ฐ์ ๋ฐ์ํ๋ ์ค์๋ฅผ ๋ฐ๊ฒฌํ๊ณ ์์ ํ๋ ๋ฐ ๋์์ ์ค๋๋ค.
์๋ฅผ ๋ค์ด ์๋์ ๋ถ์ํ ๊ณ์ฐ ํจ์๋ prop์ผ๋ก ๋ฐ์ ๋ฐฐ์ด์ ๋ณ๊ฒฝํฉ๋๋ค.
const visibleTodos = useMemo(() => {
// ๐ฉ Mistake: mutating a prop
todos.push({ id: 'last', text: 'Go for a walk!' });
const filtered = filterTodos(todos, tab);
return filtered;
}, [todos, tab]);
React๊ฐ ํจ์๋ฅผ ๋ ๋ฒ ํธ์ถํ๋ฏ๋ก todo๊ฐ ๋ ๋ฒ ์ถ๊ฐ๋ฉ๋๋ค. ๊ณ์ฐ์ด ๊ธฐ์กด์ ๊ฐ์ฒด๋ฅผ ๋ณ๊ฒฝํด์๋ ์ ๋์ง๋ง ๊ณ์ฐ ์ค์ ์์ฑ๋ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ๋ณ๊ฒฝํ๋ ๊ฒ์ ๊ด์ฐฎ์ต๋๋ค. ์๋ฅผ ๋ค์ด filterTodos
ํจ์๊ฐ ํญ์ ๋ค๋ฅธ ๋ฐฐ์ด์ ๋ฐํํ๋ ๊ฒฝ์ฐ ๋์ ํด๋น ๋ฐฐ์ด์ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
const visibleTodos = useMemo(() => {
const filtered = filterTodos(todos, tab);
// โ
์ ๋ต: ๊ณ์ฐ ์ค์ ์์ฑํ ๊ฐ์ฒด๋ฅผ ๋ณ๊ฒฝํฉ๋๋ค.
filtered.push({ id: 'last', text: 'Go for a walk!' });
return filtered;
}, [todos, tab]);
์์์ฑ์ ๋ํด ์์ธํ ์์๋ณด๋ ค๋ฉด ์ปดํฌ๋ํธ ์์ํ๊ฒ ์ ์งํ๊ธฐ๋ฅผ ์ฝ์ด๋ณด์ธ์.
๋ํ ๋ณ๊ฒฝ์ฌํญ์ด ์๋ ๊ฐ์ฒด ์ ๋ฐ์ดํธ ๋ฐ ๋ฐฐ์ด ์ ๋ฐ์ดํธ์ ๋ํ ๊ฐ์ด๋๋ ํ์ธํด๋ณด์ธ์.
useMemo
๊ฐ ๊ฐ์ฒด๋ฅผ ๋ฐํํด์ผ ํ๋๋ฐ undefined๋ฅผ ๋ฐํํฉ๋๋ค. {/my-usememo-call-is-supposed-to-return-an-object-but-returns-undefined/}
์ด ์ฝ๋๋ ์๋ํ์ง ์์ต๋๋ค.
// ๐ด () => { ์ ๊ฐ์ ํ์ดํ ํจ์๋ ๊ฐ์ฒด๋ฅผ ๋ฐํํ์ง ์์ต๋๋ค.
const searchOptions = useMemo(() => {
matchMode: 'whole-word',
text: text
}, [text]);
์๋ฐ์คํฌ๋ฆฝํธ์ () => {
๋ ํ์ดํ ํจ์์ ๋ณธ๋ฌธ์ ์์์ด๋ฏ๋ก {
์ค๊ดํธ๋ ๊ฐ์ฒด์ ์ผ๋ถ๊ฐ ์๋๋๋ค. ์ด๊ฒ์ด ๊ฐ์ฒด๋ฅผ ๋ฐํํ์ง ์๊ณ ์ค์ํ๋ ์ง์ ์
๋๋ค. ({
๊ณผ })
๊ฐ์ ๊ดํธ๋ฅผ ์ถ๊ฐํ์ฌ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ต๋๋ค.
// T์ด๊ฒ์ ์๋ํ์ง๋ง ๋๊ตฐ๊ฐ๊ฐ ๋ค์ ์๋ฐํ๊ธฐ ์ฝ์ต๋๋ค.
const searchOptions = useMemo(() => ({
matchMode: 'whole-word',
text: text
}), [text]);
ํ์ง๋ง ํด๋น ๋ฐฉ์์ ์ฌ์ ํ ํผ๋์ ์ฃผ๊ณ , ๊ดํธ๋ฅผ ์ ๊ฑฐํ๋ฉด์ ๋๊ตฐ๊ฐ ์ฝ๊ฒ ์๋ฐํ ์ ์์ต๋๋ค.
์ด ์ค์๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด return
๋ฌธ์ ๋ช
์์ ์ผ๋ก ์์ฑํ์ธ์.
// โ
์ด๊ฒ์ ์๋ํ๋ฉฐ ๋ช
ํํฉ๋๋ค.
const searchOptions = useMemo(() => {
return {
matchMode: 'whole-word',
text: text
};
}, [text]);
์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ๋ ๋๋ง๋ค useMemo
์ ๊ณ์ฐ์ด ๋ค์ ์คํ๋ฉ๋๋ค. {/every-time-my-component-renders-the-calculation-in-usememo-re-runs/}
๋ ๋ฒ์งธ ์ธ์๋ก ์ข ์์ฑ ๋ฐฐ์ด์ ์ง์ ํ๋์ง ํ์ธํ์ธ์!
์ข
์์ฑ ๋ฐฐ์ด์ ์ง์ ํ์ง ์์์ ๊ฒฝ์ฐ useMemo
๋ ๋งค๋ฒ ๋ค์ ๊ณ์ฐ์ ์คํํฉ๋๋ค.
function TodoList({ todos, tab }) {
// ๐ด ์ข
์์ฑ ๋ฐฐ์ด์ด ์์ด ๋งค๋ฒ ์ฌ๊ณ์ฐ ๋จ.
const visibleTodos = useMemo(() => filterTodos(todos, tab));
// ...
์ด๊ฒ์ ๋ ๋ฒ์งธ ์ธ์๋ก ์ข ์์ฑ ๋ฐฐ์ด์ ์ ๋ฌํ๋ ์์ ๋ ์์์ ๋๋ค.
function TodoList({ todos, tab }) {
// โ
๋ถํ์ํ ์ฌ๊ณ์ฐ์ ํ์ง ์์.
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
// ...
๋ง์ผ ์์ ์์ ๊ฐ ๋์์ด ๋์ง ์์๋ค๋ฉด, ์ข ์์ฑ ์ค ํ๋ ์ด์์ด ์ด์ ๋ ๋๋ง๊ณผ ๋ฌ๋ผ์ก๋ค๋ ๋ฌธ์ ์ผ ์ ์์ต๋๋ค. ์ข ์์ฑ ๋ค์ ์ฝ์์ ์๋์ผ๋ก ๋ก๊น ํ์ฌ ์ด ๋ฌธ์ ๋ฅผ ๋๋ฒ๊ทธํ ์ ์์ต๋๋ค.
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
console.log([todos, tab]);
๊ทธ๋ฐ ๋ค์ ์ฝ์์์ ์๋ก ๋ค๋ฅธ ๋ฆฌ๋ ๋์ ๋ฐฐ์ด์ ๋ง์ฐ์ค ์ค๋ฅธ์ชฝ ๋ฒํผ์ผ๋ก ํด๋ฆญํ๊ณ ๋ ๋ฐฐ์ด ๋ชจ๋์ ๋ํด "์ ์ญ ๋ณ์๋ก ์ ์ฅ"์ ์ ํํฉ๋๋ค. ์ฒซ ๋ฒ์งธ ๋ฐฐ์ด์ temp1
, ๋ ๋ฒ์งธ ๋ฐฐ์ด์ด temp2
๋ก ์ ์ฅ๋์๋ค๊ณ ๊ฐ์ ํ๋ฉด ๋ธ๋ผ์ฐ์ ์ฝ์์์ ๋ ๋ฐฐ์ด์ ๊ฐ ์ข
์์ฑ์ด ๋์ผํ์ง์ ๋ํด ํ์ธํ ์ ์์ต๋๋ค.
Object.is(temp1[0], temp2[0]); // ๋ฐฐ์ด ๊ฐ์ ์ฒซ ๋ฒ์งธ ์ข
์์ฑ์ด ๋์ผํฉ๋๊น?
Object.is(temp1[1], temp2[1]); // ๋ฐฐ์ด ๊ฐ์ ๋ ๋ฒ์งธ ์ข
์์ฑ์ด ๋์ผํฉ๋๊น?
Object.is(temp1[2], temp2[2]); // ... ๊ทธ๋ฆฌ๊ณ ๊ธฐํ ๋ชจ๋ ์ข
์์ฑ๋ค์ด ๋์ผํฉ๋๊น? ...
๋ฉ๋ชจ๋ฅผ ๋ฐฉํดํ๋ ์ข ์์ฑ์ ๋ฐ๊ฒฌํ๋ฉด ์ ๊ฑฐํ ๋ฐฉ๋ฒ์ ์ฐพ๊ฑฐ๋ ๋ฉ๋ชจํ ๋ฐฉ๋ฒ์ ์ฐพ์ผ์ธ์.
๋ฐ๋ณต๋ฌธ์์ ๊ฐ ๋ชฉ๋ก ํญ๋ชฉ์ ๋ํด useMemo
๋ฅผ ํธ์ถํด์ผ ํ๋๋ฐ ํ์ฉ๋์ง ์์ต๋๋ค. {/i-need-to-call-usememo-for-each-list-item-in-a-loop-but-its-not-allowed/}
Chart
์ปดํฌ๋ํธ๊ฐ memo
๋ก ๊ฐ์ธ์ ธ ์๋ค๊ณ ๊ฐ์ ํด๋ณด๊ฒ ์ต๋๋ค. ReportList
์ปดํฌ๋ํธ๊ฐ ๋ค์ ๋ ๋๋ง ๋ ๋ ๋ชฉ๋ก์ ๋ชจ๋ Chart
๋ฅผ ๋ค์ ๋ ๋๋งํ๋ ๊ฒ์ ์๋ตํ๊ณ ์ถ์ ๊ฒ์
๋๋ค. ๊ทธ๋ฌ๋ ๋ฐ๋ณต๋ฌธ์์ useMemo
๋ฅผ ํธ์ถํ ์ ์์ต๋๋ค.
function ReportList({ items }) {
return (
<article>
{items.map(item => {
// ๐ด ๋ฐ๋ณต๋ฌธ์์๋ useMemo๋ฅผ ํธ์ถํ ์ ์์ต๋๋ค.
const data = useMemo(() => calculateReport(item), [item]);
return (
<figure key={item.id}>
<Chart data={data} />
</figure>
);
})}
</article>
);
}
๋์ ๊ฐ ํญ๋ชฉ์ ๋ํ ์ปดํฌ๋ํธ๋ฅผ ์ถ์ถํ๊ณ ๊ฐ๋ณ ํญ๋ชฉ์ ๋ํ ๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ชจํ์ธ์.
function ReportList({ items }) {
return (
<article>
{items.map(item =>
<Report key={item.id} item={item} />
)}
</article>
);
}
function Report({ item }) {
// โ
์ต์์ ์์ค์์ useMemo๋ฅผ ํธ์ถํฉ๋๋ค.
const data = useMemo(() => calculateReport(item), [item]);
return (
<figure>
<Chart data={data} />
</figure>
);
}
๋๋ useMemo
๋ฅผ ์ ๊ฑฐํ๊ณ Report
์์ฒด๋ฅผ memo
๋ก ๊ฐ์ธ๋ ๋ฐฉ๋ฒ๋ ์์ต๋๋ค. item
prop๊ฐ ๋ณ๊ฒฝ๋์ง ์์ผ๋ฉด Report
๋ ์ฌ๋ ๋๋ง์ ๊ฑด๋๋ฐ๋ฏ๋ก Chart
์ญ์ ์ฌ๋ ๋๋ง์ ๊ฑด๋๋ฐ๊ฒ ๋ฉ๋๋ค.
function ReportList({ items }) {
// ...
}
const Report = memo(function Report({ item }) {
const data = calculateReport(item);
return (
<figure>
<Chart data={data} />
</figure>
);
});