아직 실험적 기능이다. (작성일 기준 최신버전 0.73)
엄격한 인터페이스로 플랫폼 전반적으로 일관성 높은 코드
C++로 코드를 작성할 수 있어 플랫폼 간에 구현 중복 필요성 X (C++로 Turbo Module 작성하기)
모듈을 지연로딩해 빠른 앱 시작 가능
JSI를 활용해 브릿지보다 네이티브 코드와 JS 코드간 더 효율적 통신
create-react-native-library에서도 turbo module 스캐폴딩 지원
모듈을 앱에서 분리된 상태로 유지하려면 모듈을 앱과 별도로 정의한 다음 나중에 앱에 종속성으로 추가하는걸 추천
분리해서 관리하면 오픈소스로 릴리즈 하기도 좋다.
문서는 프로젝트와 완전히 분리하지만 이 글에서는 루트프로젝트에서 작업
native module 폴더명은 접두사 RTN(REACT-NATIVE) 권장
하위 폴더로 android, ios,js 생성 (예시는 android만 생성)

뉴 아키텍쳐는 Flow 혹은 Typescript로 정의된 인터페이스 필요. (Codegen은 이를 기반으로 C++, Objective-C++, JAVA를 포함한 언어로 코드를 생성)
파일명 규칙
Native<MODULE_NAME>, ex) NativeRecorder.ts
Flow => .js,.jsx / Typescript => .ts,.tsx
파일은 TurboModuleRegistrySpec를 export 시켜야함
NativeRecorder.ts
import { TurboModule, TurboModuleRegistry } from 'react-native';
// 타입 이름은 반드시 Spec
// 모든 파일은 터보 네이티브 모듈의 상세 내용을 가지고 있어야 한다.
// 네이티브 모듈에서 override할 함수를 여기서 정의한다.
export interface Spec extends TurboModule {
record(): Promise<string>;
stop(): Promise<string>;
}
// 모듈의 Name을 get의 파라미터로 넘겨주고 모듈을 가져 올 수 있으면 가져온다
export default TurboModuleRegistry.get<Spec>('RTNRecorder') as Spec | null;
index.ts (필수사항 x), js/NativeModule.ts에 직접 접근해도 된다.
import NativeRecorder from './NativeRecorder';
export const record = async () => {
return await NativeRecorder?.record();
};
export const stop = async () => {
return await NativeRecorder?.stop();
};
codegen을 위한 package.json에 config 작성
Turbo Native Library 위치에 package.json
{
"name": "rtn-recorder",
"version": "0.0.1",
"description": "Record Turbo Module",
"react-native": "js/index",
"source": "js/index",
"files": [
"js",
"android",
"ios",
"rtn-recorder.podspec",
"!android/build",
"!ios/build",
"!**/__tests__",
"!**/__fixtures__",
"!**/__mocks__"
],
"keywords": ["react-native", "ios", "android"],
"license": "MIT",
"peerDependencies": {
"react": "*",
"react-native": "*"
},
"codegenConfig": {
"name": "RTNRecorderSpec",
"type": "modules",
"jsSrcsDir": "js",
"android": {
"javaPackageName": "com.rtnrecorder"
}
}
}
codegenCofnig
android 폴더 내에 build.gradle 생성
build.gradle
buildscript {
ext.safeExtGet = {prop, fallback ->
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
repositories {
google()
gradlePluginPortal()
}
dependencies {
classpath("com.android.tools.build:gradle:7.3.1")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.22")
}
}
apply plugin: 'com.android.library'
apply plugin: 'com.facebook.react'
apply plugin: 'org.jetbrains.kotlin.android'
android {
compileSdkVersion safeExtGet('compileSdkVersion', 33)
// 패키지명
namespace "com.rtncalculator"
}
repositories {
mavenCentral()
google()
}
dependencies {
implementation 'com.facebook.react:react-native'
}
TurboReactPackage 인터페이스 상속
android/src/main/java/com/rtnrecorder경로에 생성
RN은 ReactPackage 인터페이스를 활용하여 Native Module 클래스를 이해한다.
RecorderPackage.kt
package com.rtncalculator;
import com.facebook.react.TurboReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.model.ReactModuleInfoProvider
class CalculatorPackage : TurboReactPackage() {
override fun getModule(name: String?, reactContext: ReactApplicationContext): NativeModule? = null
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider? = null
}
RecorderModule.kt
package com.rtnrecorder
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
// NativeCalculatorSpec은 자바스크립트 파일명(NativeRecorder)을 기준으로 생성
import com.rtnrecorder.NativeRecorderSpec
class RecorderModule(reactContext: ReactApplicationContext) : NativeRecorderSpec(reactContext) {
override fun getName() = NAME
// JS Spec에서 정의한 함수
override fun record(promise: Promise) {
promise.resolve("recording")
}
override fun stop(promise: Promise) {
promise.resolve("stop")
}
// JS TurboModuleRegistry.get에서 호출하는 ModuleName
companion object {
const val NAME = "RTNRecorder"
}
}
RecorderPackage.kt
package com.rtnrecorder;
import com.facebook.react.TurboReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.model.ReactModuleInfo // 추가
import com.facebook.react.module.model.ReactModuleInfoProvider
class RecorderPackage : TurboReactPackage() {
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? =
if (name == RecorderModule.NAME) {
RecorderModule(reactContext)
} else {
null
}
override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
mapOf(
RecorderModule.NAME to ReactModuleInfo(
RecorderModule.NAME,
RecorderModule.NAME,
false, // canOverrideExistingModule
false, // needsEagerInit
true, // hasConstants
false, // isCxxModule
true // isTurboModule
)
)
}
}
명령어 (node_modules에 추가)
yarn add ./RTNRecorder
node_modules

package.json

android/gradle.properties위치에서 newArchEnabled를 true로 설정import {record, stop} from 'rtn-recorder';
...
const App = () => {
const onPressRecord = async () => {
const status = await record();
setStatus(status);
};
const onPressStop = async () => {
const status = await stop();
setStatus(status);
};
...
}