렌더링 최적화를 React Hook

JohnKim·2022년 3월 26일
2

React

목록 보기
11/11

크롬의 확장도구 React Developer Tools 사용하기

React Developer Toolscomponents 에서 highlight updates when components render항목을 check하면 렌더링이 되는 영역을 눈으로 확인 할 수 있습니다.

리액트에서 성능 최적화란?

React는 먼저 컴퍼넌트를 렌더링(rendering) 한 뒤, 이전 렌더된 결과와 비교하여 DOM 업데이트를 결정합니다. 만약 렌더 결과가 이전과 다르다면, React는 DOM을 업데이트합니다.

Re-Rendering이 되는 경우에는 크게 3가지가 존재합니다.

  • 전달받은 props 값이 업데이트 되었을 때
  • state가 변경되었을 때
  • 부모 컴포넌트가 re-render 되었을 때

여러개의 state 와 props 중에 하나만 변경되어도 계속 re-render가 일어나게 되고, 부모 컴포넌트의 re-ender로 인해 자식 컴포넌트까지 불필요한 re-rendering이 일어나게 된다면 성능 저하문제를 유발할 수 있습니다.

React에서 성능 최적화란 Re-Rendering을 최소화 하는 것입니다.

  • Memoization이해하기 🔻 이미 계산 된 연산 결과를 기억 해 두었다가 동일한 계산을 시키면, 다시 연산하지 않고 기억 해 두었던 데이터를 반환 시키게 하는 방법.

useCallback()를 사용한 성능 최적화

import React, { useCallback } from "react";

useCallback(() => {}, []);

//첫번째 인자 = 콜백함수
//두번쨰 인자 = 의존성배열 

React공식문서

메모이제이션된 콜백을 반환합니다.인라인 콜백과 그것의 의존성 값의 배열을 전달하세요. useCallback은 콜백의 메모이제이션된 버전을 반환할 것입니다. 그 메모이제이션된 버전은 콜백의 의존성이 변경되었을 때에만 변경됩니다. 이것은, 불필요한 렌더링을 방지하기 위해 (예로 shouldComponentUpdate를 사용하여) 참조의 동일성에 의존적인 최적화된 자식 컴포넌트에 콜백을 전달할 때 유용합니다

참조의 동일성에 의존적인 최적화된 자식 컴포넌트에 콜백을 전달할 때 유용하다는것은 다시말해 최적화된 자식 컴포넌트에 props로 콜백 함수를 내려줄 때를 말하는것 같습니다.

  • 예제 코드
    
    const TodoTemplate = () => {
      const [localTodo, setLocalTodo] = useLocalStorage<ITodo[]>('todoList', []);
    
      const deleteCompletedTodo = useCallback((): void => {
    
      const todoUpDate = useCallback((todo: ITodo): void => {
        setLocalTodo((prevState) => [...prevState, todo]);
      }, []);
    
      return (
        <>
          <TodoCreate todoUpDate={todoUpDate} />
          <TodoList localTodo={localTodo} setLocalTodo={setLocalTodo} />
        </>
      );
    };
    TodoTemplate에서 todoUpDate라는 함수를 자식 컴포넌트인 TodoCreate에 전달하였고 TodoList에서는 바뀐 localTodo를 사용하여 화면을 그려냅니다. 만약에 useCallback 사용하지 않으면 localTodo값이 TodoList안에서 바뀌게 될 때마다 전혀 상관없는 todoUpDate가 재생성 됩니다. 그것을 막기위해 위와같이 의존성 배열에 빈 배열을 주어 초기에 한번만 함수를 생성되도록 하였습니다.

React.Memo()를 사용한 성능 최적화

import React, { useMemo } from "react";

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

React공식문서

“생성(create)” 함수와 그것의 의존성 값의 배열을 전달하세요.useMemo는 의존성이 변경되었을 때에만 메모이제이션된 값만 다시 계산 할 것입니다. 이 최적화는 모든 렌더링 시의 고비용 계산을 방지하게 해 줍니다.useMemo로 전달된 함수는 렌더링 중에 실행된다는 것을 기억하세요. 통상적으로 렌더링 중에는 하지 않는 것을 이 함수 내에서 하지 마세요. 예를 들어, 사이드 이펙트(side effects)는 useEffect에서 하는 일이지 useMemo에서 하는 일이 아닙니다.배열이 없는 경우 매 렌더링 때마다 새 값을 계산하게 될 것입니다.

React.Memo는 값을 반환합니다!

어떤 함수가 있고 그 함수가 어떤 값을 리턴하고있는데 그 리턴까지의 연산을 최적화하고싶다면 useMemo를 사용해서 댑스에 어떤값이 변화할때만 그 리턴까지의 연산을 수행할것인지를 명시하면 그 함수를 값처럼 사용하여 연산최적화를 할 수 있습니다.

  • 예제 코드
    export function Movie({ title, releaseDate }) {
      return (
        <div>
          <div>Movie title: {title}</div>
          <div>Release date: {releaseDate}</div>
        </div>
      );
    }
    
    export default React.memo(Movie);
    
    //사용시
    
    <Movie
      movieTitle="Heat"
      releaseDate="December 15, 1995"
    />
    
    // 다시 렌더링 할 때 React는 MemoizedMovie 함수를 호출하지 않는다.(props로 넘겨주는 값이 변하지 않았을 경우!)
    // 리렌더링을 막는다.
    <Movie
      movieTitle="Heat"
      releaseDate="December 15, 1995"
    />
    메모이징 한 결과를 재사용 함으로써, React에서 리렌더링을 할 때 가상 DOM에서 달라진 부분을 확인하지 않아 성능상의 이점을 누릴 수 있다.

props 동등 비교 커스터마이징

React.memo()는 props 혹은 props의 객체를 비교할 때 얕은(shallow) 비교를 합니다.

비교 방식을 수정하고 싶다면 React.memo() 두 번째 매개변수로 비교함수를 만들어 넘겨주면 됩니다.

React.memo(Component, [areEqual(prevProps, nextProps)]);

areEqual(prevProps, nextProps) 함수는 prevProps와 nextProps가 같다면 true를 반환합니다.

위에 예제 코드의 Movie의 porps가 동일한지 수동으로 비교해 보겠습니다.

function moviePropsAreEqual(prevMovie, nextMovie) {
  return (
    prevMovie.title === nextMovie.title &&
    prevMovie.releaseDate === nextMovie.releaseDate
  );
}

const MemoizedMovie = React.memo(Movie, moviePropsAreEqual);

moviePropsAreEqual() 함수는 이전 props와 현재 props가 같다면 true를 반환하며 이전값을 그대로 사용하여 리렌더링이 발생하지 않습니다.

0개의 댓글