
안녕하세요~~~ ☺️👋 벌써 React Native New Architecture 3번째 시간입니다.
오늘은 새로운 아키텍처의 핵심 구성 요소라고 할 수 있는 JSI에 대해 포스팅해보려 합니다!
(Codegen은 이전 포스팅을 참고해주세요.)
새로운 아키텍처를 이해할 때 가장 중요한 포인트는 JavaScript와 네이티브가 어떻게 연결되는가? 입니다.
JSI는 JavaScript와 네이티브를 연결해주는 인터페이스로, 구 아키텍처의 브릿지 역할을 대체하기 위해 ☄️혜성같이☄️ 등장했습니다. JSI가 무엇이고, 어떻게 기존 브릿지의 역할을 대체하는지 자세히 알아봅시다! 👀
JSI(JavaScript Interface)는 기존 아키텍처의 브릿지를 대체하여 JavaScript와 네이티브를 직접 연결할 수 있도록 해주는 C++ 기반 인터페이스입니다. JSI는 C++ 힙 메모리에 직접 생성되며, JavaScript 엔진은 이 객체에 대한 포인터를 가지게 됩니다. 이로 인해 JavaScript와 네이티브 코드가 서로의 메모리 공간을 공유하게 되어 자바스크립트와 네이티브 간의 경계가 없어질 수 있게 됩니다.
JSI를 통해 아래의 구성 요소들이 JavaScript와 직접 연결됩니다.
1. Hermes 엔진
Hermes는 기기에서 JavaScript 코드를 실행하는 JavaScript 엔진이며, JSI를 통해 네이티브 코드와 직접 연결됩니다. JSI는 런타임에 접근할 수 있는 인터페이스를 제공하여, JavaScript에서 네이티브 함수나 객체를 직접 호출할 수 있게 합니다.
2. Turbo Module
TurboModule은 JSI를 통해 JavaScript에서 브릿지 없이 네이티브 메서드를 호출할 수 있도록 구현되어 있습니다.
3. Fabric
Fabric은 JSI를 기반으로 JavaScript와 네이티브 UI 렌더러를 연결하는 새로운 렌더링 엔진입니다.
(위 구성 요소들에 대해서는 다음 포스팅에서 자세히 살펴볼 예정 입니다! 🤩)
JSI를 통해 네이티브 메서드는 C++ 객체 형태로 JavaScript에 노출되며, JavaScript 코드에서 이를 참조하고 직접 호출할 수 있습니다. 이때 노출된 C++ 객체를 HostObject 라고 부릅니다. HostObject는 JSI에서 매우 중요한 개념으로, 런타임에 등록된 구현체가 JavaScript 코드 내에서 직접 참조될 수 있고, JavaScript 코드는 네이티브 메서드를 마치 자체 메서드처럼 호출할 수 있습니다.
(이는 마치 웹에서 JavaScript가 모든 DOM 요소를 참조하고, 해당 메서드를 호출하는 방식과 유사합니다. 🙂)
1. 앱 시작 시, Codegen은 모듈의 인터페이스를 정의
이전 포스팅 내용을 잠깐 복습하자면, Codegen은 빌드 과정에서 모듈의 C++ 기반 인터페이스를 생성합니다. JSI는 이 인터페이스를 기준으로 어떤 모듈을 찾아야 하는지, 어떤 메서드를 호출해야하는지 알 수 있습니다. 이 인터페이스 정의가 없다면, JSI는 필요한 모듈을 알 수 없습니다. 😭😭
2. JavaScript에서 모듈 호출
새로운 아키텍처에서는 Turbo Module을 사용하여 네이티브 모듈을 구현합니다. JavaScript에서 이 모듈을 호출할 때, JSI는 가장 먼저 Codegen이 정의한 인터페이스를 참조합니다. 최초 호출인 경우 해당 모듈이 로드되어 있지 않으므로, JSI는 모듈을 동적으로 로드하고 초기화한 뒤 호출합니다. 이미 로드된 경우는 모듈을 바로 호출합니다.
JSI에는 다음과 같은 특징이 있어, 여러가지 이점을 가져다줍니다. 대표적으로 세가지 특징을 설명드리겠습니다!🥹
동시성 : JavaScript는 UI 스레드에서 실행되는 함수를 직접 호출할 수 있습니다.
오버헤드 감소 : 새로운 아키텍처는 더 이상 데이터를 직렬화하거나 역직렬화할 필요가 없으므로 직렬화로 인한 추가 비용이 발생하지 않습니다.
동기 방식 : JSI는 비동기/동기 방식을 모두 지원하지만, 비동기적으로 실행 되어서는 안 되는 함수들은 동기적으로 실행할 수 있습니다.
제가 그중에서도 가장 강조하고 싶은 특징이 세번째 특징인 동기 방식 부분인데요, JSI의 동기 방식이 어떤 이점을 가져왔는지 자세히 알아봅시다!! 😎
비동기 호출은 UI 스레드나 JavaScript 스레드를 차단하지 않기 때문에 대부분의 경우 여전히 선호됩니다. 하지만 다음과 같은 상황에서는 동기 호출이 유익할 수 있습니다.
네이티브 모듈 초기화
일부 네이티브 모듈은 사용하기 전에 초기화해야 하는데, 비동기적으로 초기화할 경우 지연이나 충돌이 발생할 수 있습니다. JSI를 사용하면 네이티브 모듈을 동기적으로 초기화하여 이러한 문제를 방지할 수 있습니다.
상수 접근
일부 네이티브 모듈은 장치 정보나 플랫폼별 값과 같이 JavaScript 코드에서 사용되는 상수를 노출합니다. (ex. OS, 장치 이름, 앱 버전 등등..) JSI를 사용하면 이러한 상수에 브릿지 메시지를 기다릴 필요 없이 JavaScript에서 동기적으로 접근할 수 있습니다.
동기식 네이티브 메서드 호출
일부 네이티브 메서드는 클립보드 접근이나 현재 위치 가져오기처럼 JavaScript 코드에 값을 즉시 반환해야 합니다. JSI를 사용하면 콜백이나 프로미스를 사용하지 않고도 이러한 메서드를 JavaScript에서 동기적으로 호출할 수 있습니다.
Bridge의 비효율적인 통신 한계를 해결하기 위해 ☄️혜성같이☄️ 등장한 우리 JSI...!
Bridge와 JSI를 한 눈에 비교해보며... Bridge를 이제 그만 보내주도록 하겠습니다...🥹🥹
| 구분 | Bridge | JSI |
|---|---|---|
| 모듈 등록 시점 | 앱 구동 시, ReactPackage 목록을 통해 Java/ObjC 모듈을 한 번에 등록 | 앱 실행 시, C++ 객체를 JS 런타임(globalThis)에 직접 주입 |
| 데이터 전달 방식 | JS ↔ Bridge 간 JSON 직렬화/역직렬화 | JS ↔ C++ 간 메모리 직접 접근 (함수 포인터 호출) |
| 호출 타이밍 | 비동기 (Bridge 메시지 큐 통해 전달) | 동기/비동기 모두 가능 (C++ 런타임에서 직접 실행) |
| 핵심 특징 | 모듈을 미리 다 등록해두고 이름으로 호출 | 런타임에 직접 바인딩하고 바로 실행 |
Bridge 방식은 이제 안녕 👋
JSI는 브릿지를 대체하기에 매우 적절!! +_+
다음 포스팅에서는 새로운 아키텍처의 다른 구성 요소인 Hermes 엔진에 대해 알아보겠습니다!
그럼 다음 포스팅에서 만나요~~ 🥹👋
참고
https://medium.com/mj-studio/react-native-new-architecture-guide-9fd045438530