리액트를 사용하다보면 랜더링,재랜더링이 될때 함수들이 똑같은 동작을 하여도 연산시 성능을 잡아먹는다. 따라서, 리액트 어플리케이션 성능을 최적화하기위한 연산 결과를 재사용할 방법을 알아보자.
💡 잠깐) 성능최적화 방법
✏️ 리액트에서 컴포넌트 랜더링이 일어나는 경우 4가지
1. props가 변경
2. state가 변경
3. forceUpdate()를 실행
4. 부모 컴포넌트가 랜더링리액트 컴포넌트는 부모 컴포넌트의 랜더링으로 인해 자식 컴포넌트까지 랜더링된다. 이로 인해 불필요한 랜더링이 일어나고 성능저하 및 손실이 오는 것이다.
✏️ 성능 최적화 방법 5가지
1. state 선언 위치
- 랜더링은 state의 변화에 따라 일어나기 때문에, state가 선언된 컴포넌트와 하위 컴포넌트는 모두 리랜더링되게 되어 있다. 따라서 state의 선언 위치를 잘 설정해주는 것도 랜더링 횟수를 줄일 수 있는 방법이다.
예를 들어 a컴포넌트에 필요한 state를 최상위 컴포넌트에 선언한다. 그러면 a컴포넌트에서 랜더링이 일어날 때마다 최상위 컴포넌트와 연결된 또 다른 b,c컴포넌트들까지 랜더링되버리는 것이다.2. React.memo()
- React.memo는 컴포넌트를 랜더링하여 그 결과를 메모이징하는데 다음 랜더링이 일어날 때 props가 같으면 메모이징한 내용을 재사용하여 불필요한 리랜더링을 막는다.
3. key값으로 index를 사용 X
- map함수 사용시 고유 key값을 설정할때 index값으로 설정할 경우 새로운 값 삽입시 리마운트가 일어나고 데이터가 매칭되지 않으므로 오류가 발생한다. 따라서, 반드시 중복되지 않고 고유한 값을 key값으로 설정하자.
(쉬운 설명: https://www.youtube.com/watch?v=QC3PtSlzp3s)4. useMemo
- 종속 변수들이 변하지 않으면 굳이 다시 호출하지 않고 반환한 참조값을 재사용하여 호출 시간을 세이브 하고 하위 컴포넌트의 리랜더링을 막는다.
5. useCallback
- 컴포넌트 내부 로직에서 쓰이는 함수의 재런더링을 막는다.
(자세한 설명: https://velog.io/@pjh1011409/React-Hooks-useCallBack)
useMemo란 성능 최적화를 위하여 연산된 값을 재사용하는 기능을 가진 함수
컴포넌트 내에 함수 값 리턴이 오래 걸린다면 리랜더링 될때마다 함수가 호출되면서 많은 시간이 소요된다.
useMemo는 종속 변수들이 변하지 않으면 함수를 굳이 다시 호출하지 않고 이전에 반환한 참조값을 재사용한다. 이를 통해 함수 호출 시간도 세이브할 수 있고 같은 값을 props로 받는 하위 컴포넌트의 리랜더링도 방지 가능하다.
💡 중요) Memoization
계산된 값을 자료구조에 저장하고 이후 같은 **계산을 반복하지 않고 자료구조에서 꺼내 재사용하는 것으로,메모이제이션의 대표적인 예로는 동적계획법의 탑다운 방식이 있다.
useMemo(함수, deps배열 )
✏️ 첫 번째 파라미터로 어떤 연산을 할지 함수를 정의
✏️ 두 번째 파라미터에는 의존성(deps) 배열을 입력
✏️ useMemo는 2개의 인자를 받는다.
예) deps에 전달된 x,y의 변화에 따라서만 연산을 수행하여 z 를 갱신
function MyComponent({ x, y }) {
const z = useMemo(() => compute(x, y), [x, y]);
return <div>{z}</div>;
}
✏️ useMemo 사용 X
import React from "react";
const expensiveFunction = (prop) => {
// 에너지가 많이 소모되는 계산이 일어난다는 가정을 했을 때,
}
const ComponentRender = ({ prop1, prop2 }) => {
const valueProp1 = computeValueFromProp(prop1);
return (
<>
<div>{valueProp1}</div>
<div>{prop2}</div>
</>
);
};
✏️ useMemo 사용 O
import React from "react";
const expensiveFunction = (prop) => {
// 에너지가 많이 소모되는 계산이 일어난다는 가정을 했을 때,
}
const ComponentRender = ({ prop1, prop2 }) => {
const valueProp1 = useMemo(() => {
return computeValueFromProp(prop1);
}, [prop1]);
return (
<>
<div>{valueProp1}</div>
<div>{prop2}</div>
</>
);
};
정확히 언제 사용해야할까?
: https://yceffort.kr/2022/04/best-practice-useCallback-useMemo
참조