Codegen 사용하기
이 가이드에서는 다음과 같은 내용을 다룬다:
- Codegen 설정 방법
- 각 플랫폼별로 수동으로 Codegen을 실행하는 방법
또한 생성된 코드에 대해 설명한다.
사전 준비
Codegen을 수동으로 실행할 때도 항상 React Native 앱이 필요하다. Codegen 프로세스는 앱 빌드와 밀접하게 연결되어 있으며, 관련 스크립트는 react-native
NPM 패키지에 위치한다.
이 가이드를 위해 React Native CLI를 사용해 프로젝트를 생성한다. 다음 명령어를 실행하면 된다:
npx @react-native-community/cli@latest init SampleApp --version 0.76.0
Codegen은 커스텀 모듈이나 컴포넌트를 위한 접착 코드(glue-code)를 생성하는 데 사용된다. Turbo Native Modules와 Fabric Native Components를 만드는 방법에 대한 자세한 내용은 해당 가이드를 참고한다.
Codegen 설정하기
Codegen은 앱의 package.json
파일을 수정하여 설정할 수 있다. Codegen은 codegenConfig
라는 커스텀 필드로 제어된다.
"codegenConfig": {
"name": "<SpecName>",
"type": "<types>",
"jsSrcsDir": "<source_dir>",
"android": {
"javaPackageName": "<java.package.name>"
},
"ios": {
"modulesConformingToProtocol": {
"RCTImageURLLoader": [
"<iOS-class-conforming-to-RCTImageURLLoader>",
// react-native-camera-roll 예시: https://github.com/react-native-cameraroll/react-native-cameraroll/blob/8a6d1b4279c76e5682a4b443e7a4e111e774ec0a/package.json#L118-L127
// "RNCPHAssetLoader",
],
"RCTURLRequestHandler": [
"<iOS-class-conforming-to-RCTURLRequestHandler>",
// react-native-camera-roll 예시: https://github.com/react-native-cameraroll/react-native-cameraroll/blob/8a6d1b4279c76e5682a4b443e7a4e111e774ec0a/package.json#L118-L127
// "RNCPHAssetUploader",
],
"RCTImageDataDecoder": [
"<iOS-class-conforming-to-RCTImageDataDecoder>",
// 이에 대한 좋은 예시는 없지만 동일한 방식으로 동작한다. RCTImageDataDecoder를 구현하는 클래스의 이름을 전달한다. 이 클래스는 네이티브 모듈이어야 한다.
]
},
"componentProvider": {
"<componentName>": "<iOS-class-implementing-the-component>"
},
}
},
이 스니펫을 앱에 추가하고 다양한 필드를 커스터마이징할 수 있다:
name:
스펙을 포함하는 파일을 생성할 때 사용할 이름이다. 관례적으로Spec
접미사를 붙이는 것이 좋지만 필수는 아니다.type:
생성할 코드의 타입이다. 허용되는 값은modules
,components
,all
이다.modules:
Turbo Native Modules에 대한 코드만 생성할 때 사용한다.components:
Native Fabric Components에 대한 코드만 생성할 때 사용한다.all:
컴포넌트와 모듈이 혼합된 경우 사용한다.
jsSrcsDir:
모든 스펙이 위치한 루트 폴더이다.android.javaPackageName:
Codegen이 파일을 특정 패키지에 생성하도록 안드로이드 전용 설정이다.ios:
ios
필드는 앱 개발자와 라이브러리 관리자가 고급 기능을 제공하기 위해 사용할 수 있는 객체이다. 다음 필드는 모두 선택 사항이다.ios.modulesConformingToProtocol:
React Native는 네이티브 모듈이 특정 동작을 커스터마이징할 수 있도록 몇 가지 프로토콜을 제공한다. 이 필드를 사용하면 해당 프로토콜을 준수하는 모듈 목록을 정의할 수 있다. 이 모듈들은 앱이 시작될 때 React Native 런타임에 주입된다.ios.modulesConformingToProtocol.RCTImageURLLoader:
RCTImageURLLoader
프로토콜을 구현하는 iOS 네이티브 모듈 목록이다.RCTImageURLLoader
를 구현하는 iOS 클래스의 이름을 전달해야 한다. 이 클래스는 네이티브 모듈이어야 한다.ios.modulesConformingToProtocol.RCTURLRequestHandler:
RCTURLRequestHandler
프로토콜을 구현하는 iOS 네이티브 모듈 목록이다.RCTURLRequestHandler
를 구현하는 iOS 클래스의 이름을 전달해야 한다. 이 클래스는 네이티브 모듈이어야 한다.ios.modulesConformingToProtocol.RCTImageDataDecoder:
RCTImageDataDecoder
프로토콜을 구현하는 iOS 네이티브 모듈 목록이다.RCTImageDataDecoder
를 구현하는 iOS 클래스의 이름을 전달해야 한다. 이 클래스는 네이티브 모듈이어야 한다.
ios.componentProvider:
이 필드는 커스텀 JS React 컴포넌트와 이를 구현하는 네이티브 클래스 간의 연결을 생성하기 위해 사용되는 맵이다. 맵의 키는 컴포넌트의 JS 이름(예:TextInput
)이고, 값은 컴포넌트를 구현하는 iOS 클래스(예:RCTTextInput
)이다.
Codegen이 실행되면 앱의 모든 의존성을 검색하여 특정 규칙을 따르는 JS 파일을 찾고 필요한 코드를 생성한다:
- Turbo Native Modules는 스펙 파일이
Native
로 시작해야 한다. 예를 들어NativeLocalStorage.ts
는 유효한 스펙 파일 이름이다. - Native Fabric Components는 스펙 파일이
NativeComponent
로 끝나야 한다. 예를 들어WebViewNativeComponent.ts
는 유효한 스펙 파일 이름이다.
Codegen 실행하기
이 가이드의 나머지 부분에서는 프로젝트에 Native Turbo Module, Native Fabric 컴포넌트 또는 둘 다 이미 설정되어 있다고 가정한다. 또한 package.json
에 지정된 jsSrcsDir
에 유효한 스펙 파일이 있다고 가정한다.
Android
Codegen은 React Native Gradle Plugin(RNGP)과 통합되어 있다. RNGP에는 package.json
파일에 정의된 설정을 읽고 Codegen을 실행할 수 있는 태스크가 포함되어 있다. 이 Gradle 태스크를 실행하려면 먼저 프로젝트의 android
폴더로 이동한 후 다음 명령어를 실행한다:
./gradlew generateCodegenArtifactsFromSchema
이 태스크는 앱에 연결된 모든 프로젝트(앱과 연결된 모든 노드 모듈)에서 generateCodegenArtifactsFromSchema
명령어를 실행한다. 생성된 코드는 각 node_modules/<dependency>
폴더에 저장된다. 예를 들어, Fabric Native Component의 노드 모듈 이름이 my-fabric-component
라면, 생성된 코드는 SampleApp/node_modules/my-fabric-component/android/build/generated/source/codegen
경로에 위치한다. 앱의 경우, 코드는 android/app/build/generated/source/codegen
폴더에 생성된다.
생성된 코드
위의 gradle 명령어를 실행한 후, SampleApp/android/app/build
폴더에서 코드 생성 결과를 확인할 수 있다. 폴더 구조는 다음과 같다:
build
└── generated
└── source
└── codegen
├── java
│ └── com
│ ├── facebook
│ │ └── react
│ │ └── viewmanagers
│ │ ├── <nativeComponent>ManagerDelegate.java
│ │ └── <nativeComponent>ManagerInterface.java
│ └── sampleapp
│ └── NativeLocalStorageSpec.java
├── jni
│ ├── <codegenConfig.name>-generated.cpp
│ ├── <codegenConfig.name>.h
│ ├── CMakeLists.txt
│ └── react
│ └── renderer
│ └── components
│ └── <codegenConfig.name>
│ ├── <codegenConfig.name>JSI-generated.cpp
│ ├── <codegenConfig.name>.h
│ ├── ComponentDescriptors.cpp
│ ├── ComponentDescriptors.h
│ ├── EventEmitters.cpp
│ ├── EventEmitters.h
│ ├── Props.cpp
│ ├── Props.h
│ ├── ShadowNodes.cpp
│ ├── ShadowNodes.h
│ ├── States.cpp
│ └── States.h
└── schema.json
생성된 코드는 두 개의 폴더로 나뉜다:
java
: 플랫폼 특화 코드가 포함된 폴더jni
: 자바스크립트와 자바가 올바르게 상호작용할 수 있도록 필요한 C++ 코드가 포함된 폴더
java
폴더 내부에서는 com/facebook/viewmanagers
하위 폴더에서 Fabric 네이티브 컴포넌트 생성 코드를 찾을 수 있다.
<nativeComponent>ManagerDelegate.java
:ViewManager
가 커스텀 네이티브 컴포넌트에서 호출할 수 있는 메서드가 포함된 파일<nativeComponent>ManagerInterface.java
:ViewManager
의 인터페이스가 포함된 파일
codegenConfig.android.javaPackageName
에 설정된 이름의 폴더에서는 Turbo Native Module이 작업을 수행하기 위해 구현해야 하는 추상 클래스를 찾을 수 있다.
jni
폴더에는 자바스크립트와 안드로이드를 연결하는 모든 보일러플레이트 코드가 포함된다.
<codegenConfig.name>.h
: 커스텀 C++ Turbo Native Module의 인터페이스가 포함된 파일<codegenConfig.name>-generated.cpp
: 커스텀 C++ Turbo Native Module의 연결 코드가 포함된 파일react/renderer/components/<codegenConfig.name>
: 커스텀 컴포넌트에 필요한 모든 연결 코드가 포함된 폴더
이 구조는 codegenConfig.type
필드에 all
값을 사용했을 때 생성된다. 만약 modules
값을 사용한다면 react/renderer/components/
폴더가 생성되지 않는다. components
값을 사용한다면 다른 파일들이 생성되지 않는다.
iOS
iOS의 Codegen은 빌드 과정 중에 실행되는 Node 스크립트를 기반으로 동작한다. 이 스크립트들은 SampleApp/node_modules/react-native/scripts/
폴더에 위치한다.
주요 스크립트는 generate-codegen-artifacts.js
이다. 이 스크립트를 실행하려면 앱의 루트 폴더에서 다음 명령어를 입력한다:
node node_modules/react-native/scripts/generate-codegen-artifacts.js
Usage: generate-codegen-artifacts.js -p [path to app] -t [target platform] -o [output path]
Options:
--help 도움말 표시 [boolean]
--version 버전 번호 표시 [boolean]
-p, --path React Native 프로젝트 루트 경로 [required]
-t, --targetPlatform 타겟 플랫폼. 지원 값: "android", "ios", "all". [required]
-o, --outputPath 생성된 파일이 저장될 경로 [required]
여기서:
--path
는 앱의 루트 폴더 경로를 지정한다.--outputPath
는 Codegen이 생성한 파일을 저장할 경로를 지정한다.--targetPlatform
은 코드를 생성할 플랫폼을 지정한다.
생성된 코드
다음 인자와 함께 스크립트를 실행한다:
node node_modules/react-native/scripts/generate-codegen-artifacts.js \
--path . \
--outputPath ios/ \
--targetPlatform ios
이렇게 하면 ios/build
폴더에 다음과 같은 파일들이 생성된다:
build
└── generated
└── ios
├── <codegenConfig.name>
│ ├── <codegenConfig.name>-generated.mm
│ └── <codegenConfig.name>.h
├── <codegenConfig.name>JSI-generated.cpp
├── <codegenConfig.name>JSI.h
├── FBReactNativeSpec
│ ├── FBReactNativeSpec-generated.mm
│ └── FBReactNativeSpec.h
├── FBReactNativeSpecJSI-generated.cpp
├── FBReactNativeSpecJSI.h
├── RCTModulesConformingToProtocolsProvider.h
├── RCTModulesConformingToProtocolsProvider.mm
└── react
└── renderer
└── components
└── <codegenConfig.name>
├── ComponentDescriptors.cpp
├── ComponentDescriptors.h
├── EventEmitters.cpp
├── EventEmitters.h
├── Props.cpp
├── Props.h
├── RCTComponentViewHelpers.h
├── ShadowNodes.cpp
├── ShadowNodes.h
├── States.cpp
└── States.h
이 중 일부 생성된 파일은 React Native 코어에서 사용된다. 또한 package.json의 codegenConfig.name
필드에 지정한 이름과 동일한 이름을 가진 파일들도 포함된다.
<codegenConfig.name>/<codegenConfig.name>.h
: 커스텀 iOS Turbo Native Modules의 인터페이스를 포함한다.<codegenConfig.name>/<codegenConfig.name>-generated.mm
: 커스텀 iOS Turbo Native Modules의 연결 코드를 포함한다.<codegenConfig.name>JSI.h
: 커스텀 C++ Turbo Native Modules의 인터페이스를 포함한다.<codegenConfig.name>JSI-generated.h
: 커스텀 C++ Turbo Native Modules의 연결 코드를 포함한다.react/renderer/components/<codegenConfig.name>
: 커스텀 컴포넌트에 필요한 모든 연결 코드를 포함하는 폴더이다.
이 구조는 codegenConfig.type
필드에 all
값을 사용해 생성되었다. 만약 modules
값을 사용하면 react/renderer/components/
폴더가 생성되지 않는다. components
값을 사용하면 다른 파일들이 생성되지 않는다.