ABOUT ME

Today
Yesterday
Total
  • 모던 리액트 Deep dive - 렌더링은 어떻게 일어나는가? & 메모이제이션
    React 2024. 3. 19. 13:47

    ** "리액트에서" 렌더링이란?

    브라우저가 렌더링에 필요한 DOM 트리를 만드는 과정

    모든 컴포넌트들이 현재 자신들이 가지고 있는 props와 state의 값을 기반으로 어떻게 UI를 구성하고 이를 바탕으로 어떤 DOM 결과를 브라우저에 제공할 것인지 계산하는 일련의 과정.

     

    렌더링 과정을 최소한으로 줄여야 유저에게 좋은 경험을 선사할 수 있음.

     

    * 리액트의 렌더링이 일어나는 이유

     

    1. 최초 렌더링

    2. 리렌더링

     클래스형 컴포넌트의 setState가 실행되는 경우

     클래스형 컴포넌트의 forceUpdate가 실행되는 경우

     함수형 컴포넌트의 useState의 두 번째 배열 요소인 setter가 실행되는 경우

     useReducer의 두 번째 배열 요소인 dispatch가 실행되는 경우

     컴포넌트의 key props가 변경되는 경우

     

     

    * 리액트 배열에서 key를 써야하는 이유

     key는 리렌더링이 발생하는 동안 형제 요소들 사이에서 동일한 요소를 식별하는 값.

    동일한 자식 컴포넌트가 여러 개 있다고 가정했을 때, 리렌더링이 발생하면 current 트리와 workInProgress 트리 사이에서 어떠한 컴포넌트가 변경이 있었는지 구별해야 하는데, 이 두 트리 사이에서 같은 컴포넌트인지 구별하는 값이 바로 key이다.

     

    **리액트의 렌더링 프로세스

    리액트의 렌더링 프로세스가 시작되면 컴포넌트의 루트에서부터 업데이트가 필요하다고 지정돼 있는 모든 컴포넌트를 찾고, 

    발견하면 클래스형 컴포넌트 경우에는 클래스 내부의 render 함수를 실행하고,

    함수형 컴포넌트 경우에는 FunctionComponent 그 자체를 호출한 뒤에 그 결과물을 저장한다.

    export default function Hello() {
      return (
        <TestComponent a={35} b={'yceffort'}>
          안녕하세요
        </TestComponent>
      );
    }
    
    위 jsx문법은 아래처럼 변환된다
    
    export default function Hello() {
      return (
        React.createElement(
            TestComponent,
            { a: 35, b : 'yceffort'},
            '안녕하세요',
        )
      )
    }

     

     

    **렌더 단계란 컴포넌트를 렌더링하고 변경 사항을 계산하는 모든 작업

    이전 가상 DOM을 비교하여 변경이 필요한 컴포넌트를 체크하고, type, props,key의 변경 여부를 검사.

     

    **커밋 단계란 렌더 단계의 변경 사항을 실제 DOM에 적용해 사용자에게 보여주는 과정.

     

    **리액트의 렌더링이 일어난다고 해서 무조건 DOM에 업데이트가 일어나는 것은 아니다!!

    렌더링을 수행했으나 변경사항이 없다면, 커밋 단계는 생략될 수 있다.

     

    **부모가 변경되면 props가 변경됐는지와 상관없이 무조건 자식 컴포넌트도 리렌더링 된다.

    이를 방지하려면 memo를 사용해야함.

     

    **메모이제이션에도 비용이 든다.

    값을 비교하고 렌더링 또는 재계산이 필요한지 확인하는 작업, 그리고 이전에 결과물을 저장해 두었다가 다시 꺼내와야 한다는 두 가지 비용이 있다.

     

    ***따라서 최적화는 항상 신중하고 경계해야한다!!!

     

    최적화에 대한 의견 두 가지.

    1. 섣부른 최적화는 독이니 꼭 필요한 곳에만 메모이제이션 하자!

     가장 이상적인 상황이나, 리액트의 규모가 커지고 개발자들의 수가 늘어나면 어느 곳을 최적화할지 판단하기 힘들다.

     

    2. 모조리 메모이제이션해 버리자

    memo를 사용하지 않았을 때에 발생할 수 있는 문제는 다음과 같다.

     a) 렌더링 비용

     b) 컴포넌트 내부 복잡한 로직 재실행

     c) 위 두가지가 모든 자식 컴포넌트에서 반복되어 일어남

     d) 리액트가 구 트리와 신규 트리를 비교

    따라서 모조리 memo해버리자!

     

    ***결론

    리액트에서 메모이제이션에 대한 두 의견 중 정답은 없다. 

    그러나 필자는 성능에 대해 깊게 고민할 여유가 없는 상황이라면, 일단 의심스러운 곳에는 먼저 다 적용해 볼 것을 권장한다.

    조금이라도 로직이 들어간 컴포넌트는 메모이제이션이 성능 향상에 도움을 줄 가능성이 크다고 한다.

    useMemo와 useCallback 또한 마찬가지.

Designed by Tistory.