React Native 브릿지(Bridge)와 렌더링 과정
RN은 어떻게 통신이 이뤄지고 렌더링 될까?
리액트 네이티브 브릿지(Bridge)란?
일반적으로 리액트 네이티브는 자바스크립트 엔진 기반에서 실행된다.
그러나 실제 동작하는 모바일 어플은 각각 iOS(Objective-c, Swift), aOS(Java, Kotlin) 기반의 플랫폼 기반 언어로 동작한다.
자바스크립트로 각 플랫폼에 맞는 GUI 구성 요소에 필요한 프레임워크를 호출할 수 없기 때문에 중간에 ‘통신 장치’ 가 필요하다.
이 것이 바로 “브릿지” 이며, 자바스크립트와 네이티브 Framework 엔진 간의 통신이 가능하도록 데이터를 교환해주는 통로 역할을 한다.
브릿지 동작 방식
위 그림과 같이 아래 순서로 처리가 이뤄진다.
1. 자바스크립트에서 호출 : 개발자가 작성한 코드는 자바스크립트 모듈(JavaScript Module)에 의해 정의된다. 자바스크립트 모듈은 일반적으로 React 컴포넌트와 같은 UI요소를 정의하거나 어플리캐이션 로직을 정의하는 데 사용된다. 작성된 코드는 자바스크립트 엔진에서 JSON으로 변환 후 브릿지에서 직렬화하여 네이티브 모듈로 전달된다.
자바스크립트 쓰레드 (JavaScript Thread) : JS엔진에서 JS번들을 실행하는 데 사용된다. 앱의 로직 처리, API 호출, 사용자 정의 로직 실행, 애니메이션 계산 등을 담당한다. 리액트 네이티브는 이 쓰레드로 브릿지를 통해 메인 쓰레드와 통신하며 UI 변경사항, 이벤트 처리를 메인 쓰레드에 전달하는 역할을 한다. 여기서 사용되는 JS엔진은 “Hermes” 이다.
Hermes : Meta에서 개발한 리액트 네이티브 전용 자바스크립트 엔진이다. 리액트 네이티브의 실행 속도와 메모리 사용을 감소시키고, 앱 크기 축소 등의 이점을 제공한다. Hermes가 비활성화 되면 “JavaScriptCore (JSC)” 엔진이 대체되어 사용된다. 이 둘의 가장 주요한 차이점은 자바스크립트 소스 코드를 바이트코드로 사전 컴파일 한다는 점이다. 즉, Hermes는 자바스크립트 코드를 미리 hermesc(Hermes Compiler)를 통해 미리 바이트코드로 변환 (Precompilation) 하여 디바이스 단에서 번들 실행 시 컴파일 단계를 거치지 않게 되고 이는 앱 실행 속도를 크게 향상 시킨다.
2. 브릿지 통신 : 자바스크립트 엔진으로부터 처리된 JSON을 “직렬화” 하여 네이티브 모듈(Native Module)로 전달한다. 네이티브 모듈은 말 그대로 네이티브로 작성 된 모듈이다. 브릿지를 통해 자바스크립트 엔진에 의해 호출된다. 또한, 브릿지는 네이티브 모듈을 자바스크립트로 노출시키는 역할도 수행한다. 네이티브 코드로 작성된 네이티브 모듈들을 자바스크립트 코드에서 직접 호출할 수 있는 함수와 상수를 제공한다. 이를 통해 네이티브 코드로 작성된 라이브러리를 자바스크립트에서 직접 사용할 수 있게 된다.
3. 네이티브 측의 실행 : 네이티브는 넘겨 받은 JSON을 파싱하여 실제 모바일 플랫폼의 운영체제의 UI Framework를 사용하여 뷰를 생성하고 조작한다.
메인 쓰레드 (Main Thread) : UI 그리기, 사용자 입력 처리, 네이티브 이벤트 처리에 사용된다. 애니메이션을 포함한 모든 네이티브 UI 작업이 메인 쓰레드에서 실행되고 곧 앱의 성능과 직접적으로 관련되어 있다.
쉐도우 쓰레드 (Shadow Thread) : 레이아웃 계산을 맡아 컴포넌트의 위치(레이아웃)를 계산하고 그 결과를 메인 쓰레드에 전달한다. (그리는 것은 메인 쓰레드가 담당하고 단지 레이아웃만 계산함) 즉, 자바스크립트 쓰레드에서 생성된 가상 DOM의 레이아웃 정보를 바탕으로 네이티브 뷰의 레이아웃을 계산하는데, 이 때 필요한 도구로 “yoga” 가 있다.
cf) yoga는 레이아웃 엔진의 이름이며, Flexbox 레이아웃 시스템에 기반을 둔 임베드 가능한 레이아웃 전용 시스템이다. UI element의 크기와 위치를 결정하고 “그리기 작업은 하지 않는다.”
4. 처리 완료된 값을 역으로 통신 : 처리된 값은 역으로 브릿지를 통해 자바스크립트 엔진으로 전달되고 최종적으로 뷰가 그려진다.
비동기적 통신
리액트 네이티브 브릿지 통신의 최대 단점은 바로 ‘비동기적인 통신’ 이다.
상기 서술했듯이 리액트 네이티브는 iOS, aOS와 같은 일반적인 네이티브와 달리 자바스크립트 엔진에서 동작하기 때문에 통신에 있어 손실을 막기 위한 최적화 기술로 택한 것이 바로 비동기적 방식이다. 즉, 자바스크립트가 네이티브 코드 실행을 기다리지 않아도 되기 때문에 어플리케이션의 반응성이 유지된다는 장점이 있다.
그러나 자바스크립트 엔진으로부터 네이티브 모듈에 요청이 많아진다면, 많은 데이터를 주고 받아야 하는 브릿지에서 병목 현상이 일어날 수 있다. 또한, 이미지/비디오 처리와 같은 고성능이 요구되는 작업을 처리할 때 처리 속도가 느려져 앱이 멈추거나 혹은 버벅거림을 느낄 수 있다.
New Architecture
리액트 네이티브의 브릿지 시스템은 자바스크립트와 네이티브 코드 간의 통신을 용이하게 하는 핵심 메커니즘은 맞지만, 일부 단점과 한계점을 가지고 있다. 이를 위해 Meta 팀은 새로운 아키텍쳐 (New Architecture) 를 점진적으로 도입하고 있으며 최근 0.76
버전부터 기본적으로 적용되어 있다.
- 특징
- 브릿지 없는 모드 (Bridgeless Mode) : 기존의 브릿지를 완전히 제거하여 성능을 향상시키고, 네이티브 모듈과의 직접적인 통신을 가능하게 한다.
- 네이티브 모듈 상호 운용 계층 (Native Module Interop Layer) : 새로운 아키텍처로 전환할 때, 기존의 네이티브 모듈과 새로운 아키텍처가 동시에 작동할 수 있도록 중간 계층을 지원하여 새로운 아키텍처로의 전환을 부드럽게 한다. 이유는, 브릿지는 데이터를 주고 받을 수 있는 간접적인 매개체라면, 새로운 아키텍처는 “Fabric” 과 “TureboModule” 을 기반으로 동작하며 자바스크립트와 네이티브 코드 간의 직접적인 상호작용 을 지원한다. 즉, 구현 방식이 다르기 때문에 하이브리드 형태를 지원함과 동시에 점진적 전환을 위해서다.
도움글
리액트 네이티브 React Native | 브릿지(Bridge) 방식