[React] Fiber에 대해서

한호수 (The Lake)·2023년 4월 6일
4

React를 매번 사용하면서 렌더링이 어떤 방식으로 동작하는지 알지 못했는데 여러 포스팅을 읽다가 Fiber라는 키워드를 발견하고 공부하게 되어서 포스팅을 작성했다. 다만 Fiber는 react v16에서 업데이트된 내용으로 사실 내가 React를 처음 사용할때부터 함께해왔던 존재였다.
Fiber 소개영상 : https://youtu.be/ZCuYPiUIONs

reconciliation(재조정)

재조정(reconciliation)이란, React에서 매우 중요한 개념 중 하나로 UI에서 변경 사항이 발생하면 DOM 과 Virtual DOM을 비교하여 변경 사항을 식별하고 업데이트하는 과정을 뜻한다.

재조정과 렌더링은 각각 reconciler 모듈과 renderer 모듈로 분리되어 실행된다.

React v16 이전의 reconciler 모듈은 Stack Reconciler라는 모듈이었다.

Stack Reconciler

Stack Reconciler의 문제점은 virtual DOM 트리를 비교하고 변경 사항을 적용하는 과정에서 모든 작업을 동기적으로, 하나의 큰 테스크로 진행한다는 점이다. 이렇게 큰 태스크는 깊은 콜 스택을 만들게 되고 콜 스택이 모두 처리될때 까지 메인 스레드는 아무작업도 할 수 없게 된다.

일반적으로 Reconciler and Renderer 과정은 매우 빠르게 일어나기 때문에 보통의 경우라면 느끼기 어렵지만 연속적인 애니메이션과 많은 DOM조작이 필요한 경우 콜 스택이 모두 처리될때 까지 메인 스레드는 아무작업을 할 수 없게 되니 버벅임을 느낄 수 있게 된다.

React 팀은 이 현상을 해결하기 위해 증분 렌더링(Incremental Rendering)을 고안하게 되었고 그 과정에서 Fiber Reconcilerreconciler 모듈이 변경되었다.

왼쪽 Stack Reconciler / 오른쪽 Fiber Reconciler

Fiber Reconciler

증분 렌더링(Incremental Rendering)를 구현하기 위해서는 작업을 일시정지하고 나중에 다시 시작할 수 있어야 했다. 또 이전에 완료된 작업을 재사용하거나 필요하지 않은 경우 중단 할 수 있어야 했다. 그래서 도입된게 Fiber이다.

Mathpresso 프론트앤드 개발자 베일리님의 CodePen

Fiber는 React에서 작업의 단위이며, JavaSrciprt 객체이다. console에서 요소를 불러와 직접 확인할 수 있다.

Fiber에 대한 특징

  • Fiber는 항상 무언가와 1:1 관계를 갖으며 그 관계는 tag 속성을 통해서 알 수 있다.(컴포넌트, Dom 요소 등등)
  • stateNode 속성은 요소 자체에 대한 참조를 가지고 있다.(React element)
  • key는 우리가 배열에서 맵핑할때 지정하는 key와 같은 속성이다.
  • React element와 fiber는 매우 유사하지만 중요한 차이점은 React element는 매번 다시 생성 되지만 React Fiber는 가능한 자주 재사용된다.(초기 마운트 시 Fiber가 생성되며 그 후에는 대부분 재사용된다.)
  • React는 Fiber를 처리할때 마다 우선순위에 따라 다음 작업을 처리할 것 인지 예약할 것 인지 정할 수 있다.
  • 어떤 작업이 애니메이션과 같은 높은 우선순위를 가지고 있다면 requestAnimationFrame 함수를 통해 우선순위가 높은 함수를 예약할 수 있다.
    또는 requestIdleCallback 함수를 통해 유휴 기간동안 호출한 우선순위가 낮은 함수를 예약할 수 있다.(두 함수를 지원하지 않는 브라우저의 경우 폴리필을 제공한다.)
  • alternate 속성은 이전 작업 current Fiber와 workInProgress Fiber가 연결되어있는 한쌍으로 Fiber가 재사용되기 위해서 매우 중요한 속성 ( 다음 내용에서 다룬다. )
  • Fiber의 속성에 child, sibling, return이 있으며 각각 자식Fiber, 형제Fiber, 부모Fiber가 저장되어 있어 자식요소가 없다면 형제요소를 탐색하고 형제가 없다면 부모요소를 탐색하는 DFS 알고리즘으로 작동한다.

동작 방법

두가지 단계로 나누어져 있다.

렌더 단계(Render Phase)

Fiber는currentworkInProgress 두가지 트리를 가지고 있다.

current 트리는 현재 화면에 있는 것으로 React가 변경 하면 안정성을 보장 할 수 없기 때문에 복사본인 workInProgress 트리를 만들어서 작업하게 된다.

Render Phase는 비동기적으로 동작하며 두 fiber 트리를 비교하고 변경된 Effect들을 수집하는 작업을 한다. 리액트 scheduler로 인해 허용되는 시간 동안 작업하고 user input, animation 같은 더 급한 작업이 있다면 해당 작업에게 메인 스레드를 양보한다.

Effect는 Dom을 변경하거나 특정 생명 주기 메서드를 호출하는 것 같은 활동이다. 이러한 활동은 부작용(Side Effect)라고 부르며 다른 구성요소에 영향을 미칠 수 있으며 렌더링 중에는 실행할 수 없다.
( 예외는 존재한다. 렌더링 메소드 자체나 shouldComponentUpadte 메소드는 렌더링 단계 중에 호출된다. )

위 과정에서 React가 언제라도 workInProgress 트리 내부 변경사항을 버릴 수 있기 때문에 Dom 또는 componentDidMount 같은 생명 주기 메서드에 대한 변경사항은 렌더링 단계중에서 실행 할 수 없다.

커밋 단계(Commit Phase)

커밋 단계에서는 렌더 단계에서 수집한 Effect와 변경된 정보를 가지고 있는 Fiber를 통해 Effect를 실행하고 Dom에 적용하는 단계를 거친다. 이 단계는 동기적으로 한번에 이루어지기 때문에 일시정지하거나 취소할 수 없다.

Commit 후에는 workInProgress 트리가 현재(current)의 트리가 된다.

Commit Phase에서 교체되는 두 트리

Fiber의 도입으로 등장한 기술

  • Error Boundary
  • Suspense 와 React.lazy
  • Concurrent Mode

Reference
https://blog.mathpresso.com/react-deep-dive-fiber-88860f6edbd0
https://www.theteams.kr/teams/7792/post/70621
https://youtu.be/ZCuYPiUIONs
https://youtu.be/0ympFIwQFJw

profile
항상 근거를 찾는 사람이 되자

0개의 댓글