유리의 개발새발
[RN] Native Module (legacy) 본문
공식 문서를 기반으로 한 번 해보져.
https://reactnative.dev/docs/legacy/native-modules-intro
Native Modules Intro · React Native
Sometimes a React Native app needs to access a native platform API that is not available by default in JavaScript, for example the native APIs to access Apple or Google Pay. Maybe you want to reuse some existing Objective-C, Swift, Java or C++ libraries wi
reactnative.dev
Android에서 먼저 시작
Android는 비교적 구조가 단순하니 여기서부터 해보겠습니다.
비교적 Android가 쉬우니까 먼저 해보겠습니다.
1. CalendarModule.kt 작성
android/app/src/main/java/com/{프로젝트명}/CalendarModule.kt 생성.
package com.practice_rn_app
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import android.util.Log
class CalendarModule(reactContext: ReactApplicationContext)
: ReactContextBaseJavaModule(reactContext) {
override fun getName() = "CalendarModule"
@ReactMethod
fun createCalendarEvent(name: String, location: String) {
Log.d("CalendarModule", "Create event called with name: $name, location: $location")
}
}
✔ getName() → JS에서 모듈을 불러올 때 사용할 이름 지정
✔ @ReactMethod → JS에서 호출 가능한 메서드 표시
JS에서는 이렇게 접근합니다.
import { NativeModules } from 'react-native';
const { CalendarModule } = NativeModules;
CalendarModule.createCalendarEvent('회의', '서울');
2. 패키지 등록
android/app/src/main/java/com/{프로젝트명}/MyAppPackage.kt 생성
package com.practice_rn_app
import android.view.View
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ReactShadowNode
import com.facebook.react.uimanager.ViewManager
class MyAppPackage : ReactPackage {
override fun createViewManagers(
reactContext: ReactApplicationContext
): MutableList<ViewManager<View, ReactShadowNode<*>>> = mutableListOf()
override fun createNativeModules(
reactContext: ReactApplicationContext
): MutableList<NativeModule> =
listOf(CalendarModule(reactContext)).toMutableList()
}
3. MainApplication.kt에 등록
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
add(MyAppPackage()) // 직접 만든 패키지 추가
}
여기까지 하면 Android 네이티브 모듈 등록 완료! 🎉
너무 쉽지 않나요?
iOS에서 CalendarModule 만들기
이제 iOS 차례입니다.
⚠️ 팁: iOS는 Xcode에서 작업하는 게 정신 건강에 좋아요.
1. 헤더 파일 작성
RCTCalendarModule.h
#import <React/RCTBridgeModule.h>
@interface RCTCalendarModule : NSObject <RCTBridgeModule>
@end
클래스 이름: RCTCalendarModule
Objective-C에는 네임스페이스가 없으므로 접두어(prefix)를 붙이는 게 일반적입니다. 여기서 RCT는 React를 의미합니다.
2. 구현 파일 작성
RCTCalendarModule.m
#import "RCTCalendarModule.h"
#import <React/RCTLog.h>
@implementation RCTCalendarModule
// 모듈 등록
RCT_EXPORT_MODULE();
// JS에서 호출할 수 있는 메서드 정의
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name location:(NSString *)location) {
RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
}
@end
✔ RCT_EXPORT_MODULE() → 모듈 등록. 인자 없이 쓰면 클래스명에서 RCT 접두어가 빠진 이름(CalendarModule)으로 JS에 노출됩니다.
✔ RCT_EXPORT_METHOD → JS에서 접근 가능한 메서드. 반환값은 항상 void (비동기).
JS에서는 이렇게 사용합니다.
import { NativeModules } from 'react-native';
const { CalendarModule } = NativeModules;
CalendarModule.createCalendarEvent('스터디', '카페');
🚨 동기 메서드 주의
@ReactMethod(isBlockingSynchronousMethod = true) (Android)
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD (iOS)
동기 호출도 가능하지만 성능 저하 + 스레드 버그 위험이 크고, Chrome 디버거도 못 쓰게 되니 특별한 이유가 없다면 비추천합니다.
Android vs iOS 네이티브 모듈 파일 구조
1. 모듈 파일 (예: CalendarModule.kt)
- 역할: 실제 네이티브 기능 구현
- ReactContextBaseJavaModule(레거시) 또는 Native…Spec(Turbo) 상속
- getName() → JS에서 사용할 모듈 이름 정의
- @ReactMethod 붙은 메서드 → JS에서 호출 가능
- 쉽게 말하면: “이 파일이 JS에서 호출되는 기능 본체”
2. 패키지 파일 (예: MyAppPackage.kt)
- 역할: React Native에 내가 만든 모듈을 등록하는 관리자
- ReactPackage 인터페이스 구현
- createNativeModules()에서 내 모듈 인스턴스를 반환
- MainApplication에서 getPackages()에 추가해야 함
- 쉽게 말하면: “앱이 실행될 때 내 모듈을 React Native에 연결해 주는 다리”
🍎 iOS
1. 헤더 파일 (예: RCTCalendarModule.h)
- 역할: 인터페이스(명세) 정의
- @interface RCTCalendarModule : NSObject <RCTBridgeModule>
- 이 파일을 통해 “이 클래스는 네이티브 모듈이고, 이런 메서드들을 가질 수 있다”를 알림
- 쉽게 말하면: “이 모듈은 이런 기능을 가질 거야!” 선언문
2. 구현 파일 (예: RCTCalendarModule.m)
- 역할: 실제 기능 구현
- RCT_EXPORT_MODULE() → JS에서 사용할 모듈 이름 등록
- RCT_EXPORT_METHOD → JS에서 호출 가능한 메서드 구현
- RCTLogInfo 같은 API 호출로 동작 확인 가능
- 쉽게 말하면: “헤더에서 약속한 기능을 실제로 만드는 곳”
// ++
📝 브리징 헤더(Bridging Header)란?
- Swift 코드에서 Objective-C 코드(헤더 파일)를 가져다 쓰려면 필요한 파일이에요.
- 즉, Swift ↔ ObjC 간 상호운용을 위해 자동으로 생성하자고 Xcode가 묻는 겁니다.
- React Native 네이티브 모듈은 전통적으로 Objective-C 예제로 제공되는데, 프로젝트 기본 언어를 Swift로 만들면 Xcode가 “Swift에서 ObjC 코드를 쓸 거면 브리징 헤더를 만들래?” 하고 물어보는 거죠.
🤔 왜 필요한가?
React Native 예제는 보통 Objective-C(.m, .h) 기반으로 제공되는데,
프로젝트 기본 언어를 Swift로 만들면 Xcode가 묻습니다:
“Swift에서 ObjC 코드도 쓸 거야? 그럼 내가 브리징 헤더 만들어 줄까?”
👉 예를 누르면 프로젝트명-Bridging-Header.h라는 파일이 생깁니다.
🛠️ 어떻게 쓰는가?
1. 브리징 헤더 파일 안에, Swift에서 쓰고 싶은 ObjC 헤더를 #import 합니다.
예시 (practice_rn_app-Bridging-Header.h)
#import "RCTCalendarModule.h"
#import <React/RCTLog.h>
2. 그러면 Swift 파일에서 별도의 import 없이 바로 해당 클래스/함수를 쓸 수 있습니다.
예시 (Swift 네이티브 모듈)
import Foundation
@objc(CalendarModule)
class CalendarModule: NSObject {
@objc func createCalendarEvent(_ name: String, location: String) {
RCTLogInfo("Create event called with name: \(name), location: \(location)")
}
}
✅ 요약
- ObjC 기반 코드를 Swift에서 사용하려면 브리징 헤더가 필요하다.
- Swift 기반 프로젝트인데 ObjC 모듈을 추가하면 Xcode가 자동으로 “브리징 헤더 만들래?” 하고 물어본다.
- ObjC만 쓰거나 Swift만 쓰면 필요 없다.
- Swift + ObjC 혼합 프로젝트일 때만 필요하다.
지금 .m/.h로 모듈을 짜고 있다면 브리징 헤더는 필요 없음.
Swift로 네이티브 모듈을 만들고 싶을 때만 신경 쓰면 됩니다.
'React Native' 카테고리의 다른 글
| [RN] 프로젝트 초기에 할 것 (0) | 2025.12.24 |
|---|---|
| [RN] 채팅 기능 (socket.io) (0) | 2025.09.07 |
| [RN] Firebase FCM 02 (4) | 2025.08.26 |
| [RN] Firebase FCM (7) | 2025.08.26 |
| [RN] react-native-seoul/kakao-login issue (0) | 2025.02.15 |