React 성능 최적화

jaehan·2023년 2월 10일
0

React

목록 보기
31/33
post-thumbnail
post-custom-banner

현재까지 프로젝트 진행하면서 성능 최적화까진 필요 없어서 안해봤는데 실전 리액트 프로그래밍 책에 잘 설명되어 있어서 정리해 보려 한다

성능 최적화 하기

💡 리액트 렌더링

리액트가 실행될 때 가장 많은 CPU 리소스를 사용하는 것은 렌더링이다.
리액트의 렌더링 단계를 살펴보면

  1. 이전 렌더링 결과를 재사용할지 판단한다.
  2. 컴포넌트 함수를 호출한다.
  3. 가상 돔끼리 비교해서 변경된 부분만 실제 돔에 반영한다.

처음에 속성값이나 상태값의 이전 이후 값을 비교하고 만약 바뀐게 없다면 2, 3번 과정을 넘어간다
만약 바뀐게 있다면 컴포넌트 함수를 호출해서 가상 돔을 만들고 이전 가상돔과 비교해서 변경점을 찾은 뒤에 그 부분을 실제 돔에 반영하는 과정을 거친다.

💡 React.memo

React.memo는 렌더링에 대해 메모이제이션을 수행하는 HOC이다.

React.memo(Component, isEqual)
두개의 매개변수가 있는데 첫번째는 메모이제이션할 컴포넌트와 두번째는 속성값 비교함수이다.

이 속성값 비교함수는 참 또는 거짓을 반환하는데 만약 참을 반환했다면 위의 2, 3번을 건너뛰고 이전 렌더링 결과를 이용한다.

📌 만약 속성값 비교함수를 생략하면 얕은 비교를 수행하는 기본 함수가 사용된다

📌 React.memo로 묶지 않았다면 속성값 비교함수가 항상 거짓을 반환한다고 생각하면 된다

속성값 비교

속성값을 비교할때 수정가능한 객체이냐, 불변 객체이냐가 중요한데.

만약 수정가능한 객체이면 모두 비교해야하지만 불변 객체이면 한번만 비교해도 되기 때문에 렌더링 성능에 큰 도움이 된다.

// 수정 가능한 객체
prevProps.todos[0] === nextProps.todos[0] &&
  prevProps.todos[1] === nextProps.todos[1] 
	...

// 불변 객체
prevProps.todos !== nextProps.todos

💡 불변 변수로 관리하기

1. 함수 관리

<button onClick={() => setCount(count + 1)}>increase</button>
<SelectFruit
	selected={selectedFruit}
	onChage={fruit => setSelectedFruit(fruit)}
/>

버튼을 눌러서 count가 1 증가하면 이 컴포넌트의 렌더링이 시작된다
만약 SelectFruit 컴포넌트를 React.memo로 묶었다면 이 컴포넌트 함수는 호출되지 않을것이다

❗️ 하지만 onChange 속성값은 부모가 리렌더링 될때마다 새로운 함수를 만들기 때문에 호출된다

따라서 onChange 함수를 변하지 않는 함수로 설정해 줘야 한다.

useState

useState의 setState함수는 변하지 않기 때문에 onChage 함수의 상태값 변화 함수를 넣어야 한다면 setState를 넣어주자

useCallback

만약 상태값 변경외에 처리할게 있다면 useCallback을 사용하면 된다.

const onChageFruit = useCallback(fruit => {
  	...
    ...
}, [])

위 처럼 의존성 배열에 빈 배열을 넣어주면 항상 고정된 값을 가진 함수가 된다.

2. 객체 관리

<SelectFruit
	options={[
         {name: "apple", price: 500},
         {name: "banana", price: 1500},
         {name: "kiwi", price: 2500},
        ]}
/>

위 처럼 관리하면 아까처럼 options에 새로운 객체를 만들어서 전달하기 때문에 자식 컴포넌트가 호출된다

상수로 관리

상수는 렌더링과 무관하게 항상 같은 값을 가지므로 컴포넌트 밖에서 선언해서 관리하자

useMemo

만약 상태값이나 속성값을 이용해서 계산해야 하는 경우가 있다면 useMemo를 사용하자

const fruit = useMemo(() => Fruits.filter(item => item.price <= maxPrice), [maxPrice])

📌 그렇다고 무조건 useMemo, useCallback, React.memo를 사용하면 가독성이 떨어 지니까 필요한 곳에만 사용하도록 하자!

💡 가상 돔에서의 최적화

요소의 타입 변경(x)-> 속성 변경(o)

요소의 타입을 변경하면 해당 요소의 자식 요소도 같이 변경된다.

❗️ 하지만 요소의 속성값만 변경하면 해당하는 속성만 실제 돔에 반영한다

// x
flag ? <div><자식></div> : <span><자식></span>

  
// o  
<div
  className={flag ? 'yes' : 'no'}
  style={{ color: 'black', backgroundColor: flag ? 'green' : 'red'}}
>
</div>

첫번째 코드처럼 똑같은 자식의 부모 타입만 바꾸면 자식이 안변해도 실제 돔에서 삭제하고 다시 추가함으로 좋지 않다

두번째 처럼 속성값을 변경하면 속성만 실제 돔에 반영한다.
스타일도 color는 그대로 있고 backgroundColor만 실제 돔에 반영한다.

요소 추가 및 삭제 -> key 이용

return flag ? (
    <div>
      <div>사과</div>
      <div>딸기</div>
    </div>
  ) : (
    <div>
      <div>사과</div>
      <div>딸기</div>  // <div>키위</div>
      <div>키위</div>  // <div>딸기</div> 
    </div>
  );

	

만약 코드가 위와 같다면 실제 돔에서는 키위만 삭제되고 생성된다

하지만 주석처럼 되어있다면 리액트는 순서정보를 이용하기 때문에 딸기를 키위로 바꾸고 딸기를 추가하는 연산을 한다.

return flag ? (
    <div>
      <div key="apple">사과</div>
      <div key="strawberry">딸기</div>
    </div>
  ) : (
    <div>
      <div key="apple">사과</div>
  	  <div key="kiwi">키위</div>
	  <div key="strawberry">딸기</div> 
    </div>
  );

📌 위처럼 키를 입력하면 리액트는 같은 키를 가진 요소끼리 비교해서 렌더링을 효율적으로 할 수 있다.

❗️아직 최적화 까지는 해본적 없지만 다음 프로젝트에서는 이런것도 신경쓰면서 코드를 짜야겠다

참고: 실전 리액트 프로그래밍

post-custom-banner

0개의 댓글