리액트 렌더링 최적화 방법 useCallback, useMemo, React.memo 정리

Memoization (메모이제이션)

기존에 계산한 값을 메모리에 저장해두고, 재사용하는 기법

 

 

리액트에서 메모이제이션

1. 정말로 컴포넌트, 데이터의 생애주기에 따라 어떤 값을 갱신하고 싶을 때

2. 리렌더링을 할 때마다 새로운 값을 계산 / 정의하는 것이 문제가 될 때 - 무거운 작업일 때 - 값의 참조값 또는 함수의 참조값을 유지하는 것이 중요할 때

 

컴포넌트가 리렌더링되면 컴포넌트 안에 선언된 변수는 재선언/재할당이 일어난다 (= JS 런타임의 메모리 참조가 변경)

 

리액트는 가상돔으로 바뀐 부분만 업데이트(렌더링이 되도 실제 돔은 업데이트 x)

하지만 컴포넌트 안에 시간이 오래 걸리는 일이 있다면 가상돔을 만드는 시간이 오래걸림

 

 

useMemo (값)

💡 의존성 배열이 변경되지 않는 이상, 같은 값 반환

const student = useMemo(
   () => ({
       name: '김철수',
       age: 20,
       gender: 'male',
   }), []
);

 

const sampleValue = useMemo(() => {**heavyWork()**}, []) // **콜백에는 값이 들어가야함**
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab])
function CounterProvider({children}){
	const [counter, setCounter] = useState(1);
	//actions 객체를 만들어 변화를 일으키는 함수를 담음
	//리렌더링에도 재사용 할 수 있도록 useMemo로 감쌈
	const actions = useMemo(
		() => (**{
			increase(){setCounter(prev => prev+1)},
			decrease(){setCounter(prev => prev-1)}
		}**), []
	);
	
	//useMemo로 한번 더 감쌈 
	//CounterProvider가 리렌더링 될 때마다 새로운 배열을 만들기 때문에
	//useContext를 사용하는 컴포넌트 쪽에서 Context의 값이 바뀐 것으로 간주해서 렌더링 낭비됨 
	const value = useMemo(()=>[counter, actions], [counter, actions])
	
	return(
		<CoutnerContext.Provider value={value}>{children}</<CoutnerContext.Provider>>
	)
}

 

 

useCallback (함수 캐싱)

💡 useEffect등의 의존성 배열이 들어가거나, React.memo 처리된 컴포넌트에 프롭으로 들어가야 하는 등 주소값 고정이 필요할 때 사용

 

리액트 렌더링 과정에서 render phase(가상돔 생성) 은 되지만, commit phase(실제 돔 반영)은 실행 x

 

React.memo

📌 전달받은 props를 얕은 비교 후, 같다면 렌더링 된 결과(컴포넌트)를 재사용

부모가 리렌더링 되는 경우, 자식도 리렌더링 되는 조건을 해결하기 위해 사용

 

독립적으로 쓰이는 경우 많지 않음 (객체는 렌더링마다 새로운 메모리 주소를 가지므로)

 

 

무작정 사용하는 것은 옳지 않다

근본적인 문제를 해결하고, 필요할 때 최적화하자

개선 전
개선 후

 

 

참고자료

https://www.youtube.com/watch?v=1YAWshEGU6g&t=124s

https://ko.react.dev/reference/react/useCallback

챌린지 수업