Native Module 생성

Native Module 생성


네이티브 모듈이란

React Native에서 지원하지 않는 Android,IOS 만의 전용 기능을 가진 코드

🚗 Android 네이티브 모듈만들기 ( Java )

Android Studio > ReactNative 프로젝트 / android 폴더 열기

1. 모듈 클래스 만들기

2. ReactNative에 연결할 패키지 만들기

3. 만든 패키지 등록하기

Copy
// app/java/com.nativemoduleworkshop/pakagename.java
package com.pakagename.newarchitecture;
 
import android.app.Application;
import androidx.annotation.NonNull;
import com.facebook.react.PackageList;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
import com.facebook.react.bridge.JSIModulePackage;
import com.facebook.react.bridge.JSIModuleProvider;
import com.facebook.react.bridge.JSIModuleSpec;
import com.facebook.react.bridge.JSIModuleType;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.fabric.ComponentFactory;
import com.facebook.react.fabric.CoreComponentsRegistry;
import com.facebook.react.fabric.EmptyReactNativeConfig;
import com.facebook.react.fabric.FabricJSIModuleProvider;
import com.facebook.react.fabric.ReactNativeConfig;
import com.facebook.react.uimanager.ViewManagerRegistry;
 
//ReactNative Java 모듈 상속
import com.pakagename.BuildConfig;
import com.pakagename.newarchitecture.components.MainComponentsRegistry;
import com.pakagename.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
import java.util.ArrayList;
import java.util.List;
// ReactNative에 등록할 패키지를 만들기
 
/**
 * A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both
 * TurboModule delegates and the Fabric Renderer.
 *
 * <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
 * `newArchEnabled` property). Is ignored otherwise.
 */
public class MainApplicationReactNativeHost extends ReactNativeHost {
  public MainApplicationReactNativeHost(Application application) {
    super(application);
  }
 
  @Override
  public boolean getUseDeveloperSupport() {
 
  /*
        패키지를 받아오는 부분
         */
    return BuildConfig.DEBUG;
  }
 
  @Override
  protected List<ReactPackage> getPackages() {
    List<ReactPackage> packages = new PackageList(this).getPackages();
    // Packages that cannot be autolinked yet can be added manually here, for example:
    //     packages.add(new MyReactNativePackage());
    // TurboModules must also be loaded here providing a valid TurboReactPackage implementation:
    //     packages.add(new TurboReactPackage() { ... });
    // If you have custom Fabric Components, their ViewManagers should also be loaded here
    // inside a ReactPackage.
    return packages;
  }
 
  @Override
  protected String getJSMainModuleName() {
    return "index";
  }
 
  @NonNull
  @Override
  protected ReactPackageTurboModuleManagerDelegate.Builder
      getReactPackageTurboModuleManagerDelegateBuilder() {
    // Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary
    // for the new architecture and to use TurboModules correctly.
    return new MainApplicationTurboModuleManagerDelegate.Builder();
 
  }
 
  @Override
  protected JSIModulePackage getJSIModulePackage() {
    return new JSIModulePackage() {
      @Override
      public List<JSIModuleSpec> getJSIModules(
          final ReactApplicationContext reactApplicationContext,
          final JavaScriptContextHolder jsContext) {
        final List<JSIModuleSpec> specs = new ArrayList<>();
 
        //  @ReactMethod 를 붙여주면,
        // 이후 js 코드에서 호출 할 수 있다.
 
        // Here we provide a new JSIModuleSpec that will be responsible of providing the
        // custom Fabric Components.
        specs.add(
            new JSIModuleSpec() {
              @Override
              public JSIModuleType getJSIModuleType() {
                return JSIModuleType.UIManager;
              }
 
              @Override
              public JSIModuleProvider<UIManager> getJSIModuleProvider() {
                final ComponentFactory componentFactory = new ComponentFactory();
                CoreComponentsRegistry.register(componentFactory);
 
                // Here we register a Components Registry.
                // The one that is generated with the template contains no components
                // and just provides you the one from React Native core.
                MainComponentsRegistry.register(componentFactory);
 
                final ReactInstanceManager reactInstanceManager = getReactInstanceManager();
 
                ViewManagerRegistry viewManagerRegistry =
                    new ViewManagerRegistry(
                        reactInstanceManager.getOrCreateViewManagers(reactApplicationContext));
 
                return new FabricJSIModuleProvider(
                    reactApplicationContext,
                    componentFactory,
                    ReactNativeConfig.DEFAULT_CONFIG,
                    viewManagerRegistry);
              }
            });
        return specs;
      }
    };
  }
}

연결한 패키지 사용하기

Copy
// 프로젝트/App.js
import { NativeModules } from 'react-native'
const App = () => {
	const { ToastModule } = NativeModules;
    ToastModule.show('Hello Module!', ToastModule.SHORT);
	return ...
}
 

🚗 코틀린으로 만들기

React Native 프로젝트 코틀린으로 변경하기

프로젝트 / android / build.gradle 파일 수정

Copy
...
buildscript {
  ext {
    buildToolsVersion = "31.0.0"
    minSdkVersion = 21
    compileSdkVersion = 31
    targetSdkVersion = 31
    kotlinVersion = "1.5.0" // 코틀린 추가
    ...
  }
 
  ...
 
  dependencies {
    classpath("com.android.tools.build:gradle:7.1.1")
    classpath("com.facebook.react:react-native-gradle-plugin")
    classpath("de.undercouch:gradle-download-task:5.0.1")
    // 코틀린 추가
    classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
    ...
  }
  ...
}
 

프로젝트 / android / app / build.gradle 파일 수정

Copy
...
dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
    ...
}
// 맨 마지막 줄 아래에 작성
apply plugin: 'kotlin-android'
 

모듈 만들기

Copy
// 프로젝트/app/java/com.nativemoduleworkshop/BrightnessModule.kt
package com.nativemoduleworkshop
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
// ReactContext 모듈 상속 받기 ( 코틀린 방식 )
class BrightnessModule(reactContext: ReactApplicationContext) :
    ReactContextBaseJavaModule(reactContext) {
    // 모듈 이름 지정
    override fun getName(): String {
        return "BrightnessModule"
    }
    // 상수 내보내기
    override fun getConstants(): MutableMap<String, Any>? {
        val constants = HashMap<String, Any>()
        constants.put("SAMPLE_VALUE", "Hello World")
        return constants;
    }
    // 모듈에서 사용할 메서드 정의
    @ReactMethod
    fun getBrightness(){
    }
    @ReactMethod
    fun setBrightness(brightness: Float){
    }
}
 

패키지 만들기

Copy
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
import java.util.Collections
import kotlin.collections.ArrayList
class BrightnessPackage : ReactPackage {
    // 모듈 등록
    override fun createNativeModules(reactContext: ReactApplicationContext): MutableList<NativeModule> {
        val modules = ArrayList<NativeModule>()
        modules.add(BrightnessModule(reactContext))
        return modules;
    }
    // 네이티브 UI 컴포넌트 등록
    override fun createViewManagers(reactContext: ReactApplicationContext): MutableList<ViewManager<*, ReactShadowNode<*>>> {
        return Collections.emptyList();
    }
}
 

패키지 등록하기

Java와 동일한 방법으로 패키지 등록하기

Copy
// app/java/com.nativemoduleworkshop/MainApplication.java
public class MainApplication extends Application implements ReactApplication {
  private final ReactNativeHost mReactNativeHost =
      new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() { ... }
        /*
        패키지를 받아오는 부분
         */
        @Override
        protected List<ReactPackage> getPackages() {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
 
          // toast 패키지 추가
          packages.add(new ToastPackage());
          // Brightness 패키지 추가
          packages.add(new BrightnessPackage());
          return packages;
        }
        @Override
        protected String getJSMainModuleName() { ... }
      };
 
      ...
}
 

🚗 IOS 네이티브 모듈만들기 ( Objective.c )

XCode > ReactNative 프로젝트 / ios / 프로젝트.xcworkspace 열기

Header File 생성

프로젝트 / 프로젝트 폴더 우클릭 > New FIle > Header File

Header File 작성, 저장

Copy
// RCTBridgeModule 헤더파일 불러오기
#import <React/RCTBridgeModule.h>
// js 호출 가능한 메서드 만들기 위한 헤더파일
#import <UIKit/UIKit.h>
// RCTBridgeModule 객체를, RCTAlertModule라는 이름으로 사용하겠다.
@interface RCTAlertModule : NSObject <RCTBridgeModule>
@end
 

Objective.c File 생성

프로젝트 / 프로젝트 폴더 우클릭 > New FIle > Objective.c File

Objective.c File 작성, 저장

Copy
 ,/// RCTAlertModule.m
// 헤더파일 가져오기
#import "RCTAlertModule.h"
// 헤더파일에서 작성한 RCTAlertModule 상속 받기
@implementation RCTAlertModule
/* 네이티브 모듈 내보내는 메서드
 인자로 모듈의 이름을 지정한다.
 인자를 지정하지 않으면, 클래스 이름에서 RCT를 제외한 부분을 이름으로 사용한다 (AlertModule)
 인자를 문자열 ""로 지정하지 말것!
*/
RCT_EXPORT_MODULE();
// js에서 호출 가능한 메서드 만들기
RCT_EXPORT_METHOD(alert:(NSString *)message)
{
  UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"My Alert"message:@"This is an alert." preferredStyle:UIAlertControllerStyleAlert];
 
  UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@:"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {}];
 
  [alert addAction:defaultAction];
 
  UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
 
  // UI관련 작업을 main스레드에서 실행
  dispatch_async(dispatch_get_main_queue(), ^{
    [rootViewController presentViewController:alert animated:YES completion:nil]
  })
}
// 상수 내보내기
- (NSDictionary *)constantsToExport
{
  return @{
    @"STRING_VALUE": @"Hello World",
    @"NUMBER_VALUE": @(15)
  };
}
// 모듈이 js 코드를 실행하기 전에, main스레드에서 상수 초기화 하기
+ (BOOL)requiresMainQueueSetup
{
  return YES;
}
@end
 

모듈 사용하기

Copy
// 프로젝트/App.js
import { NativeModules } from 'react-native'
const App = () => {
    const { AlertModule } = NativeModules;
    AlertModule.alert('Hello Module!');
    console.log(AlertModule.NUMBER_VALUE);
    return ...
}
 

🚗 Swift로 만들기

XCode > ReactNative 프로젝트 / ios / 프로젝트.xcworkspace 열기

Swift File 생성

프로젝트 / 프로젝트 폴더 우클릭 > New FIle > Swift File

Create Bridging Header 생성 ( 버튼 클릭 )

Copy
// NativeModuleWorkshop-Bridging-Header.h
#import "React/RCTBridgeModule.h"
 

Swift File 작성, 저장

Copy
dfsdf