-
[유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 사전직무교육 2주차 - Memoization프로젝트 캠프: Next.js 과정 2기 2024. 7. 28. 22:07728x90
학습 목표
memoization을 이해하고 렌더링 최적화 방법을 익힙니다.
memoization이란?
💡 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장하여 사용함으로써 동일한 계산의 반복 수행을 제거하여 성능 향상을 도모하는 기술
useCallback
함수형 컴포넌트에서 함수를 메모이제이션하는 데 사용한다.
사용 방법
useCallback(fn, dependencies)
예시
카운트를 증가시키는 함수를 생성하고 이를 집합에 넣어보자.
const functionSet = new Set(); const App = () => { const [count, setCount] = useState(0); const increament = setCount((prev) => prev + 1); functionSet.add(increament); console.log(functionSet); return ( <> <h1>Count: {count}</h1> <button onClick={increament}>increase</button> </> ); };
집합을 출력해보면 카운트를 증가시킬 때마다 increment 함수가 추가되는 것을 확인할 수 있다.
왜 추가될까?
카운트가 증가될 때마다 App컴포넌트가 rerendering 되는데, 이때 함수의 참조값인 increment가 갱싱되기 때문에 js는 이를 다른 객체로 인식해서 집합에 계속해서 쌓이는 것이다.
어떻게 해결할 수 있을까?
useCallback 사용!
const functionSet = new Set(); const App = () => { const [count, setCount] = useState(0); const increament = useCallback(() => { setCount((prev) => prev + 1); }, []); functionSet.add(increament); console.log(functionSet); return ( <> <h1>Count: {count}</h1> <button onClick={increament}>increase</button> </> ); };
위와 같이 작성하면 useCallback이 함수를 저장하고 있기 때문에 카운트를 증가시키더라도 집합의 개체가 증가하지 않는 것을 확인할 수 있다.
주의사항
setState를 할 때 콜백함수를 사용하자
만약 setState를 사용해 상태를 업데이트할 때
const increament = useCallback(() => { setCount(count + 1); }, []);
이런 방식으로 작성한다면 카운트는 1에서 더이상 증가하지 않을 것이다. 왜냐하면 useCallback은 스냅샷을 찍듯 함수를 저장한다. 따라서 위의 코드에서는 메모이제이션됐을 때 count가 0이었기 때문에 1에서 더이상 증가하지 않게 된다.
dependencies를 확인하자
useCallback에서는 dependencies에 등록된 값의 변화를 인지하고 변화가 일어났을 때 useCallback에 정의된 함수를 실행한다. 따라서 다음과 같이 작성했을 때
const increament = useCallback(() => { setCount((prev) => prev + 1); }, [count]);
카운트를 증가시킬 때마다 카운트를 증가시키는 함수가 실행되기 때문에 카운트가 무한으로 증가되는 문제가 발생하게 된다.
useMemo
값을 메모이제이션하는 훅이다.
사용 방법
const cachedValue = useMemo(calculateValue, dependencies)
예시
길이가 매우 긴 배열에서 특정 값을 찾아 렌더링하는 코드가 있다고 하자.
import { useState } from "react"; const initialItems = new Array(29_999_999).fill(0).map((_, i) => { return { id: i, selected: i === 29_999_998 }; }); const App = () => { const [count, setCount] = useState(0); const [items, _] = useState(initialItems); const selectedItem = items.find((item) => item.selected); return ( <> <h1>Count: {count}</h1> <button onClick={() => setCount((prev) => prev + 1)}>increase</button> <h2>{selectedItem?.id}</h2> </> ); }; export default App;
위와 같이 작성하면 카운트가 변경될 때마다 selectedItem은 값이 변하지 않음에도 find함수가 새롭게 실행되어 성능적인 문제가 발생하게 된다.
어떻게 해결할까?
import { useMemo, useState } from "react"; const initialItems = new Array(29_999_999).fill(0).map((_, i) => { return { id: i, selected: i === 29_999_998 }; }); const App = () => { const [count, setCount] = useState(0); const [items, _] = useState(initialItems); const selectedItem = useMemo(() => items.find((item) => item.selected), []); return ( <> <h1>Count: {count}</h1> <button onClick={() => setCount((prev) => prev + 1)}>increase</button> <h2>{selectedItem?.id}</h2> </> ); }; export default App;
useMemo를 사용해 값을 지정해주면 그 값이 메모이제이션되어 값을 찾는 코드를 다시 실행하지 않는다.
주의사항
dependencies를 확인하자
useCallback과 동일하게 useMemo 역시 dependencies에 등록된 값의 변화를 인지하고 변화가 일어났을 때 useCallback에 정의된 함수를 실행한다. 따라서 다음과 같이 작성했을 때
const selectedItem = useMemo( () => items.find((item) => item.selected), [count] );
카운트를 증가시킬 때마다 selectedItem을 찾는 함수가 실행되기 때문에 useMemo를 사용하는 의미가 없어진다.
React.memo
컴포넌트를 메모이제이션할 때 사용하는 함수이다.
사용 방법
const MemoizedComponent = React.memo(SomeComponent, arePropsEqual?)
예시
상위 컴포넌트에서 상태변화가 일어난다면 하위 컴포넌트는 모두 재렌더링된다. 이때 React.memo를 사용하면 재렌더링을 막을 수 있다.
import { useState } from "react"; import A from "./components/learn/A"; import B from "./components/learn/B"; const App = () => { const [count, setCount] = useState(0); return ( <> <h1>App Component: {count}</h1> <button onClick={() => setCount((prev) => prev + 1)}>increase</button> <A /> <B /> </> ); }; export default App;
export default React.memo(A); export default React.memo(B);
주의사항
만약 B 컴포넌트에 count를 props로 넘겨주면 React.memo를 지정해줬어도 App에서 count를 증가시킬 때마다 컴포넌트가 재렌더링된다.
그 값을 사용하지 않더라도 props로 전달되는 순간부터 영향을 끼친다.
만약 함수를 넘겨준다면 useCallback으로 함수를 메모이제이션해주면 해결할 수 있다.useCallback vs useMemo vs React.memo
useCallback useMemo React.memo 참조 대상 함수 값 컴포넌트 훅 / 함수 훅 훅 함수 (고차 컴포넌트) *고차 컴포넌트: 컴포넌트를 반환하는 함수
728x90'프로젝트 캠프: Next.js 과정 2기' 카테고리의 다른 글
유데미, 스나이퍼팩토리, 프로젝트 캠프, Next.js 2기: 프로젝트 1~2주차 후기 (0) 2024.08.18 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 사전직무교육 2주차 후기 (0) 2024.07.28 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 사전직무교육 2주차 - Context (0) 2024.07.26 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 사전직무교육 2주차 - useReducer (0) 2024.07.26 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 사전직무교육 2주차 - 리액트 hook (0) 2024.07.25