리액트 파이버란?
JSX는 페이스북이 만든 임의의 문법이기 때문에 JS 표준이 아니다. 그렇기 때문에 JSX문법을 포함한 리액트 코드는 JS엔진이 이해할 수 있도록 babel과 같은 트랜스파일러를 통해 ECMAScript로 변환되는데, 이 때 react.createElement() 호출이 반환된다. 이해를 돕기 위해 예시 코드를 첨부한다.
const MyComponent=(
<div>
<span>Today</span>
</div>
)
// ECMAScript로 변환
var MyComponent=React.createElement(
'div',
null,
React.createElement('span', null, 'hello world'),
)
이렇게 반환된 element를 DOM에 반영하기 전에 먼저 가상DOM에 추가해야한다. 그런데 element만으로는 컴포넌트의 동작을 관리하기 어렵다. 컴포넌트의 상태, 라이프사이클, hook과 같은 정보가 더해져야하는데, 이것들을 효율적으로 관리하는 자바스크립트 객체가 리액트 파이버이다. 즉, React Element와 관련된 상태, DOM 노드, 그리고 렌더링 작업에 대한 메타 정보를 포함한다. 컴포넌트가 최초로 마운팅될 때 생성되고 이후로는 가급적이면 재사용된다.
역할
가상DOM과 실제DOM을 비교해 차이가 있으면 화면에 렌더링을 요청한다. 이 작업을 재조정(reconciliation)이라고 한다.
등장 시기
리액트16버전에서 도입된 개념이다. 하지만 본격적으로 사용되기 시작한건 useTransition 훅이 등장한 18버전부터다.
기존 방식과의 차이점
이전 리액트 조정 알고리즘은 스택 방식이었다. 즉, 렌더링에 필요한 작업들이 동기적으로 이루어졌다. 따라서 중간에 작업을 중단할 수 없어 비효율적이었다. 만약 만개의 컨텐츠를 렌더링하는 페이지에 들어갔다고 가정해보자. 렌더링 작업이 끝나기 전에 다른 이벤트가 발생해도 이벤트를 처리하지 못한다.
반면에 파이버는 비동기적으로 작업한다. 따라서 다음과 같은 일을 할 수 있다.
- 작업을 분할하고 우선순위 매기기
- 높은 우선순위: 애니메이션, 사용자 인터랙션에 반응해야하는 작업, UI 업데이트
- 낮은 우선순위: 데이터 캐싱, 화면에 즉시 표시되지 않는 비동기 작업( ex) preloading ), 백그라운드 작업, 단순 목록 렌더링
- 작업을 잠시 중지했다가 다시 시작하기
- 이전 작업을 재사용하거나 폐기하기
헷갈리면 안되는게 실제 DOM에 반영하는 작업은 동기적으로 실행된다 (물론 나중에 동시성 렌더링이라는 비동기 방식이 나오긴하지만). 비동기적으로 실행될 경우 사용자가 봐야하는 작업이 아닌 다른 작업을 보게될 수도 있기 때문이다.
갖고 있는 속성
- tag : 하나의 element에 하나의 파이버가 생성되는데 이 때 이 매칭 정보를 갖고 있는 것이 태그
- stateNode : 파이버에 대한 참조 정보를 갖고 있음
- child, sibling, return : 트리를 구성하는 데 필요한 파이버 간의 관계 개념을 갖고 있음. 리액트 컴포넌트 트리와 다른 점은 하나의 child만 존재해서 children 개념이 없다는 것. 따라서 자식이 여러개인 경우 관계는 이렇게 정의된다.
- child: 첫번째 자식
- sibling: 나머지 자식
- return: 부모 파이버
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
- index: 여러 형제들 중 자신의 위치가 몇번째인지 나타냄
- pendingProps: 아직 작업을 처리하지 못한 props
- memoizedProps: 렌더링이 완료된 후 pendingProps를 memorizedProps로 저장해 관리
- updateQueue: 상태 업데이트 등 작업을 담는 큐
- momoizedState: 함수 컴포넌트 훅 목록을 저장
- alternate: 두 개의 트리중 반대편 트리 파이버를 가리킴.
파이버 트리
두가지 트리가 존재한다. 왜 두가지인지 알기 위해서 우선 더블 버퍼링이라는 개념을 알아야한다.
더블 버퍼링은 컴퓨터 버퍼링 용어인데, 사용자가 아직 완성되지 않은 그림을 보는 것을 방지하기 위해 보이지 않는 곳에서 다음 그림을 미리 그린 후 이전 그림과 교체하는 기법을 뜻한다.
리액트에서도 이 기법을 사용하기 때문에 작업중인 상태를 나타내는 workInProgress 트리와 현재 모습을 나타내는 Current 트리 두가지가 존재하는 것이다. 작업이 끝나면 포인터만 변경해서 workInProgress트리를 현재 트리로 바꾼다. 이 작업은 커밋 단계에서 일어난다.
커밋 단계란?
리액트 렌더링 단계
리액트는 작업을 하나씩 처리하고 finishedWork() 작업으로 마무리한다. 그리고 작업을 커밋해 실제DOM에 반영한다. 이 과정은 렌더 단계와 커밋 단계로 나뉜다.
- 렌더 단계
- 컴포넌트를 렌더링하고 변경이 필요한 컴포넌트를 체크하는 단계
- 사용자에게 노출되지 않는 모든 비동기 작업 수행 & 파이버의 작업 (우선순위 지정, 작업 중지 등)
- type, props, key 중 하나라도 변경된 것이 있으면 변경이 필요한 컴포넌트로 체크
- 커밋 단계
- 렌더 단계의 변경 사항을 실제 DOM에 적용해 사용자에게 보여주는 과정
- DOM에 변경 사항을 반영하기 위한 작업으로 동기적으로 일어나며 중단될 수 없음
커밋 단계까지 끝나야 브라우저 렌더링 발생
※ 브라우저 렌더링이 발생했다고 해서 반드시 DOM이 업데이트 되는 게 아님. 변경 사항을 계산 했는데 변경이 없다면 커밋 단계는 생략 가능.
정확한 과정은 다음과 같다.
- current 트리를 기준으로 작업 시작
- 업데이트 발생시 파이버는 새 데이터로 새로운 workInProgress 트리 빌드
- 트리 빌드가 끝나면 다음 렌더링에 이 workInProgress 트리 사용
- workInProgress 트리가 UI에 최종적으로 렌더링되어 반영이 끝나면 current가 workInProgress로 변경됨
파이버 노드 생성 순서
- beginWork() 함수 호출 실행을 시작으로 더 이상 자식이 없는 파이버를 만날 때 까지 타고 내려간다.
- 1번 작업이 끝나면 copleteWork() 함수를 실행하여 파이버 작업을 완료한다.
- 형제가 있다면 형제로 넘어간다.
- 2,3번이 끝나면 return으로 돌아가 작업 완료를 알린다.
글로 보면 이해가 안 된다. 그러므로 예시 코드를 보자.
<A1>
<B1></B1>
<B2>
<C1>
<D1/>
<D2/>
</C1>
</B2>
</A1>
A1의 beginWork() 실행 ⮕ B1로 이동해 beginWork() 실행 ⮕ B1은 자식이 없으므로 completeWork() 실행 ⮕ 형제인 B2로 이동해 beginWork() 실행 ⮕ 자식인 C1로 이동해 beginWork() 실행 ⮕ 자식인 D1로 이동해 beginWork() 실행 ⮕ D1은 자식이 없으므로 completeWork() 실행 ⮕ 형제인 D2로 이동해 completeWork() 실행 ⮕ 더 이상 이동할 자식, 형제가 없으므로 위로 차례대로 올라가 D1, C1, B2 순서로 completeWork() 호출 ⮕ B2의 형제 B3으로 이동해 beginWork() 실행 ⮕ B3의 completeWork() 실행 ⮕ 상위로 이동 ⮕ A1의 completeWork() 실행 ⮕ commitWork() 실행 ⮕ 변경이 있으면 DOM에 반영
최초 렌더링 때만 이렇게 모든 파이버를 새롭게 만들어주고 이후로는 업데이트된 props를 받아 내부에서 처리하는 방식으로 재사용한다.
정리
스택 기반 방식에서는 작업 단위가 각 컴포넌트였고 동기적으로 작업하여 스케줄링이 불가능했다. 하지만 파이버라는 개념이 등장하면서 작업 단위가 잘게 쪼개진 파이버 노드로 바뀌었고 비동기로 작업하기 때문에 스케줄링이 가능하게 되었다. 파이버는 각 element에 대한 메타 정보를 갖고 있기 때문에 element와 1:1 관계이다. 컴포넌트 트리와 유사한 속성을 갖고 있지만 특이하게 자식이 많을 경우에는 첫번째 자식만 자식으로 인정(?)하고 나머지는 자식과 형제 관계를 가진다. 더블 버퍼링 기법을 사용하기 위해 두 가지 트리를 갖고 있다. 가상DOM과 비슷한 느낌이지만 리액트 파이버는 브라우저가 아닌 환경에서도 사용할 수 있고 가상DOM은 웹에서만 쓰이는 개념이기 때문에 둘은 동일한 개념이 아니다.
참고
모던 리액트 Deep Dive 도서
https://product.kyobobook.co.kr/detail/S000210725203
모던 리액트 Deep Dive | 김용찬 - 교보문고
모던 리액트 Deep Dive | 요즘 프런트엔드 개발은 자바스크립트와 리액트부터 시작한다는 말이 있을 정도로 최근 몇 년간 프런트엔드 생태계에서 리액트의 비중은 날이 갈수록 커지고 있습니다.
product.kyobobook.co.kr
[React 까보기 시리즈] Fiber 생성 과정
https://www.youtube.com/watch?v=lMuU0uTUoAQ&list=PLpq56DBY9U2B6gAZIbiIami_cLBhpHYCA&index=23
'나도 공부한다 > React' 카테고리의 다른 글
리액트의 동시성 렌더링에 대해 알아보자 (리액트 인터뷰 가이드 5장 보충) (1) | 2024.11.05 |
---|---|
리액트 라우터를 알아보자 (리액트 인터뷰 가이드 4장 보충) (1) | 2024.10.29 |
프레그먼트를 알아보자 (0) | 2024.10.23 |
React 파일 업로드시 확장자 제한하기 (input accept에 의존하면 안되는 이유) (0) | 2024.05.19 |
React "too many re-render" 문제 해결 (0) | 2024.02.01 |