Native Module 만들어서 사용하기

박은정·2022년 7월 19일
3

리액트네이티브

목록 보기
1/24
post-thumbnail

React Native Native Module 공식문서를 토대로 번역하고 공부했습니다.

Native Modules Intro

Native Modules Intro

종종 리액트 네이티브로 만든 어플은 자바스크립트에서 기본적으로 사용할 수 없는 네이티브 플랫폼 API을 활용해야 합니다. 기존 오브젝트C, 스위프트, 자바 또는 C++ 라이브러리를 자바스크립트로 다시 구현하지 않고 그대로 재사용하거나, 이미지 처리와 같은 것들을 위한 고성능 멀티 스레드 코드를 작성할 수 있습니다.

네이티브모듈 시스템은 자바/오브젝트C/C++(네이티브) 클래스의 인스턴스를 자바스크립트 개체로 자바스크립트에 노출하여 자바스크립트 내에서 임의의 네이티브 코드를 실행할 수 있습니다. 이 기능이 일반적으로 개발 프로세스의 일부가 되지는 않겠지만 반드시 존재해야 합니다. 리액트 네이티브가 자바스크립트 애플리케이션에 필요한 네이티브 API를 내보내지 않으면 직접 만들어서 내보낼 수도 있습니다.

Native Module Setup

리액트 네이티브 애플리케이션을 위한 네이티브 모듈을 작성하는 방법은 아래 두 가지가 있습니다.

  1. 리액트 네이티브 애플리케이션의 iOS/Android 프로젝트 안에서 직접 수행
  2. 다른 리액트 네이티브 응용 프로그램에서 종속성으로 설치할 수 있는 npm 패키지로 사용됩니다.

이 가이드는 먼저 리액트 네이티브 애플리케이션 안에서 직접 네이티브 모듈을 구현하는 과정을 안내합니다. 하지만 다음 가이드에서 작성하는 기본 모듈은 npm 패키지로도 배포할 수 있습니다.

Getting Started

다음 섹션에서는 리액트 네이티브 응용 프로그램 안에서 직접 네이티브 모듈을 구축하는 방법에 대한 안내합니다. 사전 요구 사항으로 작동하려면 리액트 네이티브 응용 프로그램이 필요합니다. 리액트 네이티브 프로그램이 아직 없는 경우 다음 단계에 따라 설정할 수 있습니다.

일정 이벤트를 만들기 위해 리액트 네이티브 응용 프로그램 안에서 자바스크립트에서 iOS/Android 기본 일정 API을 활용하려 한다고 가정할때, 리액트 네이티브는 네이티브 캘린더 라이브러리와 통신하기 위해 자바스크립트 API를 노출하지 않습니다. 대신 네이티브 모듈을 통해서 네이티브 캘린더 API와 통신하는 네이티브 코드를 작성할 수 있습니다. 그런 다음 리액트 네이티브 응용 프로그램의 자바스크립트를 통해 해당 네이티브 코드를 호출할 수 있습니다.


Android Native Modules

Create a Calendar Native Module

다음 가이드에서는 자바스크립트에서 안드로이드의 캘린터 API에 접근할 수 있는 기본 모듈인 CalenerModule 을 만듭니다. 모듈을 다 만들고 나서 자바스크립트에서 아래와 같은 네이티브 코드를 호출해서 캘린더 이벤트를 만드는 자바/코틀린 메서드를 호출할 수 있습니다.

CalenderModule.createCalendarEvent('Dinner Party', 'MyHouse')

Turbo Module

리액트 네이티브 팀은 현재 리액트모듈 시스템의 재-아키텍처를 연구하고 있습니다. 이러한 새로운 시스템은 터보모듈이라고 불리며, 리액트 네이티브 브릿지에 의존하지 않고 자바스크립트와 네이티브 사이에서 보다 효율적인 유형의 안전 통신을 할 수 있게 합니다. 또한 기존의 네이티브 모듈 시스템에서는 불가능했던 새로운 확장 기능을 사용할 수 있습니다. 자세한 내용은 여기를 참조해주세요. 이 문서에서는 터보모듈 릴리스에서 변경되는 기본 모듈의 일부와 터보모듈로의 원할한 업그레이드를 위한 최선의 준비방법에 대해 설명합니다.

Setup

네이티브 모듈을 만들기 위해 안드로이드 스튜디오의 리액트 네이티브 응용 프로그램에서 android 프로젝트를 열어야 합니다. 리액트 네이티브 앱에서 안드로이드 프로젝트를 찾을 수 있습니다.

안드로이드 스튜디오를 사용해서 기본 코드를 작성하는 것이 좋습니다. 안드로이드 스튜디오는 안드로이드 개발을 위해 만들어진 IDE이며, 이를 사용하면 코드 구문 오류와 같은 사소한 문제를 신속하게 해결할 수 있습니다.
또한 자바/코틀린 코드에서 반복할 때 Gradle Daemon을 활성화해서 빌드 속도를 높이면 좋습니다.

Create A Custom Native Module File

첫 번째 단계는 Android\app\src\main\java\com\projectName\ 폴더 안에 CalendarModule.java 또는 CalendarModule.kt 파일을 만드는 것입니다.
자바파일이던 코틀린파일이던 폴더는 동일하고, 이러한 파일은 네이티브 모듈 자바/코틀린 클래스를 포함합니다.

package com.nativemoduletest;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.util.Map;
import java.util.HashMap;

public class CalendarModule extends ReactContextBaseJavaModule {
   CalendarModule(ReactApplicationContext context) {
       super(context);
   }
}

위 자바코드를 보면 알 수 있듯이, CalendarModule 클래스는 ReactContextBaseJavaModule 클래스를 확장합니다. 안드로이드의 경우 자바/코틀린 네이티브 모듈은 ReactContextBaseJavaModule을 확장하고 자바스크립트에 필요한 기능을 구현하는 클래스로 작성됩니다.

ReactContextBaseJavaModule 받고, 자바스크립트에 필요한 기능 추가!

기술적으로 자바/코틀린 클래스는 BaseJavaModule 클래스를 확장하거나 NativeModule 인터페이스를 구현하면, 리액트 네이티브에 의해 네이티브 모듈로 간주됩니다.

하지만 위와같이 ReactContextBaseJavaModule을 사용하는 것이 좋습니다.
왜냐하면 ReactContextBaseJavaModule은 LifeCycle Method에 연결해야 하는 NativeModule에 유용한 ReactApplicationContext RAC을 활용할 수 있게 해줍니다.
또한 ReactContextBaseJavaModule을 사용하면 나중에 기본 모듈을 안전하게 만들 수 있습니다. 이후에 릴리즈에서 출시될 네이티브 모듈 유형 안전을 위해 리액트 네이티브는 각 네이티브모듈의 자바스크립트의 사양을 살펴보고 ReactContextBaseJavaModule을 확장하는 추상적인 기본 클래스를 생성합니다.

Module Name

안드로이드의 모든 자바/코틀린 네이티브모듈은 getName() 메서드를 구현해야 합니다.
이 메서드는 네이티브 모듈의 이름을 나타내는 문자열을 반환합니다.
네이티브 모듈은 그 이름을 사용해서 자바스크립트로 접근이 가능합니다.

예를 들어 아래 자바 코드를 보면 getName()은 문자열 'CalendarModule'을 반환합니다.

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

그리고 네이티브모듈은 다음과 같이 자바스크립트에서 접근할 수 있습니다.

const { CalendarModule } = ReactNative.NativeModules;

Export a Native Method to JavaScript

그런 다음 기본 모듈에 캘린더 이벤트를 만들고 자바스크립트에서 호출할 수 있는 메서드를 추가해야 합니다. 자바스크립트에서 호출될 모든 네이티브모듈 메서드는 반드시 @ReactMethod 주석을 달아야 합니다.

CalendarModule.createCalendarEvent() 를 통해 자바스크리브에서 호출할 수 있는 CalendarModule에 대한 메서드 createCalendarEvent()를 설정합니다. 일단 이 메서드는 이름과 위치를 문자열로 사용합니다.

@ReactMethod
public void createCalendarEvent(String name, String location) {
}

메서드에 디버그 log를 추가해서 프로그램에서 호출될대 디버그 로그가 호출되는지 확인합니다.
아래는 Android util 패키지에서 log클래스를 가져오고 사용하는 방법의 예시입니다.

import android.util.Log;

@ReactMethod
public void createCalendarEvent(String name, String location) {
   Log.d("CalendarModule", "Create event called with name: " + name
   + " and location: " + location);
}

위와같이 네이티브 모듈을 구현하고 자바스크립트로 연결하면 앱에서 로그를 볼 수 있습니다.

Synchronous Methods

isBlockingSynchronousMethod = true 를 네이티브 메서드에 전달해서 동기 메서드로 표시할 수 있습니다.

@ReactMethod(isBlockingSynchronousMethod = true)

동기식 호출방식은 자바스크립트 가상머신 JS VM이 앱과 메모리를 공유해야 하기 때문에 성능 저하가 심하고 스레드 관련 버그가 네이티브 모듈에 발생할 수 있기 때문에 현재로서는 권장하지 않습니다.
또한 리액트 네이티브는 구글 크롬의 자바스크립트 가상 머신 안에서 실행되며 웹 소켓을 통해 모바일 기기와 비동기식으로 통신하기 때문에 isBlockingSynchronousMethod를 사용하도록 설정하면 앱에서 더이상 Google Chrome 디버거를 사용할 수 없습니다.

Register the Module (Android Specific): 모듈등록

네이티브 모듈이 작성되면 리액트 네이티브에 등록해야 하는데 이를 위해 기본 모듈을 리액트 패키지에 추가하고 리액트 패키지를 리액트 네이티브에 등록해야 합니다.
초기화 중에 리액트 네이티브가 모든 패키지에 반복해서 돌고(루프하고) 각 리액트 패키지에 대해 각각의 네이티브 모듈을 등록합니다.

리액트 네이티브는 등록할 네이티브 모듈 목록을 가져오기 위해 리액트 패키지에 createNativeModules() 메서드를 호출합니다. 안드로이드의 경우 모듈이 인스턴스화되어 createNativeModules에서 반환되지 않으면 자바스크립트에서 사용할 수 없습니다.
네이티브모듈의 클래스로부터 리액트 네이티브에서 실제로 사용할 객체인 인스턴스를 생성할 때 createNativeModukles() 메서드에서 반환되지 않으면 자바스크립트에서 사용할 수 없다는 뜻이다

네이티브모듈을 리액트패키지에 추가하려면 먼저 android/app/src/main/java/com/your-app-name 경로에 MyAppPackage이라는 이름의 새로운 자바 클래스를 만들고 아래 내용으로 작성합니다.

package com.your-app-name; // replace your-app-name with your app’s name
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MyAppPackage implements ReactPackage {

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

   @Override
   public List<NativeModule> createNativeModules(
           ReactApplicationContext reactContext) {
       List<NativeModule> modules = new ArrayList<>();

       modules.add(new CalendarModule(reactContext));

       return modules;
   }

}

이 파일은 사용자가 만든 기본 모듈인 CalendarModule을 가져옵니다. 그런 다음 createNativeModules() 메서드 안에서 CalendarModule을 인스턴스화하고 등록할 NativeModules 목록으로 반환합니다.
더 많은 네이티브 모듈을 추가하는 경우, 모듈을 인스턴스화하고 여기에 반환하는 목록에 추가할 수 있습니다.

네이티브 모듈을 등록하는 이러한 방식은 애플리케이션이 시작될때 모든 네이티브모듈을 열심히 초기화해서 애플리케이션의 시작시간을 더하는대신 TurboReactPackage를 사용할 수 있습니다.
인스턴스화된 네이티브모듈 객체의 목록을 반환하는 crateNativeModules 대신, TurboReactPackage는 필요할 때 네이티브모듈 객체를 생성하는 getModule(String name, ReactApplicationContext rac) 메서드를 구현합니다.
TurboReactPackage는 현재 구현하기 좀 복잡합니다. getModule() 메서드를 구현하는 것 이외에도 getReactModuleInfoProvider() 메서드를 구현해야 합니다.
getReactModuleInfoProvider() 메서드는 패키지가 인스턴스화할 수 있는 모든 네이티브 모듈의 목록과, 이를 인스턴스화하는 함수를 반환합니다.

즉 TurboReactPackage를 사용하면 응용 프로그램이 더 빠른 시작 시간을 가질 수 있지만, 현재 쓰기는 다소 까다롭습니다. 그래서 TurboReactPackages를 사용하기로 선택한 경우 주의해서 진행해야 합니다.

CalendarModule 패키지를 등록하려면 ReactNativeHost의 getPackages() 메서드에서 반환되는 패키지 목록에서 MyAppPackage를 추가해야 합니다.
android/app/src/main/java/com/your-app-name/ 경로에서 찾을 수있는 MainApplication.java 또는 MainApplication.kt 파일을 엽니다.

ReactNativeHost의 getPackages() 메서드를 찾아서 getPackages()메서드가 return하는 패키지 목록에 내가 만든 패키지를 추가합니다.

@Override
  protected List<ReactPackage> getPackages() {
    @SuppressWarnings("UnnecessaryLocalVariable")
    List<ReactPackage> packages = new PackageList(this).getPackages();
    // below MyAppPackage is added to the list of packages returned
    packages.add(new MyAppPackage());
    return packages;
  }

이렇게 안드로이드용 기본 모듈을 등록했습니다.

Test What You Have Built

이 시점에서 안드로이드에서 기본 모듈을 위한 기본적인 뼈대를 설정했는데, 네이티브모듈에 엑세스하고 내보낸 메서드를 자바스크립트로 호출해서 테스트합니다.

프로그램에서 네이티브 모듈의 createCalendarEvent() 메서드의 호출을 추가할 위치를 찾습니다.
아래는 앱에 추가할 수 있는 NewModuleButton이라는 구상의 예시입니다. NewModuleButton의 onPress() 기능에서 기본 모듈을 호출할 수 있습니다.

자바스크립트에서 네이티브 모듈에 엑세스하기 위해서는 먼저 react-native에서 NativeModules을 import해와야 합니다.

import React from 'react';
import { NativeModules, Button } from 'react-native';

const NewModuleButton = () => {
  const onPress = () => {
    console.log('We will invoke the native module here!');
  };

  return (
    <Button
      title="Click to invoke your native module!"
      color="#841584"
      onPress={onPress}
    />
  );
};

export default NewModuleButton;

그런다음 NativeModules에서 Calendar Module 기본모듈에 엑세스할 수 있습니다.

const { CalendarModule } = NativeModules;

이제 CalendarModule 기본 모듈을 사용할 수 있게 되었으므로 기본메서드 createCalendarEvent() 메서드를 호출이 가능합니다.

const onPress = () => {
  CalendarModule.createCalendarEvent('testName', 'testLocation');
};

마지막단계는 새로 만든 네이티브 모듈을 포함한 최신 네이티브 코드를 사용할 수 있도록 네이티브 애플리케이션을 다시 빌드하면 됩니다. 리액트 네이티브 프로젝트가 있는 위치의 터미널에서 아래와 같은 명령을 실행합니다.

$ npx react-native run-android

Building as You Iterate: 구축할때마다 할 것

이러한 가이드를 읽고 네이티브모듈에 대해 반복하면서 자바스크립트에서 최근에 변경한 내용을 활용하려면 애플리케이션을 다시 빌드해야 합니다. 이는 사용자가 작성하고 있는 코드가 애플리케이션의 네이티브 영역에 있기 때문입니다.
리액트 네이티브의 메트로 번들러는 자바스크립트의 변경 사항을 감시하고 즉시 재구축할 수 있지만 네이티브 코드에 대해서는 할 수 없습니다. 따라서 네이티브의 최신 변경사항을 테스트하려면 npx react-native run-android 명령어를 사용해서 다시 빌드해야 합니다.

Recap: 요약

이제 NewModuleButton을 눌러서 네이티브 모듈에서의 crateCalendarEvent() 메서드를 호출할 수 있습니다. 다음단계에 따라 앱에서 ADB 로그들을 볼 수 있는데 그런 다음 Log.d메시지를 검색하고 네이티브모듈 메시지를 호출할 때마다 메시지가 기록되는걸 볼 수 있습니다.

ADB 로그캣이란?

디버깅을 할 때 로그를 읽어와서 원인을 분석해야 하는게 기본적인데, ADB는 logcat명령어를 이용해서 기기 전체의 로그 모니터링 및 Error, Fatal 등의 특정이슈에 대해서 확인이 가능합니다.


이제 안드로이드 네이티브 모듈을 만들고 네이티브 메서드를 자바스크립트에서 네이티브 메서드를 호출했습니다.

다음에는 네이티브 모듈 메서드에서 사용할 수 있는 arguments 유형, 콜백 및 프로미스를 어떻게 설정하는지 알아보겠습니다.

profile
새로운 것을 도전하고 노력한다

0개의 댓글