Kotlin으로 레거시 네이티브 모듈 작성하기
Kotlin에서 ReactContextBaseJavaModule을 만들고 @ReactMethod로 메서드에 주석을 단 뒤 ReactPackage를 통해 노출하고 JavaScript에서 호출합니다.
Kotlin으로 레거시 네이티브 모듈 작성하기은(는) CoddyKit의 무료 React Native Academy 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 React Native Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. React Native Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
What Is a Legacy Native Module
A legacy native module allows you to call native Android (Kotlin/Java) code from JavaScript in a React Native app. When the built-in React Native APIs do not cover a device feature you need, you write a native module to bridge the gap. Legacy modules use the Bridge architecture, which means calls go asynchronously across the JS-to-native bridge.
Creating the Kotlin Module Class
Every Android native module extends ReactContextBaseJavaModule. You override getName() to return the name that JavaScript will use to call your module. Place this class inside the android/app/src/main/java/com/yourapp/ folder alongside existing Android source files.
// ToastModule.kt
package com.yourapp
import android.widget.Toast
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
class ToastModule(reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {
override fun getName(): String = 'ToastModule'
}Annotating Methods with @ReactMethod
Any method you want to expose to JavaScript must be annotated with @ReactMethod. The method must be public and its return type must be void — results are returned asynchronously via callbacks or promises, not return values. React Native serializes parameters automatically for basic types like String, Int, Boolean, and Double.
@ReactMethod
fun show(message: String, duration: Int) {
val durationConst = if (duration == Toast.LENGTH_SHORT)
Toast.LENGTH_SHORT else Toast.LENGTH_LONG
Toast.makeText(reactApplicationContext, message, durationConst).show()
}Creating the ReactPackage
Native modules must be registered through a ReactPackage. You implement the ReactPackage interface and return your module from createNativeModules. The package is then added to the list of packages in MainApplication.kt so React Native includes it at startup.
// ToastPackage.kt
package com.yourapp
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
class ToastPackage : ReactPackage {
override fun createNativeModules(
reactContext: ReactApplicationContext
): List<NativeModule> = listOf(ToastModule(reactContext))
override fun createViewManagers(
reactContext: ReactApplicationContext
): List<ViewManager<*, *>> = emptyList()
}Registering the Package in MainApplication
Open MainApplication.kt and find the getPackages() method. Add an instance of your ToastPackage to the list. React Native calls this during startup to discover all available native modules and register their JavaScript interfaces.
// MainApplication.kt (inside getPackages)
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
add(ToastPackage()) // <-- register your package here
}Calling the Module from JavaScript
On the JavaScript side, import NativeModules from react-native and access your module by the name returned from getName(). Wrap the native module in a helper file to provide a clean TypeScript API and hide the raw NativeModules reference from your components.
// ToastModule.js
import { NativeModules } from 'react-native';
const { ToastModule } = NativeModules;
export function showToast(message, duration = 'short') {
const dur = duration === 'short' ? 0 : 1;
ToastModule.show(message, dur);
}
// Usage in a component:
// import { showToast } from './ToastModule';
// showToast('Hello from native!', 'long');Returning Values with Promises
Because @ReactMethod methods must return void, you use Promise parameters to send data back to JavaScript. Add promise: Promise as the last parameter, call promise.resolve(value) on success, or promise.reject(code, message) on error. On the JS side you await the call like any async function.
// Kotlin
@ReactMethod
fun getDeviceName(promise: Promise) {
try {
val name = android.os.Build.MODEL
promise.resolve(name)
} catch (e: Exception) {
promise.reject('ERROR', e.message)
}
}
// JavaScript
const name = await NativeModules.MyModule.getDeviceName();
console.log('Device:', name);Using Callbacks as an Alternative
Before Promises became standard, native modules used Callback parameters (Callback type in the SDK). You receive one or two callbacks — typically successCallback and errorCallback — and invoke them from native code. Callbacks can only be called once; use Promises or events for repeated responses.
@ReactMethod
fun getBatteryLevel(
successCallback: Callback,
errorCallback: Callback
) {
try {
val intent = reactApplicationContext.registerReceiver(
null,
android.content.IntentFilter(
android.content.Intent.ACTION_BATTERY_CHANGED
)
)
val level = intent?.getIntExtra(
android.os.BatteryManager.EXTRA_LEVEL, -1
) ?: -1
successCallback.invoke(level)
} catch (e: Exception) {
errorCallback.invoke(e.message)
}
}Passing Complex Data with WritableMap
To return an object (JSON map) from native to JavaScript, use WritableMap and Arguments.createMap(). You add key-value pairs to the map using typed putters like putString, putInt, and putBoolean. The map is automatically serialized into a JavaScript object when it reaches the JS layer.
@ReactMethod
fun getDeviceInfo(promise: Promise) {
val map = Arguments.createMap()
map.putString('model', android.os.Build.MODEL)
map.putString('brand', android.os.Build.BRAND)
map.putInt('sdkVersion', android.os.Build.VERSION.SDK_INT)
map.putBoolean('isTablet', reactApplicationContext
.resources.configuration.smallestScreenWidthDp >= 600)
promise.resolve(map)
}The isBlockingSynchronousMethod Annotation
By default all @ReactMethod calls are asynchronous. Adding isBlockingSynchronousMethod = true makes the call synchronous — the JS thread blocks until native returns. This is strongly discouraged in production because it freezes the UI, but it can be useful for debugging or reading a tiny cached value that must be available immediately.
@ReactMethod(isBlockingSynchronousMethod = true)
fun getAppVersionSync(): String {
return reactApplicationContext
.packageManager
.getPackageInfo(reactApplicationContext.packageName, 0)
.versionName ?: 'unknown'
}Module Constants via getConstants
You can expose compile-time constants to JavaScript by overriding getConstants() in your module. Constants are transferred once at startup and are synchronously accessible as NativeModules.MyModule.CONSTANT_NAME with no async call needed. Use constants for fixed values like error codes, config flags, or platform identifiers.
override fun getConstants(): Map<String, Any> {
return mapOf(
'LONG_TOAST' to Toast.LENGTH_LONG,
'SHORT_TOAST' to Toast.LENGTH_SHORT,
'PLATFORM' to 'android'
)
}
// In JavaScript:
// const { LONG_TOAST } = NativeModules.ToastModule;
// showToast('Hello!', LONG_TOAST);Quick Check
Test your understanding of React Native Mobile Development concepts from this lesson.
Lesson Recap
In this lesson you learned: how to create a ReactContextBaseJavaModule in Kotlin, annotate methods with @ReactMethod to expose them to JavaScript, and register the module through a ReactPackage in MainApplication. You also saw how to return data using Promises and WritableMap. Next up we explore writing a legacy native module in Swift for iOS.
자주 묻는 질문
“Kotlin으로 레거시 네이티브 모듈 작성하기” 강의는 무료인가요?
네 — “Kotlin으로 레거시 네이티브 모듈 작성하기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 React Native Academy 강의 전체를 잠금 해제할 수 있습니다. React Native Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“Kotlin으로 레거시 네이티브 모듈 작성하기”에서 뭘 배우나요?
Kotlin에서 ReactContextBaseJavaModule을 만들고 @ReactMethod로 메서드에 주석을 단 뒤 ReactPackage를 통해 노출하고 JavaScript에서 호출합니다. 브라우저에서 직접 실행하는 실습 코드로 React Native Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
React Native Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 React Native Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.
“Kotlin으로 레거시 네이티브 모듈 작성하기” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 React Native Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 React Native Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- Kotlin으로 레거시 네이티브 모듈 작성하기
- Swift로 레거시 네이티브 모듈 작성하기
- JSI를 사용한 Turbo 네이티브 모듈
- 네이티브에서 비동기 콜백, 프로미스 및 이벤트 사용하기