[React] 리액트 성능 최적화

빵호·2021년 12월 30일
0

React

목록 보기
6/8
post-thumbnail

리액트 성능 최적화

1. React.memo()

컴포넌트가 동일한 props로 동일한 결과를 렌더링 한다면 React.memo를 통해 불필요한 리렌더링을 방지해 성능 최적화를 할 수 있다. 컴포넌트가 React.memo()로 래핑 되면 결과를 메모이징(Memoizing) 하기 때문에 props가 변경되지 않으면 메모이징된 결과를 재사용한다.

사용법

export default React.memo(Component);

사용법은 간단하다 React.memo()로 래핑만 해주면 된다.

React.memo로 감싸진 컴포넌트는 props의 변화에만 동작하기 때문에 useState, useReducer 또는 useContext 훅을 사용해 state나 context를 변경할 경우에는 리렌더링이 된다.

props 비교

React.memo()는 props를 비교할 때 얕은 비교만 수행한다. 그렇기 때문에 props로 객체, 배열, 함수와 같은 참조형 데이터를 넘겨줄 경우 같은 참조 값이 아니라면 새로운 값으로 판단하여 리렌더링이 된다.

function MyComponent(props) {
  /* props를 사용하여 렌더링 */
}
function areEqual(prevProps, nextProps) {
  /*
  nextProp가 prevProps와 동일한 값을 가지면 true를 반환하고, 그렇지 않다면 false를 반환
  */
}
export default React.memo(MyComponent, areEqual);

얕은 비교를 원하지 않는 경우 커스텀 된 비교 방식을 만든 후 두 번째 인자에 넘겨주면 된다.

2. useCallback()

메모이제이션된 콜백을 반환한다.

컴포넌트 안에 함수들은 해당 컴포넌트가 리렌더링될 때마다 새로 만들어진다. 하지만 useCallback을 사용하면 해당 컴포넌트가 리렌더링되더라고 함수의 의존 값들이 바뀌지 않는 경우 기존 함수를 재사용한다.

사용법

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b], // 의존 값
);

첫 번째 인자에 함수를 작성하고, 두 번째 인자에 배열로 의존 값을 작성하면 된다. 그러면 컴포넌트가 리렌더링 되더라고 함수의 의존 값이 바뀌지 않는 이상 기존 함수를 재사용한다.

React.memo와 함께 사용하기

// App.js
function App() {
  const [showParagraph, setShowParagraph] = useState(false);

  const toggleParagraphHandler = useCallback(() => {
    setShowParagraph((prevShowParagraph) => !prevShowParagraph);
  }, []);
 
  return (
    <div>
      <Button onClick={toggleParagraphHandler}>Toggle Paragraph!</Button>
    </div>
  );
}

  export default App;

// Button.js
function Button({props}) => {
  return (
    <button onClick={props.onClick}>
      {props.children}
    </button>
  );
};

export default React.memo(Button);

React.memo에서 props를 비교할 때 얕은 비교만 수행하기 때문에 참조형 데이터의 경우 리렌더링이 발생한다고 했는데 props가 함수인 경우 useCallback을 함께 사용하면 의존 값이 바뀌지 않는 경우 함수를 재사용하기 때문에 참조 값이 바뀌지 않아 리렌더링을 줄일 수 있다.

3. useMemo()

메모이제이션된 값을 반환합니다.

리렌더링이 될 때 함수의 인자로 사용되는 props 값이 바뀌지 않아도 해당 함수는 계속 호출이 된다. 이때 useMemo를 사용하면 의존 값들이 바뀌지 않는 경우 기존 연산된 값을 재사용하기 때문에 함수를 다시 호출하지 않는다.

사용법

const DemoList = (props) => {
  const sortedList = useMemo(() => {
    return props.items.sort((a, b) => a - b);
  }, [props.items]); 

  return (
    <div>
      <h2>{props.title}</h2>
      <ul>
        {sortedList.map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
    </div>
  );
};

첫 번째 인자에 함수를 작성하고, 두 번째 인자에 배열로 의존 값을 작성하면 된다. 그러면 위의 경우 props.items이 바뀐 경우에만 함수가 호출되고 props.title만 값이 바뀐 경우에는 메모이제이션된 sortedList의 값을 재사용하기 때문에 해당 함수가 다시 호출되지 않는다.

4. 결론

  • React.memo - 컴포넌트 리렌더링 방지
  • useCallback - 함수 재사용
  • useMemo - 연산한 값 재사용

리액트에서 최적화를 위해 해당 훅을 사용할 수 있지만 메모이제이션 되는 값의 코스트(?)가 낮은 경우 위의 훅을 쓸 경우 성능 최적화 보다 악화를 유발할 수 있으니 정말 필요한 상황인지 판단하고 사용해야 한다.

메모이제이션(memoization)

동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술

profile
늘 한결같이 꾸준히

0개의 댓글