개발공부

[React] 불필요한 리렌더링을 방지하는 방법 : [유데미x스나이퍼팩토리] 프로젝트 캠프 Next.js 2기

떡볶이가 최고야 2024. 8. 4. 18:56

리렌더링은 화면을 다시 그리는 과정인데, 이 과정이 불필요하게 자주 발생하게 된다면 성능이 저하될 수 있고 사용자 경험에 부정적인 영향을 끼칠 수 있습니다. 먼저 리렌더링을 방지하려면 리액트가 어떤 상황에서 리렌더링을 시키는지 알아야 할 필요가 있습니다.

 

리액트에서 리렌더링은 언제 일어날까? 🤔

1. 컴포넌트의 state 변경

리액트가 추적하고 있는 state 값이 변했을 때 해당 state를 관리하고 있는 컴포넌트가 리렌더링 된다.

 

2. 부모 컴포넌트의 리렌더링

부모 컴포넌트가 리렌더링 되면 해당 컴포넌트의 모든 자식 컴포넌트가 리렌더링된다.

 

3. props 값의 변경

props의 값이 변경되면 props를 받고 있는 컴포넌트가 리렌더링 된다.

 

 

따라서 최적화 도구를 적용하기 전에 기본적으로 코드를 짤 때 state를 무분별하게 사용하지 않고, 최대한 하위 컴포넌트에서 관리하는 것이 중요하다고 생각합니다.

 

 

리액트에서 제공하는 최적화 방법 3가지

리액트가 제공하는 대표적인 렌더링 최적화 방법은 useMemo, useCallback, react.Memo 등을 사용해서 메모이제이션하는 것입니다.

 

메모이제이션이란?

리렌더링이 발생하면 해당 컴포넌트가 언마운트되었다가 새롭게 마운트된다.

특정 데이터를 메모리에 기억해두는 것

리액트에서는 메모이제이션할 수 있는 2가지의 훅을 제공합니다.

 

1. useCallback

const cachedFn = useCallback(fn, dependencies);

 

함수를 메모이제이션할 때 사용합니다. 첫 번째 매개변수에 메모이제이션할 함수, 두 번째 매개변수로 의존성 배열을 받습니다. 의존성 배열의 값들 중 하나의 값이라도 변경된다면 useCallback은 새로운 함수 객체로 다시 메모이제이션합니다.

 

주의해야할 점!

메모이제이션할 당시의 상태를 저장해두고 사용하기 때문에 아래 코드에서 increase 함수의 count값은 항상 0입니다.

const [ count, setCount ] = useState(0);

const increase = useCallback(()=>{
	setCount(count+1); // count를 0으로 기억함
},[]);

 

그렇다고 의존성 배열에 count 를 넣어서 사용한다면 굳이 increase 함수를 메모이제이션할 필요가 없어집니다.

const [ count, setCount ] = useState(0);

const increase = useCallback(()=>{
	setCount(count+1);
},[count]);

 

위와 같은 의미없는 useCallback을 사용하지 않도록 유의해야합니다.

 

2. useMemo

useMemo는 값을 메모이제이션하는 훅입니다. 의존성 배열이 바뀔 때까지 콜백함수의 리턴값을 기억하고 있습니다. 계산 비용이 큰 연산을 최적화하는데 사용합니다.

const cachedValue = useMemo(calculateValue, dependencies)

 

3. React.Memo

Reat.Memo는 함수 컴포넌트를 메모이제이션하는 고차컴포넌트(HOC)입니다. 컴포넌트의 props 가 바뀌지 않았다면, 리렌더링을 방지하여 컴포넌트의 리렌더링 성능 최적화를 해줄 수 있습니다. 주의할 점으로는 리액트는 props의 변경을 얕은 비교를 통해 하기 때문에 복잡한 형태의 객체를 props로 사용하는 경우에 이 점을 유의해야 합니다.

const MemoizedComponent = memo(SomeComponent)

 

*고차 컴포넌트(HOC, Higher Order Component)란?

기존의 컴포넌트를 매개변수로 받아서 새로운 컴포넌트로 반환하는 함수를 말합니다.

 

면접 단골 질문 🙋‍♀️

1️⃣ useMemo와 React.Memo의 차이가 무엇인가요?

핵심 : useMemo는 값을, React.Memo는 컴포넌트를 메모이제이션한다는 차이

 

2️⃣ useCallback과 useMemo의 차이 무엇인가요?

핵심 : useCallback는 함수를, useMemo는 값을 메모이제이션 한다는 차이

 

3️⃣ 고차 컴포넌트가 무엇인가요?

핵심 : 고차 컴포넌트는 컴포넌트를 가져와 새로운 컴포넌트를 반환하는 함수입니다. React.Memo가 고차 컴포넌트의 원리를 사용하여 컴포넌트를 메모이제이션합니다.

 

마무리

메모이제이션하는 것은 공짜가 아닙니다. 비용이 많이 드는 작업이기 때문에 과도하게 사용하는 것은 좋지 않고 성능 개선이 필요하거나 성능적 이슈가 생길 가능성이 있는 경우에 해야합니다.

 

강사님이 주신 팁

반복문으로 출력되는 컴포넌트들을 메모이제이션하면, 반복문 안에 컴포넌트를 생성 또는 삭제하더라도 전체 컴포넌트가 리렌더링되는 것을 막을 수 있어서 메모이제이션해서 쓰면 좋다고 합니다.

 

 

해당 글은 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 수업내용을 바탕으로 작성되었습니다.