React Native 라이브러리 이해하기 - 3. 라이브러리 모듈을 이해하자 (⭐️ 최종 ⭐️)

jiveloper·2025년 2월 1일
0
post-thumbnail

오와... 지난 포스팅에서 라이브러리 구조에 대해 이해해보았는데요, 너무 어려웠습니다.
작성하다가 🥲 살짝 눈물이 고일 것 같아... 포스팅을 나눠 작성하기로 하였습니다!!

이번 포스팅에서는 React Native 앱 구동 이후, 모듈이 등록되는 과정 및 모듈과 앱 간 상호작용에 대해 이해해보겠습니다! +_+

(이번 포스팅은 이전 포스팅의 코드를 기반으로 작성되었습니다. 코드 복습도 되고 일석이조? 😎)



모듈 등록 과정 이해하기

먼저 라이브러리 모듈을 React Native 애플리케이션에 등록하는 과정에 대해 이해해봅시다.

1. 애플리케이션 구동

React Native 애플리케이션이 시작되면, JavaScript 엔진이 초기화되고, JavaScript 코드가 실행됩니다.
(이 부분은 추후에 React Native New Architecture 포스팅에서 다루겠습니다.)


2. 네이티브 모듈 정의

React Native에서 필요한 모듈을 정의합니다.

Android

TestLibraryModule 파일에서 TestLibraryModule 클래스가 정의됩니다.

@ReactModule(name = TestLibraryModule.NAME)
class TestLibraryModule(reactContext: ReactApplicationContext) :
  NativeTestLibrarySpec(reactContext) {

  ...
 
}

iOS

TestLibrary.h 파일에서 TestLibrary 클래스의 인터페이스가 정의됩니다. TestLibrary.mm 파일에서 TestLibrary 클래스의 구현이 정의됩니다.

@interface TestLibrary : NSObject <NativeTestLibrarySpec>

3. 모듈 등록

아래 과정을 통해 React Native는 TestLibrary 모듈을 인식하고, JavaScript 코드에서 사용할 수 있게 됩니다.

Android

TestLibraryPackage 파일에서 TestLibraryModule이 React Native에 등록됩니다.

class TestLibraryPackage : BaseReactPackage() {
  ...

  override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
    return ReactModuleInfoProvider {
      val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
      moduleInfos[TestLibraryModule.NAME] = ReactModuleInfo(
        TestLibraryModule.NAME,
        TestLibraryModule.NAME,
        false,  // canOverrideExistingModule
        false,  // needsEagerInit
        true,  // hasConstants
        false,  // isCxxModule
        true // isTurboModule
      )
      moduleInfos
    }
  }
}

iOS

TestLibrary.mm 파일에서 RCT_EXPORT_MODULE() 매크로를 사용하여 TestLibrary 모듈이 React Native에 등록됩니다.

RCT_EXPORT_MODULE()

4. JavaScript 코드에서 호출할 메서드 구현

JavaScript 코드에서 호출할 메서드를 구현합니다.

Android

TestLibraryModule 파일에서 구현합니다.

override fun multiply(a: Double, b: Double): Double {
	return a * b
}

iOS

TestLibrary.mm 파일에서 구현합니다.

- (NSNumber *)multiply:(double)a b:(double)b {
    NSNumber *result = @(a * b);

    return result;
}

5. TurboModule 초기화 및 반환

라이브러리 프로젝트를 생성할 때 Turbo 모듈 옵션을 선택했다면, 네이티브 코드에서 TurboModule을 초기화 합니다. TurboModuleJavaScript와 네이티브 코드 간의 상호 작용을 효율적으로 처리합니다.

Android

TestLibraryPackage 파일에서 TurboModule 정보를 제공하여 초기화하고 반환합니다.

 override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
    return ReactModuleInfoProvider {
      val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
      moduleInfos[TestLibraryModule.NAME] = ReactModuleInfo(
		...,
        true // isTurboModule (TurboModule 여부)
      )
      moduleInfos
    }
  }

iOS

TestLibrary.mm 파일에서 getTurboModule 메서드가 구현되어 TurboModule을 초기화하고 반환합니다.

- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
    (const facebook::react::ObjCTurboModule::InitParams &)params
{
    return std::make_shared<facebook::react::NativeTestLibrarySpecJSI>(params);
}



모듈과 앱 간 상호작용 이해하기

자자... 모듈 등록 과정은 이해했으니,
이제 React Native 애플리케이션에서 모듈을 호출했을 때 모듈과 앱이 어떻게 상호작용 하는지 알아봅시다!!! 🤩 🤩

(React Native 라이브러리 이해하는 시리즈 Wls 최종 단계!)


앱에서 두 수를 입력 받아 multiply 버튼을 클릭하면 두 수를 곱한 결과값이 나오는 화면이 있습니다.

import { multiply } from 'react-native-test-library';

export default function App() {
  const [a, setA] = useState('0');
  const [b, setB] = useState('0');
  const [result, setResult] = useState(0);

  const handleMultiply = (a: string, b: string) => {
    const result = multiply(Number(a), Number(b));
    setResult(result);
  };

  return (
    <View>
      <TextInput value={a} onChangeText={setA} />
      <TextInput value={b} onChangeText={setB} />
      <TouchableOpacity onPress={() => handleMultiply(a, b)}>
        <Text>multiply</Text>
      </TouchableOpacity>
      <Text>결과값: {result}</Text>
    </View>
  );
}

위 multiply 버튼을 클릭하면 handleMultiply 메서드가 호출되어, react-native-test-library 라이브러리의 multiply 메서드가 호출됩니다.
multiply 메서드가 호출되면 앱 <-> 모듈 간의 상호작용이 어떻게 될까요~?

가장 먼저, 라이브러리 모듈의 진입점부터 타겠죠?

index

import TestLibrary from './NativeTestLibrary';

export function multiply(a: number, b: number): number {
  return TestLibrary.multiply(a, b);
}

위 코드가 진입점 코드고, NativeTestLibrary에서 multiply 메서드에서 나온 결과 값을 리턴하는 것을 볼 수 있습니다.

NativeTestLibrary

import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
  multiply(a: number, b: number): number;
}

export default TurboModuleRegistry.getEnforcing<Spec>('TestLibrary');

이 곳에서는 TurboModuleRegistry에서 'TestLibrary'라는 이름의 TurboModule을 가져오고, 이를 Spec 타입으로 강제합니다. 이 모듈을 기본 내보내기로 설정합니다.

위 설명에서도 배웠지만, 이 모듈의 각 OS별 코드는 4번에 정의되어있습니다!
하지만...! 복습할 겸 한번 더 언급해보겠습니다.


Android

// TestLibraryModule
  override fun multiply(a: Double, b: Double): Double {
    return a * b
  }

iOS

// TestLibrary.mm
- (NSNumber *)multiply:(double)a b:(double)b {
    NSNumber *result = @(a * b);

    return result;
}

결과

결국 위 메서드들까지 타고 나서 나온 결과값을 아까 라이브러리 진입점에서 return 했으므로,

const handleMultiply = (a: string, b: string) => {
	const result = multiply(Number(a), Number(b));
    setResult(result);
};

위 코드에서는 각 OS에서 던져준 a * b 값이 리턴 될 것입니다!

결과 😎: react-native-test-library에서 준 리턴값을 setResult에 넣어 result 값이 세팅 됩니다.



마무리

와...!!! 드디어 긴 대장정을 마쳤습니다아아아아... 😭 😭 (짧았나? 아무튼 길었음 😤)

요즘 위젯 라이브러리를 만들면서 기초 없이 만드는 기분이 들었습니다. 그래서 라이브러리의 흐름에 대한 이해가 먼저 필요할 것 같아 이 시리즈를 작성하며 정리하게 되었습니다.

또한, 그동안 라이브러리 통신 및 네이티브 코드에 대한 이해가 적었었는데, 이번 기회에 크게 배웠습니다. 역시 남에게 가르칠 때 가장 잘 배우는 것 같습니다.

위젯 라이브러리도 얼른 되는대로 포스팅 해야겠습니다...!!!!

profile
👩🏻‍💻 크로스플랫폼 앱 개발자입니다.

0개의 댓글

관련 채용 정보