React Native의 Native Module — 개념, 생성 및 통합 방법

저자: IT Sectr 게시일: 2026-06-03 읽는 시간: 9 분

Native Module은 React Native의 JavaScript에서 플랫폼 네이티브 API에 접근할 수 있게 해주는 Java 또는 Objective-C 클래스입니다. 각 모듈은 Bridge에 등록되며 JS 코드에서 일반 함수처럼 호출할 수 있는 메서드를 내보냅니다. Meta, 2024에 따르면, Native Module은 React Native 애플리케이션에 플랫폼 코드를 통합하는 주요 방식으로 남아 있습니다.

핵심 사항

  • Native Module — JavaScript와 iOS 또는 Android 네이티브 코드 간의 다리 역할.
  • 등록 — 모듈은 iOS 어노테이션(RCT_EXPORT_MODULE) 또는 Android(@ReactMethod)을 통해 선언됩니다.
  • 데이터 유형 — 원시 타입, 문자열, 배열, 딕셔너리 및 Promise가 지원됩니다.
  • 사용 — JS에서 모듈은 NativeModules.모듈이름을 통해 접근할 수 있습니다.
  • 새로운 아키텍처 — React Native 0.76+에서 Native Module은 JSI를 통해 Turbo Module로 작동할 수 있습니다.

Native Module이란?

Native Module은 React Native의 아키텍처 요소로, 플랫폼 언어(iOS의 경우 Objective-C/Swift, Android의 경우 Java/Kotlin)로 코드를 실행하고 결과를 JavaScript로 반환할 수 있게 해줍니다. Native Module 없이는 카메라, GPS, 가속도계, 파일 시스템 또는 Bluetooth와 같은 네이티브 디바이스 기능에 접근할 수 없습니다.

React Native에는 기본 제공 Native Module 세트가 포함되어 있습니다: CameraRoll, AsyncStorage, Geolocation, NetInfo 등이 있습니다. 그러나 특정 작업(타사 SDK 통합, 하드웨어 센서 작업, 백그라운드 프로세스)을 위해 개발자는 사용자 정의 모듈을 만듭니다. State of React Native 2024 설문조사에 따르면, 67%의 개발자가 프로젝트에서 최소 하나의 사용자 정의 Native Module을 사용합니다.

Native Module 아키텍처는 React Native 버전에 따라 다릅니다. 클래식 아키텍처(React Native 0.72 이하)에서는 모듈이 Bridge를 통해 연결되며 JSON 직렬화를 통해 JS와 비동기적으로 통신합니다. 새로운 아키텍처(React Native 0.76+)에서는 모듈이 직렬화 없이 동기적 접근을 위해 JSI를 사용하는 Turbo Module로 작동할 수 있습니다.

iOS용 Native Module 생성

iOS용 Native Module 생성은 RCTBridgeModule 프로토콜을 구현하는 Objective-C 클래스를 선언하는 것으로 시작됩니다. RCT_EXPORT_MODULE 매크로는 모듈을 Bridge에 등록하고, RCT_EXPORT_METHOD는 JavaScript에서 사용 가능한 메서드를 내보냅니다.

objective-c
// ImageCompressor.m — iOS용 Native Module
@interface ImageCompressor () RCT_EXPORT_MODULE()
@end

@implementation ImageCompressor

RCT_EXPORT_METHOD(compressImage:(NSString *)imagePath
                  quality:(NSNumber *)quality
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
    NSData *compressedData = [UIImageJPEGRepresentation(image, quality.floatValue)];
    NSString *outputPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"compressed.jpg"];
    [compressedData writeToFile:outputPath atomically:YES];
    resolve(outputPath);
}

@end

compressImage 메서드는 이미지 경로와 압축 품질(0.0~1.0)을 받아 네이티브 측에서 데이터를 처리하고 압축된 파일의 경로를 반환합니다. 주요 장점은 압축이 네이티브 코드로 수행되어 동등한 JavaScript 작업보다 훨씬 빠르고 메모리 효율적이라는 것입니다.

Swift 모듈의 경우 클래스와 메서드 앞에 @objc 어노테이션을 사용하여 Bridge가 작동하는 Objective-C 런타임에서 접근 가능하도록 합니다. 클래스는 NSObject를 상속하고 RCTBridgeModule을 구현해야 합니다.

swift
// ImageCompressor.swift — Swift Native Module
@objc(ImageCompressor)
class ImageCompressor: NSObject {

    @objc
    func compressImage(
        _ imagePath: String,
        quality: Float,
        resolver: @escaping RCTPromiseResolveBlock,
        rejecter: @escaping RCTPromiseRejectBlock
    ) {
        guard let image = UIImage(contentsOfFile: imagePath) else {
            rejecter("FILE_ERROR", "Cannot load image", nil)
            return
        }
        guard let data = image.jpegData(compressionQuality: CGFloat(quality)) else {
            rejecter("COMPRESS_ERROR", "Compression failed", nil)
            return
        }
        let outputPath = NSTemporaryDirectory() + "compressed.jpg"
        try? data.write(to: URL(fileURLWithPath: outputPath))
        resolver(outputPath)
    }
}

Android용 Native Module 생성

Android에서 Native Module은 ReactContextBaseJavaModule을 확장하는 Java 클래스로 생성됩니다. @ReactMethod 어노테이션은 메서드를 Bridge로 내보냅니다. 결과를 반환하기 위해 com.facebook.react.bridge의 Promise 인터페이스가 사용됩니다.

java
// ImageCompressorModule.java — Android Native Module
public class ImageCompressorModule
    extends ReactContextBaseJavaModule {

    @Override
    public String getName() {
        return "ImageCompressor";
    }

    @ReactMethod
    public void compressImage(
            String imagePath,
            Float quality,
            Promise promise) {
        try {
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
            File outputFile = new File(
                ReactNative.getApplicationContext()
                    .getCacheDir(), "compressed.jpg");
            FileOutputStream fos = new FileOutputStream(outputFile);
            bitmap.compress(
                Bitmap.CompressFormat.JPEG,
                (int)(quality * 100), fos);
            fos.close();
            promise.resolve(outputFile.getAbsolutePath());
        } catch (Exception e) {
            promise.reject("COMPRESS_ERROR", e.getMessage());
        }
    }
}

getName() 메서드는 JavaScript에서 접근 가능한 모듈 이름을 반환합니다. 위 예제에서 모듈은 ImageCompressor로 등록됩니다. @ReactMethod 어노테이션은 Bridge에 메서드를 내보내야 함을 알립니다. 중요: 메서드는 void여야 하며 Bridge가 지원하는 타입(String, Boolean, Integer, Double, ReadableArray, ReadableMap, Promise)만 허용합니다.

모듈 클래스를 생성한 후 애플리케이션 패키지에 등록해야 합니다. 이를 위해 ReactPackage를 구현하는 클래스를 만들고 createNativeModules 메서드의 모듈 목록에 추가합니다.

java
// ImageCompressorPackage.java — 모듈 등록
public class ImageCompressorPackage implements ReactPackage {

    @Override
    public List<NativeModule> createNativeModules(
            ReactApplicationContext reactContext) {
        return Arrays.asList(
            new ImageCompressorModule(reactContext)
        );
    }

    @Override
    public List<ViewManager> createViewManagers(
            ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

Native Module 등록 및 사용

두 플랫폼용 모듈을 생성한 후 React Native에 등록해야 합니다. Android의 경우 패키지는 MainApplication.java의 getPackages() 메서드에 추가됩니다. iOS의 경우 모듈은 RCT_EXPORT_MODULE 매크로를 통해 자동으로 등록되지만 AppDelegate.mm에서 수동 등록도 사용할 수 있습니다.

java
// MainApplication.java — React Native에 패키지 추가
import com.yourapp.nativemodules.ImageCompressorPackage;

public class MainApplication extends Application
    implements ReactApplication {

    private final ReactNativeHost mReactNativeHost =
        new ReactNativeHost(this) {

        @Override
        protected List<ReactPackage> getPackages() {
            List<ReactPackage> packages =
                new PackageList(this).getPackages();
            packages.add(new ImageCompressorPackage());
            return packages;
        }
    };
}

등록 후 모듈은 JavaScript에서 NativeModules를 통해 사용할 수 있습니다. React Native는 Android의 경우 getName() 또는 iOS의 경우 RCT_EXPORT_MODULE에 지정된 모듈 이름을 자동으로 사용합니다.

js
// JavaScript에서 Native Module 사용
import { NativeModules } from 'react-native';
import { Platform } from 'react-native';

const ImageCompressor = NativeModules.ImageCompressor;

async function compressPhoto(uri: string) {
  try {
    const result = await ImageCompressor.compressImage(
      uri.replace('file://', ''), 0.8
    );
    console.log('압축됨:', result);
    return result;
  } catch (error) {
    console.error('압축 실패:', error);
    throw error;
  }
}

Native Module vs Turbo Module: 비교

클래식 Native Module과 Turbo Module의 비교는 새 프로젝트에 어떤 접근 방식을 선택할지 이해하는 데 도움이 됩니다. 두 메커니즘 모두 네이티브 코드에 대한 접근을 제공하지만 아키텍처와 성능에서 근본적으로 다릅니다.

특성Native Module (Bridge)Turbo Module (JSI)
통신JSON을 통한 비동기JSI를 통한 동기
직렬화호출마다 JSON데이터 복사 없음
타이핑수동, 생성 없음Codegen을 통한 자동
로딩애플리케이션 초기화 시지연 로딩(lazy load)
호환성모든 React Native 버전React Native 0.73+

React Native 0.72 이하의 기존 프로젝트의 경우 클래식 Native Module이 주요 선택으로 남아 있습니다. 새 프로젝트의 경우, 특히 빈번한 네이티브 메서드 호출에서 높은 성능이 필요한 경우 Turbo Module이 권장됩니다. React Native의 점진적인 업데이트와 함께 커뮤니티는 새로운 아키텍처로의 완전한 전환을 향해 나아가고 있습니다.

자주 묻는 질문

Native Module에서 Promise 대신 콜백을 전달할 수 있나요?

, Bridge는 콜백을 지원합니다. Promise 대신 iOS에서는 RCTResponseSenderBlock 콜백 함수를, Android에서는 Callback을 사용할 수 있습니다. 그러나 Promise가 현대적인 표준으로 간주되며 새 모듈에 권장됩니다.

Xcode나 Android Studio에서 Native Module을 디버그하는 방법은?

Native Module은 일반 네이티브 코드처럼 디버그됩니다 — Xcode 또는 Android Studio에서 중단점을 설정하세요. iOS의 경우 React Native와 함께 빌드 스킴을 사용하고, Android의 경우 Debug 구성을 사용합니다. 진입점은 JS에서 호출된 메서드입니다.

Native Module이 지원하지 않는 데이터 유형은?

Bridge를 통한 Native Module은 바이너리 데이터(NSData/byte[]), 사용자 정의 객체 및 함수를 지원하지 않습니다. 이미지의 경우 파일 경로 또는 base64 문자열을 사용하세요. JSI를 통한 Turbo Module은 이러한 제한 중 일부를 해소합니다.

React Native를 업데이트할 때 Native Module을 다시 작성해야 하나요?

일반적으로 아니요 — Native Module API는 안정적이고 하위 호환됩니다. 새로운 아키텍처(Turbo Module)로 전환할 때 모듈은 데코레이터를 통해 적응되지만 기존 코드는 계속 작동합니다.

Native Module에서 JavaScript로 이벤트를 보내는 방법은?

iOS에서는 RCTEventEmitter를, Android에서는 DeviceEventEmitter를 사용하세요. 모듈이 이벤트를 보내고 JS 측에서 react-native의 NativeEventEmitter를 통해 구독합니다. 이는 스트리밍 데이터 및 센서 이벤트에 유용합니다.

요약

  • Native Module — React Native의 JavaScript에서 네이티브 API에 접근할 수 있게 하는 Java/Objective-C 클래스.
  • 등록 — iOS는 RCT_EXPORT_MODULE, Android는 @ReactMethod와 함께 ReactContextBaseJavaModule을 사용.
  • 호출 — JS에서 모듈은 NativeModules.Module로 접근 가능하며 Promise와 콜백 지원.
  • 데이터 유형 — Bridge는 JSON 호환 유형(문자열, 숫자, 배열, 딕셔너리)만 전송.
  • 성능 — 클래식 Native Module은 비동기성과 직렬화로 인해 Turbo Module보다 열등.
  • 마이그레이션 — React Native 0.76+로 업그레이드 시 모듈은 로직 재작성 없이 Turbo Module로 작동 가능.
  • 적용 — Native Module은 카메라, Bluetooth, 파일 시스템 및 SDK 통합에 필수적.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기