frontend

React Lane 간단 정리

하리하링웹 2024. 8. 3. 18:22

개요

Lane이란 동시성 문제를 해결하기 위해 나온 개념이다.

 

Lane은 어떤 작업을 처리할 때 이를 먼저 시작된 작업을 처리하는 시간순이 아니라 우선 순위에 따라 해당 작업을 먼저 처리할 수 있도록 도와주는 역할을 한다.

 

현재 react가 렌더링 중이더라도 유저 액션, 애니메이션과 같은 우선순위가 더 높은 작업이 발생된다면 현재 렌더링 과정을 중단하고 우선순위가 높은 작업을 먼저 처리한 뒤 다시 원래 작업하던 곳으로 돌아가 렌더링을 진행할 수 있게 만들어준다.

Lane이란

react 내부에서 Lane은 아래와 같이 정의되어 있다.

const NoLanes: Lanes = /*                        */ 0b0000000000000000000000000000000
const NoLane: Lane = /*                          */ 0b0000000000000000000000000000000

const SyncLane: Lane = /*                        */ 0b0000000000000000000000000000010

const InputContinuousLane: Lane = /*             */ 0b0000000000000000000000000001000

const DefaultLane: Lane = /*                     */ 0b0000000000000000000000000100000

const SyncUpdateLanes: Lane = /*                */ 0b0000000000000000000000000101010

const TransitionLanes: Lanes = /*                       */ 0b0000000011111111111111110000000
const TransitionLane1: Lane = /*                        */ 0b0000000000000000000000010000000
/*...*/
const TransitionLane16: Lane = /*                       */ 0b0000000010000000000000000000000

const RetryLanes: Lanes = /*                            */ 0b0000111100000000000000000000000
const RetryLane1: Lane = /*                             */ 0b0000000100000000000000000000000
/*...*/
const RetryLane4: Lane = /*                             */ 0b0000100000000000000000000000000

 

이는 비트 마스킹 형태로 Lane의 우선순위를 계산하며 이를 위한 연산 과정은 너무 과하고 거기까지 볼 필요는 없다고 생각하기에 생략하며 어떤 종류가 있고 이런식으로 생겼구나 정도만 이해하면 될 것 같다.

 

React는 업데이트가 발생하면 해당 업데이트의 종류에 따라서 Lane 값을 할당해준다. (대충 아래 느낌이다.)

--------------------------------------------------------------
|                        React Lanes                         |
--------------------------------------------------------------
| NoLanes            | ------------------------------------ |
|                    | |                                  | |
--------------------------------------------------------------
| SyncLane           | ------------------------------------ |
|                    | | Task 1                           | |
--------------------------------------------------------------
| InputContinuousLane| ------------------------------------ |
|                    | | Task 2                           | |
--------------------------------------------------------------
| DefaultLane        | ------------------------------------ |
|                    | | Task 3                           | |
--------------------------------------------------------------
| SyncUpdateLanes    | ------------------------------------ |
|                    | | Task 4 | Task 5                  | |
--------------------------------------------------------------
| TransitionLanes    | ------------------------------------ |
|                    | | Task 6 | Task 7 | ... | Task 21   | |
|   TransitionLane1  | ------------------------------------ |
|                    | | Task 6                           | |
|   TransitionLane2  | | Task 7                           | |
|   ...              | | ...                              | |
|   TransitionLane16 | | Task 21                          | |
--------------------------------------------------------------
| RetryLanes         | ------------------------------------ |
|                    | | Task 22 | Task 23 | Task 24 | ...  |
|   RetryLane1       | ------------------------------------ |
|                    | | Task 22                          | |
|   RetryLane2       | | Task 23                          | |
|   ...              | | ...                              | |
|   RetryLane4       | | Task 25                          | |
--------------------------------------------------------------

 

재조정 과정에서 현재 렌더링 대상 정보들을 Lane 위에 들고있으며 이를 배치 처리하는 구조이다.

 

Lane의 우선순위는 아래와 같다.

  1. SyncLane(DiscreteEventPriority)
  2. InputContinuousLane(ContinuousEventPriorit)
  3. DefaultLane(DefaultEventPriority)
  4. TransitionLane

Lane이 할당되는 시점은 아래와 같다.

  • 상태가 변경될 때
  • DOM 이벤트
  • react 외부 이벤트
  • transition 이벤트

react는 내부적으로 requestUpdateLane 함수를 실행하여 업데이트의 우선순위를 찾고 처리한다.

동작

react 18에서는 우선순위를 기반으로 렌더링을 진행한다. 이를 위해 렌더링 시작 전 가장 우선순위가 높은 Lane을 뽑아낸다. 이 때 렌더링이 진행중인 상황에서도 우선순위가 더 높은 Lane이 선택될 수 있는데 이러면 현재 진행중인 렌더링 작업을 중단하고 신규 Lane으로 다시 렌더링을 시작하게 된다.

 

렌더링 작업은 단일 Lane일수도 있지만 복수개일수도 있다. 이는 React가 같은 종류의 Lane에 있는 작업을 배치처리 하기 때문이다.

 

Lane은 업데이트의 발생 여부를 판단하는데에도 사용된다.

 

업데이트가 발생하면 해당 정보는 상태가 변경된 fiber node에 기록된다. 또한 해당 node의 부모부터 최상단 node까지 하위 트리에 업데이트가 발생했음을 기록한다. 이 때 최상단 노드부터 해당 노드까지 업데이트가 발생했다는 정보를 childLanes에 기록한다.

function markUpdateLaneFromFiberToRoot(sourceFiber, update, lane) {
  // 상태가 변경된 노드(fiber)에 업데이트 발생을 기록합니다.
  fiber.lanes = mergeLanes(fiber.lanes, lane)
  const alternate = fiber.alternate
  if (alternate !== null) {
    alternate.lanes = mergeLanes(alternate.lanes, lane)
  }

  // 부모 노드들에 하위 트리에 업데이트 발생을 기록합니다.
  let parent = sourceFiber.return;
  let node = sourceFiber;
  while (parent !== null) {
    parent.childLanes = mergeLanes(parent.childLanes, lane);
    alternate = parent.alternate;
    if (alternate !== null) {
      alternate.childLanes = mergeLanes(alternate.childLanes, lane);
    }

    node = parent;
    parent = parent.return;
  }
}

 

React는 또한 렌더링을 중단하여 보류중인 Lane또한 알고있어야 하며 이 정보는 pendingLines에 기록한다.

export function markRootUpdated(
  root: FiberRoot,
  updateLane: Lane,
  eventTime: number
) {
  root.pendingLanes |= updateLane
  /*...*/
}

 

이 정보를 통해 어떤 노드가 업데이트 되었는지 중단 되었는지에 대한 정보를 알 수 있으며 재조정 과정에서 불필요한 렌더링을 피하고 어떤 상태가 변경되었는지를 빠르게 판단할 수 있게 된다.

이를 통해 react는렌더링 우선순위를 더 효율적으로 관리할 수 있게 된다.