[React Native] New Architecture 2편 (Fabric)

우혁주·2024년 8월 2일

Fabric

Fabric은 뉴아키텍처에서 도입된 새로운 렌더링 시스템이다.

이 역시 JSI를 사용해 브릿지의 의존성을 없애며, UI 랜더링 성능을 크게 향상시켰다.

그렇다면, 이 새로운 렌더링 시스템인 Fabric은 RN의 UI 관련 성능을 어떻게 향상시킨 것일까?

  • 레이아웃 점프: 비동기 처리로 인해 레이아웃이 완전히 렌더링되기 전에 중간 상태(빈 공간)가 사용자에게 보일 수 있다.

  • 프레임 드랍: 비동기 통신으로 인해 UI 업데이트가 지연되면서 애니메이션이 끊기거나 프레임이 드랍되는 현상이 발생할 수 있다. 이는 JavaScript 스레드가 바쁜 경우 네이티브 스레드로의 업데이트가 지연되기 때문이다. ( 나의 포스팅 중 이에 대해 다룬적이 있다 링크 )

  • Shadow Tree의 변경 가능성

    • 기존 아키텍처에서는 Shadow Tree가 JavaScript 스레드에서 생성되고, UI 변경이 있을 때마다 Shadow Tree가 업데이트되었다.
    • 여러 스레드에서 Shadow Tree를 동시에 접근하고 변경할 수 있었기 때문에, 데이터 일관성을 유지하기가 어려웠다.
    • 예를 들어, JavaScript 스레드와 네이티브 스레드가 동시에 Shadow Tree를 변경하려고 하면 충돌이 발생할 수 있었다.

기존 방식의 UI랜더링 방식을 보고싶다면? 제 블로그를 구경해봐주세용 ~ !


Fabric의 UI 렌더링 방식은 어떤지 한번 알아보자!

  1. JavaScript 코드 실행 (JavaScript 스레드):

    • JavaScript 스레드에서 React와 JavaScript 코드를 실행한다.
    • 이 단계에서는 컴포넌트 트리를 생성하고, 상태와 props를 처리한다.
    • React는 컴포넌트의 렌더 메서드를 호출하여 React Element Tree를 생성한다.
      • React Element Tree는 순수한 JavaScript 객체로, UI의 구조와 속성을 표현한다.

    기존 아키텍처와 다른점:

    • Fabric은 React 18에서 도입된 기능인 Concurrent Mode를 지원하여 우선순위 기반의 렌더링이 가능해져, 중요한 업데이트를 더 빠르게 처리할 수 있다.
    • 기존엔 이 부분에서 Shadow Tree를 생성했다.
  2. Shadow Tree 생성 ( C++ 코어 )

    • React Element Tree를 기반으로 네이티브 스레드에서 Shadow Tree를 생성한다.

    기존 아키텍처와 다른점:

    • Fabric에서는 Shadow Tree가 에 C++ 코어에서 생성되며, 불변성을 가진다.
    • 기존의 Shadow Tree는 Shadow Thread에서 생성
  3. 동기 레이아웃 계산 (네이티브 스레드):

    • Shadow Tree는 불변(Immutable)으로 생성되어 데이터 일관성을 유지한다.
    • Shadow Tree를 사용하여 Yoga 레이아웃 엔진으로 동기적으로 레이아웃을 계산한다.
    • JavaScript Interface (JSI)를 통해 JavaScript 스레드와 네이티브 코드 간의 효율적인 통신이 이루어진다.

    기존 아키텍처와 다른점:

    • 동기적으로 레이아웃을 계산하여 지연을 줄인다.
    • JSI를 통해 네이티브 코드와 더 빠르게 통신할 수 있어, 레이아웃 계산 과정에서의 지연이 크게 줄어든다.
    • 네이티브 코드에서 Yoga를 사용함으로써 레이아웃 계산의 성능이 향상되었고, 모든 플랫폼에서 일관된 레이아웃 결과를 얻을 수 있게 되었다.
  4. 네이티브 트리 생성 및 업데이트 (네이티브 스레드):

    • 계산된 레이아웃 정보를 바탕으로 네이티브 UI 트리를 생성하거나 업데이트한다.
    • 이 단계에서는 네이티브 뷰의 생성, 삭제, 속성 업데이트 등이 이루어진다.
    • 네이티브 트리는 실제 네이티브 UI 컴포넌트의 구조를 나타낸다.

    기존 아키텍처와 다른점:

    • Fabric에서는 불변 Shadow Tree와 동기적으로 계산된 레이아웃 정보를 사용하여 네이티브 트리를 생성하고 업데이트한다.
    • 이로 인해 네이티브 트리 생성 및 업데이트 과정이 더 빠르고 효율적이다.
    • Fabric은 증분 업데이트(Incremental Updates)를 지원하여, 변경된 부분만 효율적으로 업데이트할 수 있다.
  5. UI 렌더링 (메인(UI) 스레드):

    • 메인(UI) 스레드에서 최종적으로 UI를 렌더링한다.
    • 이 단계에서는 최종적으로 화면에 UI가 그려진다.
    • 네이티브 UI 컴포넌트가 생성되거나 업데이트되어 사용자에게 표시된다.

    기존 아키텍처와 다른점:

    • Fabric의 최적화된 과정 덕분에 UI 렌더링이 더 빠르고 일관되게 이루어진다.
    • Fabric은 우선순위 기반 렌더링을 지원하여, 중요한 UI 업데이트를 더 빠르게 처리할 수 있다.
    • 또한, 백그라운드 우선순위 렌더링을 통해 메인 스레드의 부하를 줄이고, 더 부드러운 UI 경험을 제공한다.

Concurrent Mode (React 18에서 도입된 기능인 Concurrent Mode을 Fabric에서 사용할 수 있게 됐다!!)

  1. 기존 아키텍처:
    • 기존 React Native 아키텍처에서는 렌더링 작업이 단일 스레드에서 순차적으로 처리되었다.
    • 모든 렌더링 작업이 동일한 우선순위로 처리되었기 때문에, 복잡한 업데이트가 있을 경우 UI가 느려지거나 응답하지 않는 경우가 발생할 수 있었다.
  2. Concurrent Mode 기능
    • Concurrent Mode는 렌더링 작업을 여러 작은 작업으로 나누어, 우선순위가 높은 작업을 먼저 처리할 수 있게 해준다.
    • 이를 통해 중요한 업데이트를 더 빠르게 처리하고, 덜 중요한 작업은 백그라운드에서 처리할 수 있다.
    • 사용자 인터페이스가 더 빠르고 반응성 있게 동작할 수 있다.
    • 예를 들어, 사용자 입력과 같은 중요한 작업은 즉시 처리하고, 레이아웃 변경과 같은 덜 중요한 작업은 나중에 처리한다.

Incremental Updates ( Fabric 기능 )

  1. 기존 아키텍처:
    • 기존 React Native 아키텍처에서는 상태나 props가 변경될 때 전체 UI를 다시 렌더링하는 경향이 있었다.
    • 이는 불필요한 리소스 사용과 성능 저하를 초래할 수 있었다.
    • Shadow Tree를 사용하여 변경된 부분을 감지하고 업데이트했지만, 여전히 많은 경우 전체 트리를 다시 계산해야 했다.
  2. Fabric 아키텍처:
    • Incremental Updates는 변경된 부분만을 업데이트하는 방식이다.
    • 전체 UI를 다시 렌더링하는 대신, 변경된 컴포넌트와 그 자식 컴포넌트들만 업데이트한다.
    • 이를 통해 성능을 최적화하고, 불필요한 리소스 사용을 줄일 수 있다.
    • Fabric은 더 정교한 변경 감지 메커니즘을 사용하여, 최소한의 업데이트로 최대한의 성능을 이끌어낸다

새로운 아키텍처에서 Shadow Tree

기존의 Shadow Tree와 New Architecture에서의 Shadow Tree 를 비교하며 자세히 알아보자.

image

문제와 해결

문제

기존의 아키텍쳐에서는 Shadow Tree는 새로 생성되는게 아니라, 어느 스레드에서 변경을 하던 계속해서 Shadow Tree가 변경되어서, 충돌이 발생했고, 데이터가 불안정하게 관리됐다. 또한 동일한 데이터에 서로 다른 스레드에서 동시에 접근하면 잠금현상이 발생할 수도 있었다.

해결

처음에 자바스크립트가 실행되면 JavaScript코드를 실행하고 해당 정보를 브릿지 없이 바로 네이티브스레드에 보낸다. 네이티브스레드에 보내진 정보를 바탕으로 공간을 공유하고 있는 C++이 해당 정보에 바로 접근하여 Yoga와 함께 그려질 화면을 계산한다.(이는 변화가 바로바로 동기적으로 수행되는 것이다.) 계산을 마치면 네이티브 UI스레드가 바로 이 계산을 가지고 유저에게 보이는 화면을 그려낸다.

첫 실행시 ReactElementTree를 기반으로 변하지 않는 ShadowTree1을 만들고 새로운 ShadowTree1-1를 만든다. 이때 변화가 생기면 ReactElementTree가 변화를 감지해 ReactElementTree가 변경되며, 이와 ShadowTree1을 기반으로 변한부분만 감지하고 적용해 다시 새로운 ShadowTree1-1를 만드는 방식으로 작동하기에 ShadowTree1은 불변의 성질을 가지기에 안정적으로 작동한다.


여기까지 Fabric에 대해서 알아봤다. 남은 TurboModules와 Codegen은 바로 다음 포스팅에서 확인할 수 있다!

profile
프론트엔드 개발자

0개의 댓글