<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>근둥이의 블로그</title>
    <link>https://codevkr.tistory.com/</link>
    <description>새로운 것을 좋아하는 프론트엔드 개발자 ✨</description>
    <language>ko</language>
    <pubDate>Mon, 13 Jul 2026 17:51:32 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>이근둥</managingEditor>
    <image>
      <title>근둥이의 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/2774648/attach/e6b0f2ba593343319e911b6483e5ff19</url>
      <link>https://codevkr.tistory.com</link>
    </image>
    <item>
      <title>React Native Under The Hood - TurboModule</title>
      <link>https://codevkr.tistory.com/138</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdv3Nb/btsP8MdrHkx/sBClXxzPovKk3KQjnds9Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdv3Nb/btsP8MdrHkx/sBClXxzPovKk3KQjnds9Jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdv3Nb/btsP8MdrHkx/sBClXxzPovKk3KQjnds9Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcdv3Nb%2FbtsP8MdrHkx%2FsBClXxzPovKk3KQjnds9Jk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;720&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 터보 모듈(TurboModule)에 대해 살펴보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://reactnative.dev/docs/turbo-native-modules-introduction&quot;&gt;공식 문서&lt;/a&gt;에서 소개하는 것처럼, TurboModule은 터보 네이티브 모듈이라는 용어로 표기하려고 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TurboModule: 터보 네이티브 모듈&lt;/li&gt;
&lt;li&gt;NativeModules: 레거시 네이티브 모듈&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 이번 글에서는 Bridgeless 모드를 기준으로 설명하며, 터보 네이티브 모듈의 하위 호환(Interop)에 대한 내용은 다루지 않으니 참고 바란다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;TurboModule 이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://reactnative.dev/blog/2022/03/30/version-068#opting-in-to-the-new-architecture&quot;&gt;React Native 0.68&lt;/a&gt; 버전에서 새로운 Fabric과 함께 추가된 New Architecture의 네이티브 모듈 구현체이다. 공식 문서에서는 터보 네이티브 모듈(Turbo Native Module)이라고 소개하고 있으며, 레거시 아키텍처(Bridge)에서 사용하던 비동기 메시지 큐 기반 대신, JSI(JavaScript Interface)를 통해 C++ 레이어에서 직접 상호작용하는 메커니즘으로 동작한다. 안드로이드는 플랫폼 특성상 JNI(Java Native Interface) 레이어를 추가로 거쳐 상호작용 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터보 네이티브 모듈 뿐만 아니라 코드젠(Codegen) 기능이 함께 추가되어, 보다 안정적인 타입 안정성을 제공하고, 복잡한 네이티브 모듈 스캐폴딩 과정을 간소화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터보 네이티브 모듈의 경우 공식 문서에 잘 설명되어있어서 쉽게 만들어볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript 혹은 Flow 환경에서 &lt;code&gt;TurboModule&lt;/code&gt; 인터페이스를 확장하여 원하는 모듈의 사양(Spec)을 정의하면 기본 준비는 끝난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3168&quot; data-origin-height=&quot;1600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2hMpB/btsP8CBY0sM/oHrKLy4o7WbOJTbwXGQOPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2hMpB/btsP8CBY0sM/oHrKLy4o7WbOJTbwXGQOPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2hMpB/btsP8CBY0sM/oHrKLy4o7WbOJTbwXGQOPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2hMpB%2FbtsP8CBY0sM%2FoHrKLy4o7WbOJTbwXGQOPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;364&quot; data-origin-width=&quot;3168&quot; data-origin-height=&quot;1600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 정의한 모듈 사양은 Android의 경우 Gradle 빌드 과정에서, iOS의 경우 Pod 설치 시점에 Codegen에 의해 필요한 네이티브 구현체들이 자동으로 생성된다. 우리는 생성된 네이티브 코드 인터페이스에 맞게 기능을 구현하기만 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터보 네이티브 모듈은 &lt;code&gt;TurboModuleRegistry&lt;/code&gt;의 &lt;code&gt;get&lt;/code&gt;(모듈이 없을 경우 &lt;code&gt;null&lt;/code&gt; 반환) 혹은 &lt;code&gt;getEnforcing&lt;/code&gt;(모듈이 없을 경우 Throw) 메서드를 통해 가져올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 구현체를 코드 한 줄만으로 가져오는 것이 신기하지 않은가? &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/TurboModule/TurboModuleRegistry.js&quot;&gt;TurboModuleRegistry.js&lt;/a&gt; 파일을 살펴보면, 어떻게 동작하는지 엿볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AAEPR9W.png&quot; data-origin-width=&quot;3495&quot; data-origin-height=&quot;4088&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/89OjA/btsP691wg5w/gF5tdN4VmTsYrcX1PkwmU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/89OjA/btsP691wg5w/gF5tdN4VmTsYrcX1PkwmU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/89OjA/btsP691wg5w/gF5tdN4VmTsYrcX1PkwmU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F89OjA%2FbtsP691wg5w%2FgF5tdN4VmTsYrcX1PkwmU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;842&quot; data-filename=&quot;AAEPR9W.png&quot; data-origin-width=&quot;3495&quot; data-origin-height=&quot;4088&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현체는 생각보다 간단한데, &lt;code&gt;global.__turboModuleProxy&lt;/code&gt; 혹은 &lt;code&gt;NativeModules&lt;/code&gt; 객체에서 모듈을 참조해보고, 모듈이 존재하지 않을 경우 &lt;code&gt;get&lt;/code&gt;, &lt;code&gt;getEnforcing&lt;/code&gt; 함수 구현에 따라 각각 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 &lt;code&gt;NativeModules&lt;/code&gt;는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/BatchedBridge/NativeModules.js#L184&quot;&gt;global.nativeModuleProxy&lt;/a&gt;이다. 이에 대한 자세한 내용은 차차 살펴볼 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미리 언급하자면, 터보 네이티브 모듈은 &lt;code&gt;NativeModules&lt;/code&gt;(&lt;code&gt;global.nativeModuleProxy&lt;/code&gt;)를 통해 참조하고, &lt;code&gt;Bridgeless&lt;/code&gt;(New Architecture 기본) 모드가 활성화되어있는 경우 터모 네이티브 모듈 호환(Interop) 플래그도 함께 활성화된다. 이는 기존 레거시 네이티브 모듈과의 호환성을 위함이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 본 주제로 돌아와서, 터보 네이티브 모듈은 결국 전역에 노출된 &lt;code&gt;nativeModuleProxy&lt;/code&gt; 객체를 통해 참조한다는 것을 알 수 있다. Codegen 된 네이티브 구현체들이 어떤 과정을 거쳐 JavaScript 런타임 환경에 객체로 노출되는지 하나씩 살펴보려고 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;TurboModule&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Android, iOS 터보 네이티브 모듈을 살펴보기에 앞서, 기반이 되는 &lt;code&gt;TurboModule&lt;/code&gt; 구현체를 간단히 살펴보려고 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp&quot;&gt;TurboModule.cpp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.h&quot;&gt;TurboModule.h&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;TL2Mguu.png&quot; data-origin-width=&quot;3580&quot; data-origin-height=&quot;4184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VsjK8/btsP77Ws65u/kxkmWFhUbs4E6V7qZweHWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VsjK8/btsP77Ws65u/kxkmWFhUbs4E6V7qZweHWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VsjK8/btsP77Ws65u/kxkmWFhUbs4E6V7qZweHWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVsjK8%2FbtsP77Ws65u%2FkxkmWFhUbs4E6V7qZweHWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;841&quot; data-filename=&quot;TL2Mguu.png&quot; data-origin-width=&quot;3580&quot; data-origin-height=&quot;4184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;TurboModule&lt;/code&gt;은 JSI를 통해 JavaScript 런타임과 상호작용 할 수 있도록 &lt;code&gt;jsi::HostObject&lt;/code&gt; 클래스를 상속하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 레거시 네이티브 모듈의 기반이 되던 비동기 메시지 큐(Message Queue) 없이, 즉시 런타임과 상호작용 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 구조적인 특성 덕분에 과거 브릿지(Bridge) 시절의 레거시 네이티브 모듈보다 월등히 좋은 퍼포먼스를 자랑한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현체는 다소 간단하다. 주요한 부분만 간단히 짚고 넘어가자면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;name_&lt;/code&gt;: 네이티브 모듈의 이름&lt;/li&gt;
&lt;li&gt;&lt;code&gt;methodMap_&lt;/code&gt;: 네이티브 모듈에 구현된 메서드 Map 컬렉션&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eventEmitterMap_&lt;/code&gt;: 네이티브 모듈에 구현된 Event Emitter Map 컬렉션&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jsInvoker_&lt;/code&gt;: &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/callinvoker/ReactCommon/CallInvoker.h&quot;&gt;CallInvoker&lt;/a&gt; 구현체의 공유 참조 포인터&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jsRepresentation_&lt;/code&gt;: JavaScript에 노출될 객체(&lt;code&gt;natievModuleProxy&lt;/code&gt;)의 단일 참조 포인터&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;jsInvoker_&lt;/code&gt;는 &lt;code&gt;CallbackInvoker&lt;/code&gt; 구현체의 참조 포인터로, JavaScript 스레드에서 Native -&amp;gt; JavaScript 방향으로 함수를 호출하는 것을 보장해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;jsRepresentation_&lt;/code&gt;은 JavaScript 런타임에서 접근하는 네이티브 모듈의 실체(객체)이며, 모듈 속성에 대한 캐싱을 지원하기 위해 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;methodMap_&lt;/code&gt;은 이름에서 유추할 수 있듯이, 네이티브 모듈에 구현된 함수들이 포함된다. &lt;code&gt;MethodMetadata&lt;/code&gt; 구조체 타입을 값으로 들고 있으며, 이는 메서드가 받을 수 있는 인자의 수, 실제로 호출하게 될 함수 포인터로 구성되어 있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;methodMap_&lt;/code&gt; 그리고 &lt;code&gt;eventEmitterMap_&lt;/code&gt;은 지난 글에서 살펴본 코드젠(Codegen)과 밀접하게 연관되어 있다. 앞서 살펴보았던 Baisc 모듈 사양을 기준으로 조금 더 살펴보도록 하자.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
  numericMethod(arg: number): number;
  booleanMethod(arg: boolean): boolean;
  stringMethod(arg: string): string;
}

export default TurboModuleRegistry.getEnforcing&amp;lt;Spec&amp;gt;('Basic');&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;JavaTurboModule (Android)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Android 환경에서는 다음과 같은 코드들이 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;android/app/build/generated/source/codegen/jni/BasicSpec-generated.cpp&lt;/code&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre class=&quot;cpp&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;#include &quot;BasicSpec.h&quot;

namespace facebook::react {

static facebook::jsi::Value __hostFunction_NativeBasicSpecJSI_numericMethod(facebook::jsi::Runtime&amp;amp; rt, TurboModule &amp;amp;turboModule, const facebook::jsi::Value* args, size_t count) {
  static jmethodID cachedMethodId = nullptr;
  return static_cast&amp;lt;JavaTurboModule &amp;amp;&amp;gt;(turboModule).invokeJavaMethod(rt, NumberKind, &quot;numericMethod&quot;, &quot;(D)D&quot;, args, count, cachedMethodId);
}

static facebook::jsi::Value __hostFunction_NativeBasicSpecJSI_booleanMethod(facebook::jsi::Runtime&amp;amp; rt, TurboModule &amp;amp;turboModule, const facebook::jsi::Value* args, size_t count) {
  static jmethodID cachedMethodId = nullptr;
  return static_cast&amp;lt;JavaTurboModule &amp;amp;&amp;gt;(turboModule).invokeJavaMethod(rt, BooleanKind, &quot;booleanMethod&quot;, &quot;(Z)Z&quot;, args, count, cachedMethodId);
}

static facebook::jsi::Value __hostFunction_NativeBasicSpecJSI_stringMethod(facebook::jsi::Runtime&amp;amp; rt, TurboModule &amp;amp;turboModule, const facebook::jsi::Value* args, size_t count) {
  static jmethodID cachedMethodId = nullptr;
  return static_cast&amp;lt;JavaTurboModule &amp;amp;&amp;gt;(turboModule).invokeJavaMethod(rt, StringKind, &quot;stringMethod&quot;, &quot;(Ljava/lang/String;)Ljava/lang/String;&quot;, args, count, cachedMethodId);
}

NativeBasicSpecJSI::NativeBasicSpecJSI(const JavaTurboModule::InitParams &amp;amp;params)
  : JavaTurboModule(params) {
  methodMap_[&quot;numericMethod&quot;] = MethodMetadata {1, __hostFunction_NativeBasicSpecJSI_numericMethod};
  methodMap_[&quot;booleanMethod&quot;] = MethodMetadata {1, __hostFunction_NativeBasicSpecJSI_booleanMethod};
  methodMap_[&quot;stringMethod&quot;] = MethodMetadata {1, __hostFunction_NativeBasicSpecJSI_stringMethod};
}

std::shared_ptr&amp;lt;TurboModule&amp;gt; BasicSpec_ModuleProvider(const std::string &amp;amp;moduleName, const JavaTurboModule::InitParams &amp;amp;params) {
  if (moduleName == &quot;Basic&quot;) {
    return std::make_shared&amp;lt;NativeBasicSpecJSI&amp;gt;(params);
  }
  return nullptr;
}

} // namespace facebook::react&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;android/build/generated/source/codegen/jni/BasicSpec.h&lt;/code&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre class=&quot;cpp&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;#pragma once

#include &amp;lt;ReactCommon/JavaTurboModule.h&amp;gt;
#include &amp;lt;ReactCommon/TurboModule.h&amp;gt;
#include &amp;lt;jsi/jsi.h&amp;gt;

namespace facebook::react {

/**
 * JNI C++ class for module 'NativeBasic'
 */
class JSI_EXPORT NativeBasicSpecJSI : public JavaTurboModule {
public:
  NativeBasicSpecJSI(const JavaTurboModule::InitParams &amp;amp;params);
};


JSI_EXPORT
std::shared_ptr&amp;lt;TurboModule&amp;gt; BasicSpec_ModuleProvider(const std::string &amp;amp;moduleName, const JavaTurboModule::InitParams &amp;amp;params);

} // namespace facebook::react&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드 터보 네이티브 모듈은 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/platform/android/ReactCommon/JavaTurboModule.h#L31-L66&quot;&gt;JavaTurboModule&lt;/a&gt;을 상속한 &lt;code&gt;NativeBasicSpecJSI&lt;/code&gt; 클래스가 정의된 것을 볼 수 있다. 여기서 상속하는 &lt;code&gt;JavaTurboModule&lt;/code&gt;은 내부적으로 &lt;code&gt;TurboModule&lt;/code&gt;을 상속하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2671&quot; data-origin-height=&quot;1064&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uQwjr/btsP9aSIoGr/wBIpruOLd9TyFOikSqpyx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uQwjr/btsP9aSIoGr/wBIpruOLd9TyFOikSqpyx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uQwjr/btsP9aSIoGr/wBIpruOLd9TyFOikSqpyx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuQwjr%2FbtsP9aSIoGr%2FwBIpruOLd9TyFOikSqpyx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;287&quot; data-origin-width=&quot;2671&quot; data-origin-height=&quot;1064&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 코드를 조금 더 자세히 들여다보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4284&quot; data-origin-height=&quot;1316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czHLuz/btsQbx6VNox/mMP8yUNb1ee2XpiQd4Nf70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czHLuz/btsQbx6VNox/mMP8yUNb1ee2XpiQd4Nf70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czHLuz/btsQbx6VNox/mMP8yUNb1ee2XpiQd4Nf70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczHLuz%2FbtsQbx6VNox%2FmMP8yUNb1ee2XpiQd4Nf70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;221&quot; data-origin-width=&quot;4284&quot; data-origin-height=&quot;1316&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;code&gt;methodMap_&lt;/code&gt;에 정의한 메서드 이름 과 &lt;code&gt;MethodMetadata&lt;/code&gt; 를 Key-Value 쌍으로 할당하는 모습을 볼 수 있다. 이 부분이 바로 앞서 살펴본 &lt;code&gt;TurboModule&lt;/code&gt;의 인터페이스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;code&gt;JavaTurboModule&lt;/code&gt;은 Java(JNI) 환경에서 터보 네이티브 모듈을 구현하기 위한 &lt;code&gt;TurboModule&lt;/code&gt;의 래퍼이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생성된 C++ 코드들은 React Native 런타임(JSI)과 Android 플랫폼(JNI) 사이에서 서로 상호작용할 수 있도록 중간 다리 역할을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 네이티브 모듈 기능을 구현하는 것은 우리의 몫인데, 코드젠을 통해 정해진 메서드들을 구현할 수 있도록 모듈 사양을 포함하는 추상 클래스(Abstract class)가 생성된 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;code&gt;android/build/ganerated/source/codegen/java/&amp;lt;package&amp;gt;/NativeBasicSpec.java&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4176&quot; data-origin-height=&quot;2828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kFtNn/btsQbvnJ2fa/HLPFipBKs2OjA8PkrBrtIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kFtNn/btsQbvnJ2fa/HLPFipBKs2OjA8PkrBrtIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kFtNn/btsQbvnJ2fa/HLPFipBKs2OjA8PkrBrtIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkFtNn%2FbtsQbvnJ2fa%2FHLPFipBKs2OjA8PkrBrtIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;488&quot; data-origin-width=&quot;4176&quot; data-origin-height=&quot;2828&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 모듈 구현체 코드를 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2778&quot; data-origin-height=&quot;2744&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dt9N0E/btsP79tiBvs/5lXpgYv5OMfczLFSlvOIM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dt9N0E/btsP79tiBvs/5lXpgYv5OMfczLFSlvOIM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dt9N0E/btsP79tiBvs/5lXpgYv5OMfczLFSlvOIM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdt9N0E%2FbtsP79tiBvs%2F5lXpgYv5OMfczLFSlvOIM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;711&quot; data-origin-width=&quot;2778&quot; data-origin-height=&quot;2744&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;미리 만들어진 추상 클래스를 상속하도록 함으로써, 타입 불일치, 메서드 구현 누락 등의 문제를 사전에 미리 방지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생성된 터보 네이티브 모듈의 구현체는 Gradle 빌드 과정에서 수행되는 Auto Linking 시점에 일괄적으로 등록된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 터보 네이티브 모듈(Kotlin) 구현체는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GeneratePackageListTask.kt&quot;&gt;GeneratePackageListTask.kt&lt;/a&gt; 태스크를 통해 &lt;code&gt;PackageList.getPackages&lt;/code&gt; 메서드 반환 값에 자동으로 추가된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;android/app/build/generated/autolinking/src/main/java/com/facebook/react/PackageList.java&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2671&quot; data-origin-height=&quot;1736&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3lKsC/btsP75xCg4a/RqnkcwKKZ29dH9O0LRKOgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3lKsC/btsP75xCg4a/RqnkcwKKZ29dH9O0LRKOgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3lKsC/btsP75xCg4a/RqnkcwKKZ29dH9O0LRKOgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3lKsC%2FbtsP75xCg4a%2FRqnkcwKKZ29dH9O0LRKOgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;468&quot; data-origin-width=&quot;2671&quot; data-origin-height=&quot;1736&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들어진 &lt;code&gt;PackageList&lt;/code&gt;는, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/helloworld/android/app/src/main/java/com/helloworld/MainApplication.kt#L24-L28&quot;&gt;MainApplication&lt;/a&gt;에서 참조한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4140&quot; data-origin-height=&quot;2072&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkp1Us/btsP8wBTRcm/REkEfpnXcZnBdpf1h8RFG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkp1Us/btsP8wBTRcm/REkEfpnXcZnBdpf1h8RFG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkp1Us/btsP8wBTRcm/REkEfpnXcZnBdpf1h8RFG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbkp1Us%2FbtsP8wBTRcm%2FREkEfpnXcZnBdpf1h8RFG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;360&quot; data-origin-width=&quot;4140&quot; data-origin-height=&quot;2072&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;code&gt;getPackages&lt;/code&gt; 반환 값에 패키지를 수동으로 추가하여 모듈을 등록할 수도 있는데, 이것이 바로  Manual Linking(Non-Auto)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 &lt;code&gt;reactHost&lt;/code&gt;가 오버라이드 된 것을 볼 수 있는데, Bridgeless 모드에서는 &lt;code&gt;ReactNativeHost&lt;/code&gt;를 그냥 사용하지 않고 &lt;code&gt;ReactHost&lt;/code&gt;로 변환하여 인스턴스를 생성하기에 이를 위한 멤버가 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 언급하는 이유는 등록된 &lt;code&gt;PackageList&lt;/code&gt;를 통해 터보 네이티브 모듈에 접근하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Getter를 통해 ReactHost에 접근하는 경우 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt#L285-L296&quot;&gt;DefaultReactHost.getDefaultReactHost&lt;/a&gt;를 통해 &lt;code&gt;ReactHost&lt;/code&gt;인스턴스를 생성한다. 여기에서 &lt;code&gt;MainApplication&lt;/code&gt;에 정의된 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/helloworld/android/app/src/main/java/com/helloworld/MainApplication.kt#L24-L28&quot;&gt;DefaultReactNativeHost.getPackages()&lt;/a&gt;를 통해 등록된 모듈들을 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ReactHost 생성 시점을 확인해보면, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt#L128&quot;&gt;DefaultTurboModuleManagerDelegate.Builder&lt;/a&gt;를 &lt;code&gt;TurboModuleManagerDelegate&lt;/code&gt; 빌더로 사용하고, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt#L130-L138&quot;&gt;DefaultReactHostDelegate&lt;/a&gt;를 &lt;code&gt;ReactHostDelegate&lt;/code&gt;로 사용하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이들은 추후 &lt;code&gt;ReactInstance&lt;/code&gt;가 생성될 때, 참조되며 가져온 패키지 목록을 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.kt#L162-L166&quot;&gt;TurboModuleManagerDelegate(DefaultTurboModuleManagerDelegate)에 전달&lt;/a&gt;하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4033&quot; data-origin-height=&quot;1484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dGKNul/btsP97g3UAk/7kQkgnJT261vtzwcZxriVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dGKNul/btsP97g3UAk/7kQkgnJT261vtzwcZxriVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dGKNul/btsP97g3UAk/7kQkgnJT261vtzwcZxriVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdGKNul%2FbtsP97g3UAk%2F7kQkgnJT261vtzwcZxriVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;265&quot; data-origin-width=&quot;4033&quot; data-origin-height=&quot;1484&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;DefaultTurboModuleManagerDelegate&lt;/code&gt;는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.java&quot;&gt;ReactPackageTurboModuleManagerDelegate&lt;/a&gt;를 상속하고 있으며, 실제 기능은 부모 클래스에 구현되어있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4176&quot; data-origin-height=&quot;4844&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dSt8Y4/btsP98tvfZ9/VWkr4LfJiRomkmWKbr4gtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dSt8Y4/btsP98tvfZ9/VWkr4LfJiRomkmWKbr4gtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dSt8Y4/btsP98tvfZ9/VWkr4LfJiRomkmWKbr4gtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdSt8Y4%2FbtsP98tvfZ9%2FVWkr4LfJiRomkmWKbr4gtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;835&quot; data-origin-width=&quot;4176&quot; data-origin-height=&quot;4844&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;구현체를 간단히 살펴보면 패키지 목록을 순회하며, 각 패키지별로 모듈 제공자(Provider) 람다 함수를 만들어 할당하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 살펴볼 내용이지만, &lt;code&gt;getModule&lt;/code&gt;, &lt;code&gt;getLegacyModule&lt;/code&gt;을 통해 모듈을 가져오게 되는데, 이는 &lt;code&gt;package.getModule&lt;/code&gt;을 의미한다. 패키지에서 제공하는 모듈은 우리가 구현하는 Java(Kotlin) 모듈로, &lt;code&gt;TurboModule&lt;/code&gt; 클래스를 상속하는 모듈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 Java(Kotlin)단에서 어떻게 등록되는지 간단히 살펴보았는데, 터보 네이티브 모듈은 C++ 구현체이다. 이 부분은 어떻게 처리되는지 이어서 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일하게 Gradle 빌드 시점에 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateAutolinkingNewArchitecturesFileTask.kt&quot;&gt;GenerateAutolinkingNewArchitecturesFileTask.kt&lt;/a&gt; 태스크를 통해 C++ 빌드 구성(CMake)과 Auto Linking될 터보 네이티브 모듈 코드들이 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 프로젝트 루트 기준으로 아래 경로를 찾아가보면, CMake 파일이 하나 생성된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;android/app/build/generated/autolinking/src/main/jni/Android-autolinking.cmake&lt;/code&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre class=&quot;stylus&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
set(REACTNATIVE_MERGED_SO true)

add_subdirectory(&quot;/path/to/android/build/generated/source/codegen/jni/&quot; BasicSpec_autolinked_build)

set(AUTOLINKED_LIBRARIES
  react_codegen_BasicSpec
)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용을 살펴보면, 사용하고 있는 터보 네이티브 모듈이 빌드 대상에 포함되는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 디렉터리에 있는 &lt;code&gt;autolinking.cpp&lt;/code&gt; 파일을 확인해보면, 앞서 살펴본 Basic 모듈의 C++ Provider가 통합되어있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;5610&quot; data-origin-height=&quot;2828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o7iSf/btsP98mJNU8/GHZO8K34mL7udcdTruRkIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o7iSf/btsP98mJNU8/GHZO8K34mL7udcdTruRkIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o7iSf/btsP98mJNU8/GHZO8K34mL7udcdTruRkIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo7iSf%2FbtsP98mJNU8%2FGHZO8K34mL7udcdTruRkIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;363&quot; data-origin-width=&quot;5610&quot; data-origin-height=&quot;2828&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 코어에서는 &lt;code&gt;autolinking_cxxModuleProvider&lt;/code&gt;, &lt;code&gt;autolinking_ModuleProvider&lt;/code&gt;를 통해 Auto Linking 된 터보 네이티브 모듈에 접근하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 구현체를 살펴보면, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp#L106-L109&quot;&gt;JNI를 통해 모듈을 참조&lt;/a&gt;할 수 있도록 구현되어있고, 로드 될 때 &lt;code&gt;DefaultTurboModuleManagerDelegate&lt;/code&gt;의 모듈 제공자(Provider)로, &lt;code&gt;facebook::react::cxxModuleProvider&lt;/code&gt;, &lt;code&gt;facebook::react::javaModuleProvider&lt;/code&gt;의 함수 포인터를 할당하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3423&quot; data-origin-height=&quot;1736&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CG7My/btsP87BEbIA/te1wZdIBijE7MigWsLg1X0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CG7My/btsP87BEbIA/te1wZdIBijE7MigWsLg1X0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CG7My/btsP87BEbIA/te1wZdIBijE7MigWsLg1X0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCG7My%2FbtsP87BEbIA%2Fte1wZdIBijE7MigWsLg1X0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;365&quot; data-origin-width=&quot;3423&quot; data-origin-height=&quot;1736&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;code&gt;DefaultTurboModuleManagerDelegate&lt;/code&gt; 는 이후 알아볼 &lt;code&gt;TurboModuleManagerDelegate&lt;/code&gt;의 기본 구현체로, 여기에 정의된 &lt;code&gt;getTurboModule&lt;/code&gt; 메서드를 통해 터보 네이티브 모듈에 접근하게 된다는 점만 알아두자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;400.png&quot; data-origin-width=&quot;3531&quot; data-origin-height=&quot;3164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5AAqN/btsP9L6ctt4/pbqKf38M462KiS7fNI2PGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5AAqN/btsP9L6ctt4/pbqKf38M462KiS7fNI2PGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5AAqN/btsP9L6ctt4/pbqKf38M462KiS7fNI2PGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5AAqN%2FbtsP9L6ctt4%2FpbqKf38M462KiS7fNI2PGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;645&quot; data-filename=&quot;400.png&quot; data-origin-width=&quot;3531&quot; data-origin-height=&quot;3164&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/cmake-utils/default-app-setup/CMakeLists.txt&quot;&gt;CMakeLists.txt&lt;/a&gt;를 확인해보면, &lt;code&gt;appmodules&lt;/code&gt; 프로젝트에서 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/cmake-utils/ReactNative-application.cmake&quot;&gt;ReactNative-application.cmake&lt;/a&gt; 파일을 참조하는데, &lt;code&gt;default-app-setup/*.cpp&lt;/code&gt; 과 Auto Linking 과정에서 생성된 cpp 파일들을 빌드 대상으로 포함시키도록 구성되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 빌드되는 C++ 구현체는 각 아키텍처에 맞는 Shared Library(.so) 파일로 생성되고, 이는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultSoLoader.kt&quot;&gt;DefaultSoLoader&lt;/a&gt;를 통해 로드된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 터보 네이티브 모듈 구현체는 Java(Kotlin) 기반인데, C++ 구현체와 상호작용 하는 부분은 아직 보이지 않는 것이 정상이다. 이 부분에 대해서는 이후 알아볼 예정인데, 내부적으로는 JNI를 통해 Java(Kotlin) &amp;lt;-&amp;gt; C++간 상호작용 한다는 것만 알고 있자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 살펴본 과정을 정리해보면 Codegen 과정을 통해 생성된 코드, 그리고 Auto Linking 을 통해 생성된 코드들은 공유 라이브러리 형태로 빌드되고, React Native 인스턴스가 생성되고 초기화되는 시점에 SoLoader를 통해 로드된다. 로드된 라이브러리는 JNI를 통해 Android 플랫폼 환경과 상호작용할 수 있도록 초기화 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ObjCTurboModule (iOS)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iOS 환경에서 생성되는 네이티브 코드는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ios/build/generated/ios/BasicSpec/BasicSpec-generated.mm&lt;/code&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre class=&quot;arduino&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;#import &quot;BasicSpec.h&quot;

@implementation NativeBasicSpecBase

- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper
{
  _eventEmitterCallback = std::move(eventEmitterCallbackWrapper-&amp;gt;_eventEmitterCallback);
}
@end

namespace facebook::react {

  static facebook::jsi::Value __hostFunction_NativeBasicSpecJSI_numericMethod(facebook::jsi::Runtime&amp;amp; rt, TurboModule &amp;amp;turboModule, const facebook::jsi::Value* args, size_t count) {
    return static_cast&amp;lt;ObjCTurboModule&amp;amp;&amp;gt;(turboModule).invokeObjCMethod(rt, NumberKind, &quot;numericMethod&quot;, @selector(numericMethod:), args, count);
  }

  static facebook::jsi::Value __hostFunction_NativeBasicSpecJSI_booleanMethod(facebook::jsi::Runtime&amp;amp; rt, TurboModule &amp;amp;turboModule, const facebook::jsi::Value* args, size_t count) {
    return static_cast&amp;lt;ObjCTurboModule&amp;amp;&amp;gt;(turboModule).invokeObjCMethod(rt, BooleanKind, &quot;booleanMethod&quot;, @selector(booleanMethod:), args, count);
  }

  static facebook::jsi::Value __hostFunction_NativeBasicSpecJSI_stringMethod(facebook::jsi::Runtime&amp;amp; rt, TurboModule &amp;amp;turboModule, const facebook::jsi::Value* args, size_t count) {
    return static_cast&amp;lt;ObjCTurboModule&amp;amp;&amp;gt;(turboModule).invokeObjCMethod(rt, StringKind, &quot;stringMethod&quot;, @selector(stringMethod:), args, count);
  }

  NativeBasicSpecJSI::NativeBasicSpecJSI(const ObjCTurboModule::InitParams &amp;amp;params)
    : ObjCTurboModule(params) {
    methodMap_[&quot;numericMethod&quot;] = MethodMetadata {1, __hostFunction_NativeBasicSpecJSI_numericMethod};
    methodMap_[&quot;booleanMethod&quot;] = MethodMetadata {1, __hostFunction_NativeBasicSpecJSI_booleanMethod};
    methodMap_[&quot;stringMethod&quot;] = MethodMetadata {1, __hostFunction_NativeBasicSpecJSI_stringMethod};
  }
} // namespace facebook::react&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&amp;lt;appRoot&amp;gt;/ios/build/generated/ios/BasicSpec/BasicSpec.h&lt;/code&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre class=&quot;objectivec&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;#import &amp;lt;Foundation/Foundation.h&amp;gt;
#import &amp;lt;RCTRequired/RCTRequired.h&amp;gt;
#import &amp;lt;RCTTypeSafety/RCTConvertHelpers.h&amp;gt;
#import &amp;lt;RCTTypeSafety/RCTTypedModuleConstants.h&amp;gt;
#import &amp;lt;React/RCTBridgeModule.h&amp;gt;
#import &amp;lt;React/RCTCxxConvert.h&amp;gt;
#import &amp;lt;React/RCTManagedPointer.h&amp;gt;
#import &amp;lt;ReactCommon/RCTTurboModule.h&amp;gt;
#import &amp;lt;optional&amp;gt;
#import &amp;lt;vector&amp;gt;

NS_ASSUME_NONNULL_BEGIN

@protocol NativeBasicSpec &amp;lt;RCTBridgeModule, RCTTurboModule&amp;gt;

- (NSNumber *)numericMethod:(double)arg;
- (NSNumber *)booleanMethod:(BOOL)arg;
- (NSString *)stringMethod:(NSString *)arg;

@end

@interface NativeBasicSpecBase : NSObject {
@protected
facebook::react::EventEmitterCallback _eventEmitterCallback;
}
- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper;

@end

namespace facebook::react {
  class JSI_EXPORT NativeBasicSpecJSI : public ObjCTurboModule {
  public:
    NativeBasicSpecJSI(const ObjCTurboModule::InitParams &amp;amp;params);
  };
} // namespace facebook::react

NS_ASSUME_NONNULL_END
#endif&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 살펴보면 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h#L50-L169&quot;&gt;ObjCTurboModule&lt;/a&gt;을 상속한 &lt;code&gt;NativeBasicSpecJSI&lt;/code&gt; 클래스가 정의된 것을 볼 수 있다. 여기서 상속하는 &lt;code&gt;ObjCTurboModule&lt;/code&gt;은 내부적으로 &lt;code&gt;TurboModule&lt;/code&gt;을 상속하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2671&quot; data-origin-height=&quot;1064&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wbPlH/btsQbCNW8uB/FZssnQmylUKjDoZTMxvzvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wbPlH/btsQbCNW8uB/FZssnQmylUKjDoZTMxvzvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wbPlH/btsQbCNW8uB/FZssnQmylUKjDoZTMxvzvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwbPlH%2FbtsQbCNW8uB%2FFZssnQmylUKjDoZTMxvzvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;287&quot; data-origin-width=&quot;2671&quot; data-origin-height=&quot;1064&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 코드를 조금 더 자세히 들여다보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4355&quot; data-origin-height=&quot;1652&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A8Qga/btsQajasNcC/aehURKDLyKNL0Urwk65aRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A8Qga/btsQajasNcC/aehURKDLyKNL0Urwk65aRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A8Qga/btsQajasNcC/aehURKDLyKNL0Urwk65aRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA8Qga%2FbtsQajasNcC%2FaehURKDLyKNL0Urwk65aRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;273&quot; data-origin-width=&quot;4355&quot; data-origin-height=&quot;1652&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;안드로이드와 동일하게,&lt;code&gt;methodMap_&lt;/code&gt;에 정의한 메서드 이름 과 &lt;code&gt;MethodMetadata&lt;/code&gt; 를 Key-Value 쌍으로 할당하는 모습을 볼 수 있다. 이 부분이 바로 앞서 살펴본 &lt;code&gt;TurboModule&lt;/code&gt;의 구현체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;code&gt;ObjCTurboModule&lt;/code&gt;은 Objective-C 환경에서 터보 네이티브 모듈을 구현하기 위한 &lt;code&gt;TurboModule&lt;/code&gt;의 래퍼이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생성된 코드를 통해 JSI와 Objective-C가 서로 상호작용 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 네이티브 모듈 기능을 구현하는 것은 우리의 몫인데, 코드젠을 통해 정해진 메서드들을 구현할 수 있도록 모듈 사양을 포함하는 프로토콜(Protocol)이 생성된 것을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2814&quot; data-origin-height=&quot;1400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUpvWm/btsP990iHZ1/JqbexW1HrtNcTRPCUL667K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUpvWm/btsP990iHZ1/JqbexW1HrtNcTRPCUL667K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUpvWm/btsP990iHZ1/JqbexW1HrtNcTRPCUL667K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUpvWm%2FbtsP990iHZ1%2FJqbexW1HrtNcTRPCUL667K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;358&quot; data-origin-width=&quot;2814&quot; data-origin-height=&quot;1400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 터보 네이티브 모듈 구현체 부분을 살펴보면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ArcMWQT.png&quot; data-origin-width=&quot;2312&quot; data-origin-height=&quot;1232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIeRfg/btsQaduCwyy/X0KW7fPpHa0XWJpeBqCom1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIeRfg/btsQaduCwyy/X0KW7fPpHa0XWJpeBqCom1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIeRfg/btsQaduCwyy/X0KW7fPpHa0XWJpeBqCom1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIeRfg%2FbtsQaduCwyy%2FX0KW7fPpHa0XWJpeBqCom1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;384&quot; data-filename=&quot;ArcMWQT.png&quot; data-origin-width=&quot;2312&quot; data-origin-height=&quot;1232&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;미리 만들어진 프로토콜을 구현하도록 함으로써, 타입 불일치, 메서드 구현 누락 등의 문제를 사전에 미리 방지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 구현체 코드를 한 번 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;zzImnfe.png&quot; data-origin-width=&quot;3316&quot; data-origin-height=&quot;2828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pp8vX/btsP912geVF/VgNgZAw7W2PdNjpKbKX1YK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pp8vX/btsP912geVF/VgNgZAw7W2PdNjpKbKX1YK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pp8vX/btsP912geVF/VgNgZAw7W2PdNjpKbKX1YK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpp8vX%2FbtsP912geVF%2FVgNgZAw7W2PdNjpKbKX1YK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;614&quot; data-filename=&quot;zzImnfe.png&quot; data-origin-width=&quot;3316&quot; data-origin-height=&quot;2828&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서도 Codegen을 통해 생성된 &lt;code&gt;BasicSpec.h&lt;/code&gt; 헤더 파일을 통해, 정의된 &lt;code&gt;facebook::react::NativeBasicSpecJSI&lt;/code&gt;을 참조하고 있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 C++ 코드와 Objective-C 구현체 코드는 Codegen을 통해 함께 생성된 podspec 파일을 통해 하나로 묶여 프로젝트에 포함된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ios/build/generated/ios/ReactCodegen.podspec&lt;/code&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre class=&quot;livescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot;&gt;&lt;code&gt;Pod::Spec.new do |s|
  s.name = &quot;ReactCodegen&quot;
  s.version = version
  s.summary = 'Temp pod for generated files for React Native'
  s.homepage = 'https://facebook.com/'
  s.license = 'Unlicense'
  s.authors = 'Facebook'
  s.compiler_flags = &quot;#{folly_compiler_flags} #{boost_compiler_flags} -Wno-nullability-completeness -std=c++20&quot;
  s.source = { :git =&amp;gt; '' }
  s.header_mappings_dir = './'
  s.platforms = min_supported_versions
  s.source_files = &quot;**/*.{h,mm,cpp}&quot;
  s.exclude_files = &quot;RCTAppDependencyProvider.{h,mm}&quot; # these files are generated in the same codegen path but needs to belong to a different pod
  s.pod_target_xcconfig = {
    &quot;HEADER_SEARCH_PATHS&quot; =&amp;gt; header_search_paths.join(' '),
    &quot;FRAMEWORK_SEARCH_PATHS&quot; =&amp;gt; framework_search_paths,
    &quot;OTHER_CPLUSPLUSFLAGS&quot; =&amp;gt; &quot;$(inherited) #{folly_compiler_flags} #{boost_compiler_flags}&quot;
  }

  s.dependency &quot;React-jsiexecutor&quot;
  s.dependency &quot;RCTRequired&quot;
  s.dependency &quot;RCTTypeSafety&quot;
  s.dependency &quot;React-Core&quot;
  s.dependency &quot;React-jsi&quot;
  s.dependency &quot;ReactCommon/turbomodule/bridging&quot;
  s.dependency &quot;ReactCommon/turbomodule/core&quot;
  s.dependency &quot;React-NativeModulesApple&quot;
  s.dependency 'React-graphics'
  s.dependency 'React-rendererdebug'
  s.dependency 'React-Fabric'
  s.dependency 'React-FabricImage'
  s.dependency 'React-debug'
  s.dependency 'React-utils'
  s.dependency 'React-featureflags'
  s.dependency 'React-RCTAppDelegate'

  depend_on_js_engine(s)
  add_rn_third_party_dependencies(s)

  s.script_phases = {
    # ...
  }

end&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 살펴보면, 소스 파일(&lt;code&gt;source_files&lt;/code&gt;)로 Codegen 디렉터리 내의 모든 &lt;code&gt;.cpp&lt;/code&gt;, &lt;code&gt;.mm&lt;/code&gt;, &lt;code&gt;.h&lt;/code&gt; 파일들을 포함하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 부분이 하나 더 있는데, 그것은 바로 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/React/Base/RCTBridgeModule.h#L72-L81&quot;&gt;RCT_EXPORT_MODULE&lt;/a&gt; 매크로이다. 이전 레거시 네이티브 모듈에서도 사용되던 매크로인데, 매크로 구현체를 살펴보면, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/React/Base/RCTBridge.mm#L136-L164&quot;&gt;RCTBridge.mm&lt;/a&gt; 에 구현된 &lt;code&gt;RCTRegisterModule&lt;/code&gt;를 호출하여 등록하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록 과정에서 등록할 클래스가 &lt;code&gt;RCTBridgeModule&lt;/code&gt; 프로토콜을 따르는지 검사하는 과정을 볼 수 있는데, 이 부분이 다소 의아할 수 있다. &lt;code&gt;RCTBridge&lt;/code&gt;는 이름에서 유추할 수 있듯이, 레거시 아키텍처에서 사용되던 브릿지의 역할을 수행하는데, 왜 여기서도 사용되는 것일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애초부터 &lt;code&gt;RCT_EXPORT_MODULE&lt;/code&gt;은 레거시 아키텍처에서부터 존재하던 매크로이다. 레거시 아키텍처와 새로운 아키텍처 환경에서 호환성을 최대한 보장하기 위해 지금과 같은 구조가 된 것인데, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/React/Base/RCTBridge.mm&quot;&gt;RCTBridge.mm&lt;/a&gt; 구현체만 봐도, 레거시 구현체와 새로운 아키텍처 구현체가 공존해 있는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;RCTRegisterModule&lt;/code&gt;은 &lt;code&gt;[RCTModuleClasses addObject]&lt;/code&gt;를 통해 모듈 클래스가 등록되는 것을 볼 수 있다. &lt;code&gt;RCTModuleClasses&lt;/code&gt;는 등록된 모듈 클래스들은 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/React/Base/RCTBridge.mm#L36-L43&quot;&gt;RCTGetModuleClasses&lt;/a&gt;를 통해 참조되는데, 어디에서 이를 요구하는지 좀 더 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 찾다보면, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm&quot;&gt;RCTTurboModuleManager&lt;/a&gt;에서 &lt;code&gt;RCTGetModuleClasses&lt;/code&gt;를 호출하는 것을 확인할 수 있다. 호출부가 두 군데 있지만, 터보 네이티브 모듈과 관련된 부분만 한 번 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출부를 찾아보면 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm#L818-L851&quot;&gt;_getModuleClassFromName&lt;/a&gt; 에서 호출하고 있다. 모듈 이름을 받아, 해당 이름으로 등록된 모듈 클래스를 찾도록 구현되어있다. 그러면 이 메서드는 어디에서 호출하고 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm#L561-L623&quot;&gt;_provideObjCModule&lt;/a&gt;에서 호출하고 있고, 이는 오버로드된 또 다른 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm#L534-L559&quot;&gt;_provideObjCModule&lt;/a&gt;에서 호출하고 있다. 호출부를 추적해보면 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm#L493-L515&quot;&gt;_moduleProviderForName&lt;/a&gt; 에서 호출하고 있으며 이는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm#L303-L414&quot;&gt;provideTurboModule&lt;/a&gt; 메서드가 호출하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4714&quot; data-origin-height=&quot;3920&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ESrXt/btsQaobLhuv/vDFMOn7HgWRMUI6k3nZnr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ESrXt/btsQaobLhuv/vDFMOn7HgWRMUI6k3nZnr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ESrXt/btsQaobLhuv/vDFMOn7HgWRMUI6k3nZnr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FESrXt%2FbtsQaobLhuv%2FvDFMOn7HgWRMUI6k3nZnr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;599&quot; data-origin-width=&quot;4714&quot; data-origin-height=&quot;3920&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;provideTurboModule&lt;/code&gt; 구현체를 살펴보면, &lt;code&gt;_moduleProviderForName&lt;/code&gt;를 통해 &lt;code&gt;RCT_EXPORT_MODULE&lt;/code&gt;에 의해 등록된 모듈 클래스를 가져온 뒤 터보 네이티브 모듈의 &lt;code&gt;getTurboModule&lt;/code&gt;을 호출하여 C++ 기반의 터보 모듈을 인스턴스화 하는 과정을 거친다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 &lt;code&gt;provideTurboModule&lt;/code&gt; 메서드는 터보 네이티브 모듈을 제공하는 역할을 수행하는데, 이는 안드로이드에서 확인했던 &lt;code&gt;cxxModuleProvider&lt;/code&gt;, &lt;code&gt;javaModuleProvider&lt;/code&gt;의 역할과 동일하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 살펴본 내용을 정리하면 Codegen 과정을 통해 생성된 코드, 그리고 Auto Linking 을 통해 생성된 코드들은 하나의 podspec으로 묶여 프로젝트에 포함되어 빌드되고, 안드로이드와 달리 Objective-C 특성상 JNI와 같은 별도 레이어 없이 직접 상호작용한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;TurboModuleManager&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 런타임에서 터보 네이티브 모듈에 어떻게 접근하는지 이해하기 위해서는 등록된 모듈들을 관리하는 &lt;code&gt;TurboModuleManager&lt;/code&gt;에 대해  이해해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;TurboModuleManager&lt;/code&gt;는 &lt;code&gt;ReactInstance&lt;/code&gt;가 초기화 되는 시점에 같이 생성되는데, 애플리케이션이 실행되었을 때 어떤 흐름을 거쳐 &lt;code&gt;TurboModuleManager&lt;/code&gt;가 생성되는지 살펴보자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Android&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;5018&quot; data-origin-height=&quot;1878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLRM4Y/btsQahX351K/NUHiWEnD4QfRWOfuHpQftK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLRM4Y/btsQahX351K/NUHiWEnD4QfRWOfuHpQftK/img.png&quot; data-alt=&quot;다이어그램에는 최소한의 관계만 표기함&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLRM4Y/btsQahX351K/NUHiWEnD4QfRWOfuHpQftK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLRM4Y%2FbtsQahX351K%2FNUHiWEnD4QfRWOfuHpQftK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;269&quot; data-origin-width=&quot;5018&quot; data-origin-height=&quot;1878&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다이어그램에는 최소한의 관계만 표기함&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 하나 하나 살펴보기에는 분량이 많아질 것 같아 정리한 그림으로 대체한다. 주요한 흐름에 맞게 번호를 붙여두었는데 자세히 살펴보고 싶다면 아래에 정리해둔 내용과 소스코드를 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흐름은 &lt;code&gt;MainApplication&lt;/code&gt;에서 시작한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;애플리케이션이 실행되면 &lt;code&gt;MainApplication&lt;/code&gt;의 생명주기 중 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/helloworld/android/app/src/main/java/com/helloworld/MainApplication.kt#L41-L44&quot;&gt;onCreate&lt;/a&gt; 가 호출되고, 이어서 &lt;code&gt;loadReactNative&lt;/code&gt; 메서드가 호출된다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;loadReactNative&lt;/code&gt; 메서드는 코드 상에서 찾아볼 수 없는데, 이는 빌드 시점에 Gradle 플러그인(&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateEntryPointTask.kt&quot;&gt;GenerateEntryPointTask.kt&lt;/a&gt;)을 통해 동적으로 생성되는 메서드이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loadReactNative&lt;/code&gt;에 의해 &lt;a&gt;DefaultNewArchitectureEntryPoint.load&lt;/a&gt; 메서드가 호출되고, React Native의 기본 네이티브 구현체와 앞서 살펴보았던 Auto Linking을 통해 생성된 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultSoLoader.kt#L18&quot;&gt;appmodule&lt;/a&gt; 정적 라이브러리를 로드한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MainActivity&lt;/code&gt;가 생성되어, &lt;code&gt;onCreate&lt;/code&gt; 생명주기가 호출된다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java#L58-L65&quot;&gt;ReactActivity에 onCreate가 오버라이드&lt;/a&gt; 되어있고, &lt;code&gt;ReactActivityDelegate&lt;/code&gt;의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java#L61&quot;&gt;onCreate&lt;/a&gt;를 호출한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;ReactActivity&lt;/code&gt; 인스턴스가 생성될 때, &lt;code&gt;createReactActivityDelegate&lt;/code&gt;를 호출하여 &lt;code&gt;ReactActivityDelegate&lt;/code&gt; 인스턴스를 생성하는데, 이는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/helloworld/android/app/src/main/java/com/helloworld/MainActivity.kt#L27-L28&quot;&gt;MainActivity&lt;/a&gt;에 오버라이드 되어 구현된 메서드이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;onCreate&lt;/code&gt; 내에서 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java#L128-L130&quot;&gt;ReactDelegate 인스턴스를 생성&lt;/a&gt;하고, 이어서 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java#L150&quot;&gt;loadApp 메서드를 호출&lt;/a&gt;한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;ReactDelegate&lt;/code&gt; 인스턴스를 생성할 때, &lt;code&gt;ReactHost&lt;/code&gt; 인스턴스를 넘겨주는데, 이는 &lt;code&gt;MainApplication&lt;/code&gt;에 정의된 &lt;code&gt;reactHost&lt;/code&gt; Getter를 통해 가져온다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ReactDelegate&lt;/code&gt;의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.java#L290-L305&quot;&gt;loadApp&lt;/a&gt; 메서드가 호출되면 Fabric 아키텍처에서 관리하는 화면 단위인 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.java#L293-L296&quot;&gt;Surface를 생성&lt;/a&gt;하고, &lt;code&gt;surface.start()&lt;/code&gt; 메서드를 호출한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java#L424-L433&quot;&gt;createSurface&lt;/a&gt; 내에서는 Surface 구현체(&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactSurfaceImpl.kt&quot;&gt;ReactSurfaceImpl&lt;/a&gt;)와 View(&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactSurfaceView.kt&quot;&gt;ReactSurfaceView&lt;/a&gt;)를 생성하고 &lt;code&gt;ReactHost&lt;/code&gt;를 Attach 하는 과정을 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java#L424-L433&quot;&gt;포함&lt;/a&gt;한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Surface.start&lt;/code&gt; 메서드에서는 &lt;code&gt;ReactHostImpl&lt;/code&gt;의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactSurfaceImpl.kt#L138&quot;&gt;startSurface 메서드를 호출&lt;/a&gt;한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ReactInstance&lt;/code&gt;를 생성하기 위한 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java#L262-L274&quot;&gt;태스크(Task)를 만들어 트리거&lt;/a&gt; 한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;callAfterGetOrCreateReactInstance&lt;/code&gt; -&amp;gt; &lt;code&gt;getOrCreateReactInstance&lt;/code&gt; -&amp;gt; &lt;code&gt;getOrCreateReactInstanceTask&lt;/code&gt; 순서로 진행된다.&lt;/li&gt;
&lt;li&gt;태스크 실행이 성공적으로 완료되는 경우, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java#L1194-L1202&quot;&gt;ReactInstance를 생성&lt;/a&gt;한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ReactInstance&lt;/code&gt; 초기화 시점에 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.kt#L162-L174&quot;&gt;TurboModuleManagerDelegate와 TurboModuleManager 인스턴스를 생성&lt;/a&gt;한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TurboModuleManager&lt;/code&gt;에서는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManager.kt#L63&quot;&gt;installJSIBindings&lt;/a&gt;을 호출한다
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;실제로는 JNI를 통해 바인딩된 C++ 구현체(&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp#L321-L339&quot;&gt;TurboModuleManager::installJSIBindings&lt;/a&gt;)를 호출한다.&lt;/li&gt;
&lt;li&gt;인자로 모듈을 제공하는 Provider들을 넘기는 것을 확인할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/main/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp#L97-L142&quot;&gt;TurboModuleBinding::install&lt;/a&gt;을 통해 터보 네이티브 모듈 프록시를 JavaScript 런타임에 노출시킨다. (&lt;code&gt;global.nativeModuleProxy&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주요한 부분만 추려서 정리해보면, Auto Linking을 통해 생성된 C++의 빌드 결과물인 &lt;code&gt;appmodules&lt;/code&gt; 정적 라이브러리를 로드하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 &lt;code&gt;ReactInstance&lt;/code&gt;가 생성되면서 &lt;code&gt;TurboModuleManager&lt;/code&gt;도 함께 생성되고, &lt;code&gt;TurboModuleBinding&lt;/code&gt;을 통해 JavaScript 런타임에 터보 네이티브 모듈 프록시 객체가 노출된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 자세히 살펴볼 부분은 모듈 제공자(Provider)인데, &lt;code&gt;TurboModuleManager&lt;/code&gt;가 초기화 될 때 다음과 같은 코드를 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;lBRkkaH.png&quot; data-origin-width=&quot;4284&quot; data-origin-height=&quot;3836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eCHkdZ/btsP8IWstiH/jTHNbDrK5Ds8VgATF0xGK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eCHkdZ/btsP8IWstiH/jTHNbDrK5Ds8VgATF0xGK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eCHkdZ/btsP8IWstiH/jTHNbDrK5Ds8VgATF0xGK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeCHkdZ%2FbtsP8IWstiH%2FjTHNbDrK5Ds8VgATF0xGK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;645&quot; data-filename=&quot;lBRkkaH.png&quot; data-origin-width=&quot;4284&quot; data-origin-height=&quot;3836&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현체를 보면 모듈 이름을 받아 위임자(&lt;code&gt;TurboModuleManagerDelegate&lt;/code&gt;)의 &lt;code&gt;getModule&lt;/code&gt;(레거시인 경우 &lt;code&gt;getLegacyModule&lt;/code&gt;) 메서드를 호출하여 반환하는 것이 전부이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 앞서 살펴본 &lt;code&gt;DefaultTurboModuleManagerDelegate&lt;/code&gt; (= &lt;code&gt;ReactPackageTurboModuleManagerDelegate&lt;/code&gt;)의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.java#L143-L168&quot;&gt;getModule&lt;/a&gt;, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.java#L192-L221&quot;&gt;getLegacyModule&lt;/a&gt; 메서드를 호출하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들어진 모듈 제공자는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManager.kt#L220-L302&quot;&gt;getOrCreateModule&lt;/a&gt;에서 참조되고 있으며, 이는 C++ 구현체에서 호출하는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp#L180-L183&quot;&gt;getTurboJavaModule&lt;/a&gt;, &lt;a href=&quot;https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp#L263-L266&quot;&gt;getTurboLegacyCxxModule&lt;/a&gt; 등의 메서드를 통해 &lt;a href=&quot;https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManager.kt#L170-L212&quot;&gt;getModule&lt;/a&gt;을 거쳐 접근한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++ 구현체를 살펴보면, 위 메서드들은 결국 &lt;a href=&quot;https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp#L148-L228&quot;&gt;TurboModuleManager::getTurboModule&lt;/a&gt;을 통해 호출되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;getTurboModule&lt;/code&gt;은 &lt;a href=&quot;https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp#L129-L146&quot;&gt;TurboModuleManager::createTurboModuleProvider&lt;/a&gt;에서 호출하는데, 이를 통해 &lt;a href=&quot;https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.cpp#L333&quot;&gt;TurboModuleBinding::install&lt;/a&gt; 메서드에 전달되는 모듈 제공자가 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;u4GJW60.png&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHA22c/btsP7xgT2WU/k0S8Nkfdlu17pRyHgj4wBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHA22c/btsP7xgT2WU/k0S8Nkfdlu17pRyHgj4wBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHA22c/btsP7xgT2WU/k0S8Nkfdlu17pRyHgj4wBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHA22c%2FbtsP7xgT2WU%2Fk0S8Nkfdlu17pRyHgj4wBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;302&quot; data-filename=&quot;u4GJW60.png&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;670&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript 런타임에서 접근하는 &lt;code&gt;global.nativeModuleProxy&lt;/code&gt;는 결국 &lt;code&gt;TurboModuleManagerDelegate&lt;/code&gt;의 &lt;code&gt;getModule&lt;/code&gt;(레거시인 경우 &lt;code&gt;getLegacyModule&lt;/code&gt;)의 반환값이라는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/turbomodule/core/TurboModuleManagerDelegate.kt&quot;&gt;TurboModuleManagerDelegate.kt&lt;/a&gt; 코드를 보면 비어있는데, 실제 로직은 다음 코드들에 구현되어있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactPackageTurboModuleManagerDelegate.java&quot;&gt;ReactPackageTurboModuleManagerDelegate.java&lt;/a&gt; (상속)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/jni/react/newarchdefaults/DefaultTurboModuleManagerDelegate.cpp&quot;&gt;DefaultTurboModuleManagerDelegate.cpp&lt;/a&gt; (JNI)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 살펴본 내용 중 기억할 부분은 &lt;code&gt;javaModuleProvider&lt;/code&gt;, &lt;code&gt;cxxModuleProvider&lt;/code&gt;를 통해 모듈에 접근하게 되며, 이들은 &lt;code&gt;TurboModuleBinding::install&lt;/code&gt;의 인자로 전달된다는 부분이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;iOS&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;n0DypW6.png&quot; data-origin-width=&quot;3147&quot; data-origin-height=&quot;1400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lqXir/btsQaSKqFZ1/Y2RSJRsZSU3VZK9XuKO2Fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lqXir/btsQaSKqFZ1/Y2RSJRsZSU3VZK9XuKO2Fk/img.png&quot; data-alt=&quot;다이어그램에는 최소한의 관계만 표기함&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lqXir/btsQaSKqFZ1/Y2RSJRsZSU3VZK9XuKO2Fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlqXir%2FbtsQaSKqFZ1%2FY2RSJRsZSU3VZK9XuKO2Fk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;320&quot; data-filename=&quot;n0DypW6.png&quot; data-origin-width=&quot;3147&quot; data-origin-height=&quot;1400&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다이어그램에는 최소한의 관계만 표기함&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iOS 역시 모든 코드를 살펴보지 않고 정리된 그림으로 대체한다. 자세한 내용은 아래에 정리해둔 내용과 소스코드를 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iOS의 경우 많은 로직이 위임자(Delegate) 패턴으로 구현되어있어 순서대로 나열하면 헷갈릴 수 있으니, 인스턴스 생성과 할당과 같은 부분을 먼저 훑어본 뒤 그림에 표기한 순서를 설명하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애플리케이션 시작 지점인 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/helloworld/ios/HelloWorld/AppDelegate.swift&quot;&gt;AppDelegate.swift&lt;/a&gt;를 살펴보면, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/AppDelegate/RCTDefaultReactNativeFactoryDelegate.mm&quot;&gt;RCTDefaultReactNativeFactoryDelegate&lt;/a&gt;를 상속한 &lt;code&gt;ReactNativeDelegate&lt;/code&gt; 클래스를 확인할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 클래스를 인스턴스화 한 뒤 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm#L44-L47&quot;&gt;RCTReactNativeFactory&lt;/a&gt; 생성자에게 전달한다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm#L61&quot;&gt;self.createRCTRootViewFactory&lt;/a&gt;를 호출하는데 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm#L243-L313&quot;&gt;구현체&lt;/a&gt;를 살펴보면, 기본 구성(&lt;code&gt;configuration&lt;/code&gt;)에 위임자(&lt;code&gt;delegate&lt;/code&gt;)의 동작을 할당하는 것을 확인할 수 있다.&lt;/li&gt;
&lt;li&gt;여기서 의미하는 위임자는 앞서 생성한 &lt;code&gt;ReactNativeDelegate&lt;/code&gt;의 인스턴스이다.&lt;/li&gt;
&lt;li&gt;마지막으로 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm&quot;&gt;RCTRootViewFactory&lt;/a&gt;를 생성하며, 위임자로 &lt;code&gt;self&lt;/code&gt; (= &lt;code&gt;RCTReactNativeFactory&lt;/code&gt;)를 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RCTRootViewFactory&lt;/code&gt;를 생성한 뒤에는 스스로를 반환한다. 다시 &lt;code&gt;AppDelegate&lt;/code&gt;로 돌아가서 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/helloworld/ios/HelloWorld/AppDelegate.swift#L26-L37&quot;&gt;구현체&lt;/a&gt;를 살펴보자.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위임자(&lt;code&gt;delegate&lt;/code&gt;)의 &lt;code&gt;dependencyProvider&lt;/code&gt;에 &lt;code&gt;RCTAppDependencyProvider&lt;/code&gt;를 할당한다. 이는 이전 글에서 살펴보았던 것처럼 코드젠에 의해 생성되는 모듈 제공자이다.&lt;/li&gt;
&lt;li&gt;생성한 팩토리의 &lt;code&gt;startReactNative&lt;/code&gt; 메서드를 호출하여 React Native 애플리케이션을 실행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지가 React Native 애플리케이션을 실행되기 위해 기본적으로 준비하는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 순서를 살펴보기에 앞서, 그림에 나타낸 것 처럼 &lt;code&gt;RCTReactNativeFactory&lt;/code&gt;는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h#L28-L50&quot;&gt;RCTHostDelegate&lt;/a&gt; 그리고 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.h#L23-L63&quot;&gt;RCTTurboModuleManagerDelegate&lt;/a&gt;를 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm#L35-L40&quot;&gt;구현&lt;/a&gt;하고 있다는 점을 잘 기억하고 있자. (언급하진 않았지만, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/React/Fabric/Mounting/RCTComponentViewFactory.h#L23-L31&quot;&gt;RCTComponentViewFactoryComponentProvider&lt;/a&gt;, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/AppDelegate/RCTJSRuntimeConfiguratorProtocol.h#L16-L20&quot;&gt;RCTJSRuntimeConfiguratorProtocol&lt;/a&gt; 도 포함된다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;factory.startReactNative&lt;/code&gt; 메서드가 호출되면 &lt;code&gt;RCTReactNativeFactory&lt;/code&gt;의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm#L83-L95&quot;&gt;startReactNativeWithModuleName&lt;/a&gt;메서드가 호출된다.&lt;/li&gt;
&lt;li&gt;앞서 생성해둔 &lt;code&gt;RCTRootViewFactory&lt;/code&gt;의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm#L83-L95&quot;&gt;viewWithModuleName 메서드를 통해 루트 뷰를 생성&lt;/a&gt;한다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm#L151-L180&quot;&gt;viewWithModuleName&lt;/a&gt; 메서드를 살펴보면
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm#L155&quot;&gt;initializeReactHostWithLaunchOptions&lt;/a&gt; 메서드를 호출한다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm#L242-L248&quot;&gt;createReactHostIfNeeded&lt;/a&gt; 메서드를 호출한다.&lt;/li&gt;
&lt;li&gt;초기에는 생성된 &lt;code&gt;self.reactHost&lt;/code&gt;가 없으니 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm#L247&quot;&gt;createReactHost&lt;/a&gt; 메서드를 호출하여 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm#L250-L266&quot;&gt;RCTHost 인스턴스를 생성&lt;/a&gt;한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이때 &lt;code&gt;turboModuleManagerDelegate&lt;/code&gt;를 전달하는데, 이는 &lt;code&gt;RCTReactNativeFactory&lt;/code&gt;이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;생성한 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm&quot;&gt;RCTHost&lt;/a&gt;의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/Libraries/AppDelegate/RCTRootViewFactory.mm#L264&quot;&gt;start 메서드를 호출&lt;/a&gt;한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RCTHost&lt;/code&gt;에서는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm#L210-L251&quot;&gt;RCTInstance 인스턴스를 생성&lt;/a&gt;한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이때 &lt;code&gt;turboModuleManagerDelegate&lt;/code&gt;를 전달하는데, 이는 &lt;code&gt;RCTReactNativeFactory&lt;/code&gt;이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;전달받은 여러 위임자와 구성을 할당한 뒤 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm#L98-L144&quot;&gt;_start&lt;/a&gt; 메서드를 호출한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm#L249-L420&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;구현체&lt;/a&gt;를 살펴보면, JavaScript 런타임 스레드에서 코드를 실행시키도록 도와주는 &lt;code&gt;jsInvoker&lt;/code&gt;와, 레거시 아키텍처에서 사용하던 &lt;code&gt;RCTBridge&lt;/code&gt;의 호환 레이어인 &lt;code&gt;RCTBridgeProxy&lt;/code&gt;를 생성하고 있는 것을 볼 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;`RCTTurboModuleManager`&amp;nbsp;인스턴스도&amp;nbsp;생성하고&amp;nbsp;있는&amp;nbsp;것을&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;이때&amp;nbsp;위임자를&amp;nbsp;전달하는데,&amp;nbsp;&lt;code&gt;self&lt;/code&gt;&amp;nbsp;(=&amp;nbsp;&lt;code&gt;RCTInstance&lt;/code&gt;)이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RCTInstnace&lt;/code&gt;에&amp;nbsp;구현한&amp;nbsp;&lt;code&gt;RCTTurboModuleManagerDelegate&lt;/code&gt;&amp;nbsp;프로토콜&amp;nbsp;구현체를&amp;nbsp;살펴보면,&amp;nbsp;실제로는&amp;nbsp;&lt;code&gt;_appTMMDelegate&lt;/code&gt;의&amp;nbsp;메서드를&amp;nbsp;호출하고&amp;nbsp;있다.&amp;nbsp;이는&amp;nbsp;결국&amp;nbsp;&lt;code&gt;RCTReactNativeFactory&lt;/code&gt;를&amp;nbsp;의미한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;필요한 구성 요소들을 생성한 뒤, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm#L386-L417&quot;&gt;React Native 런타임을 초기화&lt;/a&gt; 한다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;런타임 초기화 직후 &lt;code&gt;TurboModuleManager&lt;/code&gt;의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm#L392&quot;&gt;installJSIBindings 메서드를 호출&lt;/a&gt;한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm#L914-L977&quot;&gt;installJSBindings 메서드 구현체&lt;/a&gt;를 살펴보면, 모듈을 제공하기 람다 함수를 생성하고, 이를 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm#L973-L975&quot;&gt;TurboModuleBinding::install 메서드 인자로 전달&lt;/a&gt;하여 JavaScript 런타임에 &lt;code&gt;global.nativeModuleProxy&lt;/code&gt;를 정의하는 것을 확인할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드와 달리 JNI 같은 레이어 없이 즉시 상호작용 하기 때문에 때문에 상대적으로 구조는 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 자세히 살펴볼 부분은 모듈 제공자(Provider)인데, &lt;code&gt;installJSBindings&lt;/code&gt; 메서드 내에서 다음과 같은 코드를 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;RUlsMM6.png&quot; data-origin-width=&quot;4821&quot; data-origin-height=&quot;2828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3SsHK/btsP8HiTRsu/H1SqeVRNTooDJivINAun5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3SsHK/btsP8HiTRsu/H1SqeVRNTooDJivINAun5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3SsHK/btsP8HiTRsu/H1SqeVRNTooDJivINAun5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3SsHK%2FbtsP8HiTRsu%2FH1SqeVRNTooDJivINAun5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;422&quot; data-filename=&quot;RUlsMM6.png&quot; data-origin-width=&quot;4821&quot; data-origin-height=&quot;2828&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈을 제공하기 위한 람다(Lambda) 함수를 선언하고, 이에 대한 참조를 &lt;code&gt;TurboModuleBinding::install&lt;/code&gt;의 인자로 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다 함수 내에서는 앞서 살펴본 &lt;code&gt;provideTurboModule&lt;/code&gt;를 통해 모듈을 가져오는 것을 확인할 수 있는데, 이는 곧 &lt;code&gt;RCT_EXPORT_MODULE&lt;/code&gt; 매크로를 통해 등록된 모듈 클래스를 가져온 뒤, 터보 네이티브 모듈의 &lt;code&gt;getTurboModule&lt;/code&gt;을 호출하여 C++ 기반의 터보 모듈을 인스턴스화 하는 과정을 거치는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 살펴본 내용 중 기억할 부분은 &lt;code&gt;provideTurboModule&lt;/code&gt;을 통해 모듈에 접근하게 되며, 이들은 &lt;code&gt;TurboModuleBinding::install&lt;/code&gt;의 인자로 전달된다는 부분이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;TurboModuleBinding&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Android, iOS 환경에서 React Native가 초기화 되면서 어떻게 &lt;code&gt;TurboModuleManager&lt;/code&gt;가 구성되는지 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;TurboModuleBinding:install&lt;/code&gt; 호출부까지 살펴보고 끝났는데, 이번 섹션에서 조금 더 자세히 알아보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native에서는 JavaScript 런타임에서 네이티브 모듈에 접근할 수 있도록, React Native 인스턴스가 생성될 때 JSI를 통해 프록시 객체를 런타임에 노출시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp&quot;&gt;TurboModuleBinding&lt;/a&gt;이 '프록시 객체를 런타임에 노출'시키는 역할을 수행한다. 구체적으로 런타임 환경에 모듈을 어떻게 노출 시키는지 한 번 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3372&quot; data-origin-height=&quot;3036&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eMiOcd/btsP8ftBTV5/p7sIC3GvGv4p8mrWaa0irK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eMiOcd/btsP8ftBTV5/p7sIC3GvGv4p8mrWaa0irK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eMiOcd/btsP8ftBTV5/p7sIC3GvGv4p8mrWaa0irK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeMiOcd%2FbtsP8ftBTV5%2Fp7sIC3GvGv4p8mrWaa0irK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;648&quot; data-origin-width=&quot;3372&quot; data-origin-height=&quot;3036&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.h&quot;&gt;클래스 정의&lt;/a&gt;는 다음과 같으며, 정적 메서드 &lt;code&gt;install&lt;/code&gt; 그리고, 네이티브 모듈을 가져오기 위한 &lt;code&gt;getModule&lt;/code&gt; 메서드가 정의되어있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;install&lt;/code&gt; 메서드는 네이티브 모듈들을 JavaScript 런타임 환경에서 접근할 수 있도록 처리하는 역할을 수행하는데, 실제 구현체를 살펴보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3652&quot; data-origin-height=&quot;5264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzjlCB/btsP8bxQB7n/I6LbtzPcl8XhNNLGRKaF90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzjlCB/btsP8bxQB7n/I6LbtzPcl8XhNNLGRKaF90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzjlCB/btsP8bxQB7n/I6LbtzPcl8XhNNLGRKaF90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzjlCB%2FbtsP8bxQB7n%2FI6LbtzPcl8XhNNLGRKaF90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;1038&quot; data-origin-width=&quot;3652&quot; data-origin-height=&quot;5264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현체를 살펴보면, 인자로 JSI 런타임 컨텍스트와 모듈 제공자 두 개를 받는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나는 터보 네이티브 모듈 제공자이며, 또 다른 하나는 레거시 모듈 제공자이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터보 네이티브 모듈 호환성 플래그(Turbo module interop) 상태에 따라 레거시 모듈 제공자는 존재하지 않을 수도 있다. 두 가지 제공자를 모두 받도록 구현되어있는 이유는, 새로운 아키텍쳐에서의 네이티브 모듈 체계가 하위 호환성을 어느정도 보장하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 메서드 구현체를 살펴보면, Bridgeless 모드가 비활성화 되었을 때, 전역 컨텍스트에 &lt;code&gt;__turboModuleProxy&lt;/code&gt; 라는 이름의 함수를 생성하고, 아닌 경우에는 레거시(브릿지) 아키텍쳐와 동일하게 &lt;code&gt;nativeModuleProxy&lt;/code&gt;라는 이름의 프록시 객체(&lt;code&gt;jsi::Object&lt;/code&gt;)를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 Bridgeless 환경(두 번째 케이스)인 경우를 살펴보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현체를 살펴보면, &lt;code&gt;nativeModuleProxy&lt;/code&gt;에 정의되는 객체는&lt;code&gt;std::make_shared&amp;lt;BridgelessNativeModuleProxy&amp;gt;(...)&lt;/code&gt;를 통해 동적으로 &lt;code&gt;BridgelessNativeModuleProxy&lt;/code&gt; 인스턴스를 생성하고, 이에 대한 참조 포인터를 넘긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 &lt;code&gt;TurboModuleBinding.cpp&lt;/code&gt; 코드를 살펴보면, 여기에 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp#L20-L83&quot;&gt;BridgelessNativeModuleProxy 클래스&lt;/a&gt;가 구현되어있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3567&quot; data-origin-height=&quot;5096&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbuD8x/btsP77oDRlT/dLKaSN7gbqXencXhy5m4f0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbuD8x/btsP77oDRlT/dLKaSN7gbqXencXhy5m4f0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbuD8x/btsP77oDRlT/dLKaSN7gbqXencXhy5m4f0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbuD8x%2FbtsP77oDRlT%2FdLKaSN7gbqXencXhy5m4f0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;1029&quot; data-origin-width=&quot;3567&quot; data-origin-height=&quot;5096&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 &lt;code&gt;jsi::HostObject&lt;/code&gt;를 상속하고 있어, JSI를 통해 JavaScript 런타임에 바인딩 될 수 있는 구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, &lt;code&gt;BridgelessNativeModuleProxy&lt;/code&gt;는 앞서 살펴본 것과 같이 두 개의 모듈 제공자(Provider)를 참조를 받고, 이를 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.h#L24-L59&quot;&gt;TurboModuleBinding&lt;/a&gt; 인스턴스로 만드는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 구현체를 살펴보면, &lt;code&gt;jsi::HostObject&lt;/code&gt;의 &lt;code&gt;get&lt;/code&gt; 메서드를 오버라이드 하고 있는데, 이 부분을 통해 JavaScript 객체에서 속성 접근을 시도할 때, 네이티브 모듈을 가져오게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체에 접근하는 프로퍼티 값을 모듈 이름으로 간주하여 &lt;code&gt;TurboModuleBinding&lt;/code&gt;에서 네이티브 모듈을 찾아오는 매커니즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 JavaScript에서 다음과 같이 코드를 작성하는 경우,&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;global.nativeModuleProxy['SomeModule'];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 코어에서는 &lt;code&gt;turboBinding_.getModule(runtime, &quot;SomeModule&quot;)&lt;/code&gt;이 호출되는 셈이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 터모 네이티브 모듈 바인딩에서 모듈을 찾을 수 없다면, 레거시 모듈 바인딩에서 모듈을 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 모듈을 가져오는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp#L151-L199&quot;&gt;TurboModuleBinding::getModule&lt;/a&gt; 을 조금 더 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3580&quot; data-origin-height=&quot;3700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dsm7tk/btsP8NXMH3i/06Ubzv5plkKgBe3akb3NQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dsm7tk/btsP8NXMH3i/06Ubzv5plkKgBe3akb3NQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dsm7tk/btsP8NXMH3i/06Ubzv5plkKgBe3akb3NQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdsm7tk%2FbtsP8NXMH3i%2F06Ubzv5plkKgBe3akb3NQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;744&quot; data-origin-width=&quot;3580&quot; data-origin-height=&quot;3700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;getModule&lt;/code&gt; 메서드는 JSI 컨텍스트에 접근하기 위한 런타임 참조와, 가져올 네이티브 모듈의 이름 두 개의 인자를 받아 &lt;code&gt;jsi::Value&lt;/code&gt; 값을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초반에 &lt;code&gt;TurboModule&lt;/code&gt; 구현체가 &lt;code&gt;jsi::HostObject&lt;/code&gt; 기반이었다는 점을 기억하는가? &lt;code&gt;getModule&lt;/code&gt; 메서드에서 반환되는 값은 &lt;code&gt;TurboModule&lt;/code&gt; 혹은 모듈이 존재하지 않을 때 사용되는 &lt;code&gt;null&lt;/code&gt; 값 (&lt;code&gt;jsi::Value(nullptr)&lt;/code&gt;)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 자세히 이야기 하자면, 안드로이드 환경에서는 &lt;code&gt;JavaTurboModule&lt;/code&gt; 인스턴스가, iOS 환경에서는 &lt;code&gt;ObjCTurboModule&lt;/code&gt; 인스턴스를 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순차적으로 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Step 1&lt;/b&gt;&lt;br /&gt;지정된 네이티브 모듈 제공자로부터 &lt;code&gt;TurboModule&lt;/code&gt; 인스턴스를 가져온다. 만약 모듈이 존재하지 않는 경우 &lt;code&gt;else&lt;/code&gt; 블럭으로 이동하여 &lt;code&gt;jsi::Value(nullptr)&lt;/code&gt; 값을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Step 2&lt;/b&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleWithJSIBindings.h&quot;&gt;TurboModuleWithJSIBindings&lt;/a&gt; 클래스의 &lt;code&gt;installJSIBindings&lt;/code&gt; 메서드를 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;TurboModule&lt;/code&gt;에는 두 가지 유형이 있는데, 첫 번째는 일반적인 &lt;code&gt;TurboModule&lt;/code&gt;이고, 두 번째는 언급한 &lt;code&gt;TurboModuleWithJSIBindings&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘의 차이는 크지 않은데, &lt;code&gt;TurboModuleWithJSIBindings&lt;/code&gt;는 &lt;code&gt;TurboModule&lt;/code&gt;을 상속하는 클래스로, 정적 메서드 &lt;code&gt;installJSIBindings&lt;/code&gt;와, 내부용 &lt;code&gt;installJSIBindingsWithRuntime&lt;/code&gt; 메서드가 포함된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleWithJSIBindings.cpp&quot;&gt;구현체&lt;/a&gt;를 확인하면 알 수 있듯이, 인자로 받은 &lt;code&gt;TurboModule&lt;/code&gt; 인스턴스를 대상으로 다이나믹 캐스팅(Dynamic Cast)을 시도한 뒤 &lt;code&gt;TurboModuleWithJSIBindings&lt;/code&gt; 인터페이스를 충족하는 경우, 모듈 인스턴스의 &lt;code&gt;installJSIBindingsWithRuntime&lt;/code&gt; 메서드를 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 &lt;a href=&quot;https://github.com/mrousavy/nitro/blob/main/packages/react-native-nitro-modules/ios/turbomodule/NativeNitroModules%2BNewArch.mm#L35-L48_&quot;&gt;NitroModule iOS 구현체&lt;/a&gt;가 이를 활용하여 JSI에 C++ 구현체를 직접 주입하고 있다. (안드로이드에는 조금 다른 방식으로 구현했다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Step 3&lt;/b&gt;&lt;br /&gt;&lt;code&gt;_jsRepresentation&lt;/code&gt; 값은 앞서 살펴본 &lt;code&gt;TurboModule&lt;/code&gt; 클래스에 포함된 멤버 변수(포인터 변수)이다. &lt;code&gt;TurboModule&lt;/code&gt; 인스턴스가 처음 생성되면 해당 값은  빈 참조를 갖고 있기에, &lt;code&gt;getModule&lt;/code&gt;이 처음 호출되었을 때에는 조건이 충족되지 않아 다음 코드로 이어지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Step 4&lt;/b&gt;&lt;br /&gt;반대로 이 과정부터는 모듈 최초 접근 시 반드시 거쳐가는 과정이라고 볼 수 있다. 먼저 빈 JavaScript 객체를 생성하고, 이를 &lt;code&gt;weakJsRepresentation&lt;/code&gt;(= &lt;code&gt;_jsRepresentation&lt;/code&gt;)에 단일 참조 포인터로 할당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Step 5&lt;/b&gt;&lt;br /&gt;&lt;code&gt;TurboModule&lt;/code&gt; 인스턴스가 바인딩된 &lt;code&gt;jsi::Object&lt;/code&gt; 인스턴스를 새로 만든다. &lt;code&gt;TurboModule&lt;/code&gt; 클래스는 &lt;code&gt;jsi::HostObject&lt;/code&gt; 클래스를 상속하기에 가능하다는 점은 앞서 살펴본 내용이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 4번 과정에서 생성한 빈 객체의 프로토타입(&lt;code&gt;__proto__&lt;/code&gt;)에 할당하고 해당 객체를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 이러한 과정을 거치는지 궁금할 것이다. 이유는 바로 &lt;code&gt;TurboModule&lt;/code&gt; 속성에 대한 캐싱을 구현하기 위함이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5번 과정까지 거친 뒤 가져온 객체는 JavaScript 런타임에서 확인해보면 빈 객체로 보일 것이다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;const mod = TurboModuleRegistry.getEnforcing('MyModule');
console.log(mod); // {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 속성 접근을 시도할 경우 JavaScript의 프로토타입 체이닝으로 인해 빈 객체인 &lt;code&gt;mod&lt;/code&gt;를 지나 프로토타입(&lt;code&gt;__proto__&lt;/code&gt;) 즉, 앞서 할당했던 &lt;code&gt;hostObject&lt;/code&gt;인 &lt;code&gt;TurboModule&lt;/code&gt;의 실체에 접근할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;// =&amp;gt; __proto__['someMethod']
mod['someMethod'];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지만 두고 보면 이게 캐싱과 무슨 연관이 있을까? 의문이 들 것이다.&lt;br /&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.2/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.h#L58-L69&quot;&gt;TurboModule 구현체&lt;/a&gt;를 보면 정답이 숨겨져있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3404&quot; data-origin-height=&quot;3628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0bt4s/btsQa4X9B5i/bmoesruXLCXKe838Ez7Jck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0bt4s/btsQa4X9B5i/bmoesruXLCXKe838Ez7Jck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0bt4s/btsQa4X9B5i/bmoesruXLCXKe838Ez7Jck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0bt4s%2FbtsQa4X9B5i%2FbmoesruXLCXKe838Ez7Jck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;767&quot; data-origin-width=&quot;3404&quot; data-origin-height=&quot;3628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;TurboModule&lt;/code&gt;은 &lt;code&gt;jsi::HostObject&lt;/code&gt;의 &lt;code&gt;get&lt;/code&gt; 메서드를 오버라이딩하고 있는데, 객체 속성에 접근하는 경우 내부에 구현된 로직을 통해 값을 생성 및 반환하도록 구현되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 속성에 접근하는 경우에는 &lt;code&gt;create&lt;/code&gt; 메서드가 호출되는데, 여기에서는 속성 이름이 &lt;code&gt;_methodMap&lt;/code&gt; 내에 존재하는지, &lt;code&gt;_eventEmitterMap&lt;/code&gt;에 존재하는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드 부분을 살펴보면, 속성 이름에 해당하는 메서드가 존재할 경우 &lt;code&gt;jsi::Function&lt;/code&gt; 타입의 함수 인스턴스를 생성한 뒤 반환한다. 이 함수는 추후 JavaScript 런타임에서 호출하게 되는 네이티브 모듈의 메서드가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 구현체를 보면, 이전에 살펴본 &lt;code&gt;MethodMetadata&lt;/code&gt;의 &lt;code&gt;invoker&lt;/code&gt;를 호출하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 반환된 값은 &lt;code&gt;jsReresentation_&lt;/code&gt; 객체 속성에 추가되며, 이후 동일한 속성으로 접근할 경우에는 값을 새로 만들지 않고 기존에 생성된 &lt;code&gt;jsi::Value&lt;/code&gt; 값을 반환하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 살펴본 내용을 간단히 정리하면 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ntCg8J4.png&quot; data-origin-width=&quot;2488&quot; data-origin-height=&quot;1802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6GcUV/btsP8vXjkYF/vGHXmG74hYzKuohVJDtsbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6GcUV/btsP8vXjkYF/vGHXmG74hYzKuohVJDtsbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6GcUV/btsP8vXjkYF/vGHXmG74hYzKuohVJDtsbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6GcUV%2FbtsP8vXjkYF%2FvGHXmG74hYzKuohVJDtsbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;521&quot; data-filename=&quot;ntCg8J4.png&quot; data-origin-width=&quot;2488&quot; data-origin-height=&quot;1802&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;TurboModuleBinding::install&lt;/code&gt; 메서드가 호출되면 &lt;code&gt;BridgelessNativeModuleProxy&lt;/code&gt; 인스턴스가 생성되고, 이에 대한 레퍼런스가 &lt;code&gt;jsi::HostObject&lt;/code&gt;에 바인딩되어 JavaScript 런타임에 주입된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript에서는 &lt;code&gt;TurboModuleRegistry&lt;/code&gt;를 통해 모듈을 가져올 경우, 주입된 &lt;code&gt;HostObject&lt;/code&gt;(&lt;code&gt;BridgelessNativeModuleProxy&lt;/code&gt;)를 거쳐 모듈 제공자(Provider)로부터 등록된 모듈을 찾아온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 바로 JavaScript 런타임에서 터보 네이티브 모듈을 참조하는 매커니즘이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리하며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 터보 네이티브 모듈이 어떻게 등록되고, JavaScript 런타임에서 참조되어 동작하는지 간단히 살펴보았다. 이제 터보 네이티브 모듈은 기본 사양이 되었고, 이를 기반으로 하는 다양한 프로젝트들도 많이 탄생하고 있다. (대표적으로 &lt;a href=&quot;https://github.com/mrousavy/nitro&quot;&gt;Nitro&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서 다루진 않았지만 플랫폼 종속적인 터보 네이티브 모듈(&lt;code&gt;JavaTurboModule&lt;/code&gt;, &lt;code&gt;ObjCTurboModule&lt;/code&gt;) 뿐만 아니라 C++ 구현체만 사용하여 모듈을 구현할 수도 있다. 물론 터보 네이티브 모듈이 아닌 순수 C++ 모듈을 구현할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자 개인적으로는 내부 구현을 살펴보면서, 새로운 아키텍처 환경에서 시도해볼 수 있는 것들이 정말 많을 것이라는 기대로 가득 차게 되었다. 앞으로는 단순히 퍼포먼스가 뛰어난 C++ 기반 모듈 뿐만 아니라, React Native 환경에서의 &lt;a href=&quot;https://www.callstack.com/blog/announcing-node-api-support-for-react-native&quot;&gt;Node-API&lt;/a&gt;, Rust 통합 등 다양한 기술들이 탄생할 것이라고 믿는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ps 1. 정리하다보니 내용이 너무 많아 세부적으로 다루지 못하고 건너뛴 부분들이 많이 있다. 글은 지속적으로 업데이트 할 예정이나, 보충, 수정이 필요한 부분이 보인다면 댓글에 남겨주길 바란다.&lt;br /&gt;ps 2. Surface와 같이 Fabric과 연관된 내용이 글에서 몇 번 언급이 되었으나, Fabric은 필자의 관심사가 아니기에 후속 글이 작성될지는 모른다&lt;/p&gt;</description>
      <category>언어 &amp;amp; 프레임워크/React Native</category>
      <author>이근둥</author>
      <guid isPermaLink="true">https://codevkr.tistory.com/138</guid>
      <comments>https://codevkr.tistory.com/138#entry138comment</comments>
      <pubDate>Thu, 28 Aug 2025 08:04:48 +0900</pubDate>
    </item>
    <item>
      <title>React Native Under The Hood - Codegen</title>
      <link>https://codevkr.tistory.com/137</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;rnh-codegen-cover.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVbynJ/btsOALAWCZp/wkavTl7rl12CekCg2AJkQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVbynJ/btsOALAWCZp/wkavTl7rl12CekCg2AJkQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVbynJ/btsOALAWCZp/wkavTl7rl12CekCg2AJkQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVbynJ%2FbtsOALAWCZp%2FwkavTl7rl12CekCg2AJkQ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;800&quot; data-filename=&quot;rnh-codegen-cover.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개요&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 새로운 아키텍처에서 함께 탄생한 개발 편의 기능인 코드젠(Codegen)에 대해 살펴보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Codegen 이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://reactnative.dev/docs/the-new-architecture/what-is-codegen&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;React Native 공식 문서&lt;/a&gt;에도 잘 설명이 되어있는데, 사용자가 정의한 인터페이스에 맞게 네이티브 모듈과 뷰(View) 코드를 작성해 주는 도구이다. 과거 브릿지 시절에는 네이티브 모듈 혹은 뷰를 구현해야 하는 경우 JavaScript 환경과 네이티브 간의 통신 과정을 직접 구현해야 했는데, 반복적인 작업이고 무엇보다 타입 불일치로 인해 오류가 발생하는 경우가 종종 생기기도 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codegen은 이러한 과정을 자동화하여 생산성 향상과 타입 안정성을 확보하는 도구로 자리매김하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 package.json 파일에 &lt;code&gt;codegenConfig&lt;/code&gt; 필드를 추가하고, 구성 추가하면 Codegen 준비 과정은 끝이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2008&quot; data-origin-height=&quot;2412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZbCLB/btsOBcdTanT/JXEnKAmMtzgU7FKeVUPcy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZbCLB/btsOBcdTanT/JXEnKAmMtzgU7FKeVUPcy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZbCLB/btsOBcdTanT/JXEnKAmMtzgU7FKeVUPcy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZbCLB%2FbtsOBcdTanT%2FJXEnKAmMtzgU7FKeVUPcy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;480&quot; data-origin-width=&quot;2008&quot; data-origin-height=&quot;2412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://www.typescriptlang.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript&lt;/a&gt; 혹은 &lt;a href=&quot;https://flow.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Flow&lt;/a&gt; 로 구현하고자 하는 모듈의 사양(Spec)을 정의하면, iOS / Android 앱 빌드 시점에 Codegen 도구가 이를 파싱 하여 네이티브 코드를 자동으로 생성해 준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3320&quot; data-origin-height=&quot;1476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgppCY/btsOA9g9Lpa/kscrfxOuFWRebhgNs18c21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgppCY/btsOA9g9Lpa/kscrfxOuFWRebhgNs18c21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgppCY/btsOA9g9Lpa/kscrfxOuFWRebhgNs18c21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgppCY%2FbtsOA9g9Lpa%2FkscrfxOuFWRebhgNs18c21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;320&quot; data-origin-width=&quot;3320&quot; data-origin-height=&quot;1476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 사용자가 작성한 사양이 어떤 과정을 거쳐 네이티브 코드로 변환되는지, 변환된 코드는 React Native 환경에 어떻게 바인딩되는지 전반적으로 살펴보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Codegen 명령어&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codegen은 앱이 빌드되는 시점에 자동으로 실행되도록 구성되어 있지만, React Native CLI를 통해 실행할 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1749980392645&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx react-native codegen&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;632&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZfCNh/btsOBBLb299/fZD1NhnoOVR3vS07qahySk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZfCNh/btsOBBLb299/fZD1NhnoOVR3vS07qahySk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZfCNh/btsOBBLb299/fZD1NhnoOVR3vS07qahySk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZfCNh%2FbtsOBBLb299%2FfZD1NhnoOVR3vS07qahySk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;264&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;632&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;React Native 프로젝트 루트 경로에서 실행하면, 프로젝트 내에 존재하는 네이티브 모듈, 설치되어 있는 의존성 정보를 탐색하여 네이티브 코드를 생성해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codegen&amp;nbsp;명령어는 기본 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/react-native.config.js#L103&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;react-native.config.js&lt;/a&gt; 파일 내에서 등록되고 있고, 명령어의 실제 구현체는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/scripts/codegen/generate-artifacts-executor/index.js&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;generate-artifacts-executor&lt;/a&gt; 이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3320&quot; data-origin-height=&quot;2004&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RP0Ns/btsOCP9itaY/iX926jKXlcwpsjWwJFUs21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RP0Ns/btsOCP9itaY/iX926jKXlcwpsjWwJFUs21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RP0Ns/btsOCP9itaY/iX926jKXlcwpsjWwJFUs21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRP0Ns%2FbtsOCP9itaY%2FiX926jKXlcwpsjWwJFUs21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;435&quot; data-origin-width=&quot;3320&quot; data-origin-height=&quot;2004&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Codegen 과정 살펴보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CLI를 통해 Codegen 작업을 수행하던, 앱을 빌드하던 모두 동일한 로직이 수행된다. 구현체를 하나씩 살펴보면서 구체적으로 어떻게 동작하는지 자세히 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Codegen 대상 라이브러리 탐색&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2944&quot; data-origin-height=&quot;1688&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lRHgv/btsOA9aoq27/6OoHabkRcKhoUsrDzZfFfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lRHgv/btsOA9aoq27/6OoHabkRcKhoUsrDzZfFfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lRHgv/btsOA9aoq27/6OoHabkRcKhoUsrDzZfFfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlRHgv%2FbtsOA9aoq27%2F6OoHabkRcKhoUsrDzZfFfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;413&quot; data-origin-width=&quot;2944&quot; data-origin-height=&quot;1688&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 루트 경로를 기준으로 &lt;code&gt;package.json&lt;/code&gt; 파일과 &lt;code&gt;react-native.config.js&lt;/code&gt; 파일을 로드하는 것을 확인할 수 있다. 이 파일들에 포함된 메타 정보를 기준으로 Codegen이 필요한 라이브러리들을 탐색(findCodegenEnabledLibraries)하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;findCodegenEnabledLibraries&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Code: &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js#L110-L121&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js#L110-L121&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3404&quot; data-origin-height=&quot;2412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qmDzL/btsOA9IdbCp/kdCXwr8UknT3c74ZrM1fSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qmDzL/btsOA9IdbCp/kdCXwr8UknT3c74ZrM1fSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qmDzL/btsOA9IdbCp/kdCXwr8UknT3c74ZrM1fSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqmDzL%2FbtsOA9IdbCp%2FkdCXwr8UknT3c74ZrM1fSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;510&quot; data-origin-width=&quot;3404&quot; data-origin-height=&quot;2412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codegen&amp;nbsp;처리가 필요한 대상 라이브러리를 찾아야 하는데, 첫 번째로는 루트 &lt;code&gt;package.json&lt;/code&gt; 에서 &lt;code&gt;codegenConfig&lt;/code&gt; 이 있는지 탐색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 &lt;code&gt;codegenConfig.includesGeneratedCode&lt;/code&gt; 옵션이 활성화되어있는 경우 Codegen에 의해 생성된 코드들이 포함되어있음을 의미하므로 루트 &lt;code&gt;package.json&lt;/code&gt; 에서 찾은 라이브러리만 Codegen 대상으로 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 활성화되어있지 않은 경우(일반적인 케이스)엔 외부 라이브러리(&lt;code&gt;findExternalLibraries&lt;/code&gt;) 그리고 &lt;code&gt;react-native.config.js&lt;/code&gt; 값을 기준으로 추가로 탐색(&lt;code&gt;findLibrariesFromReactNativeConfig&lt;/code&gt;)하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;findExternalLibaries&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Code: &lt;a href=&quot;https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js#L185-L225&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js#L185-L225&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 라이브러리를 찾는 과정은 다음과 같이 구현되어 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3108&quot; data-origin-height=&quot;2152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u4LX4/btsOA5Z9U9G/SxgUmSGZGOMIJ34kr23Z61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u4LX4/btsOA5Z9U9G/SxgUmSGZGOMIJ34kr23Z61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u4LX4/btsOA5Z9U9G/SxgUmSGZGOMIJ34kr23Z61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu4LX4%2FbtsOA5Z9U9G%2FSxgUmSGZGOMIJ34kr23Z61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;499&quot; data-origin-width=&quot;3108&quot; data-origin-height=&quot;2152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;package.json&lt;/code&gt; 파일의 &lt;code&gt;dependencies&lt;/code&gt;, &lt;code&gt;devDependencies&lt;/code&gt;, &lt;code&gt;peerDependencies&lt;/code&gt;에 명시되어 있는 의존성 정보들의 &lt;code&gt;package.json&lt;/code&gt; 파일을 로드하고, 해당 파일의 &lt;code&gt;codegenConfig&lt;/code&gt; 값을 읽는 구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js#L227-L248&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;extractLibrariesFromJSON&lt;/a&gt; 함수를 통해 &lt;code&gt;codegenConfig&lt;/code&gt; 구성이 존재하는지 확인한 뒤 라이브러리 정보를 담고 있는 객체를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;findLibrariesFromReactNativeConfig&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Code: &lt;a href=&quot;https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js#L152-L183&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js#L152-L183&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 &lt;code&gt;react-native.config.js&lt;/code&gt; 파일에서 Codegen 대상 라이브러리를 찾는 부분이다. 구성에 포함된 &lt;code&gt;dependencies&lt;/code&gt; 값을 기준으로 의존성을 찾는다. 그외 과정은 &lt;code&gt;findExternalLibraries&lt;/code&gt; 함수와 동일하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2836&quot; data-origin-height=&quot;1836&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8dTvE/btsODknK9Cd/BHwO0trthB24DwP1aDo4N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8dTvE/btsODknK9Cd/BHwO0trthB24DwP1aDo4N0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8dTvE/btsODknK9Cd/BHwO0trthB24DwP1aDo4N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8dTvE%2FbtsODknK9Cd%2FBHwO0trthB24DwP1aDo4N0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;466&quot; data-origin-width=&quot;2836&quot; data-origin-height=&quot;1836&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 &lt;code&gt;findCodegenEnabledLibraries&lt;/code&gt; 함수의 반환 값은 다음과 같은 모습이다. Codegen 작업을 처리하기 위한 기본적인 정보라고 보면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3108&quot; data-origin-height=&quot;1524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vHOMJ/btsOBrPow4F/kk4DtoDkiBQZ5TyXJPA540/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vHOMJ/btsOBrPow4F/kk4DtoDkiBQZ5TyXJPA540/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vHOMJ/btsOBrPow4F/kk4DtoDkiBQZ5TyXJPA540/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvHOMJ%2FbtsOBrPow4F%2Fkk4DtoDkiBQZ5TyXJPA540%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;353&quot; data-origin-width=&quot;3108&quot; data-origin-height=&quot;1524&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 과정을 거쳐 루트, 의존성으로 설치한 라이브러리들의 Codegen 정보를 모두 수집한다. 이제 수집된 정보를 기준으로 코드를 생성하는 작업을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;네이티브 코드 생성&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3056&quot; data-origin-height=&quot;3688&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4bFMN/btsOB82QiZZ/bugJj9sluYXlpbaKONl3jK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4bFMN/btsOB82QiZZ/bugJj9sluYXlpbaKONl3jK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4bFMN/btsOB82QiZZ/bugJj9sluYXlpbaKONl3jK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4bFMN%2FbtsOB82QiZZ%2FbugJj9sluYXlpbaKONl3jK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;869&quot; data-origin-width=&quot;3056&quot; data-origin-height=&quot;3688&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플랫폼별로 코드를 생성하기에 앞서, Auto Linking 이 비활성화되어있는 라이브러리를 찾는다. (&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js#L377-L383&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;findDisabledLibrariesByPlatform&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Auto Linking이 비활성화 되어있는 라이브러리인 경우 Codegen 대상에서 제외시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 Codegen을 통해 생성되는 코드의 경로를 만들어오는 것을 볼 수 있다. (&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/scripts/codegen/generate-artifacts-executor/index.js#L182-L202&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;computeOutputPath&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현체를 살펴보면, 기준 경로(&lt;code&gt;baseOutputPath&lt;/code&gt;)가 없는 경우에는 &lt;code&gt;codegenConfig.outputDir&lt;/code&gt;의 경로 혹은 플랫폼별 경로 &lt;code&gt;codegenConfig.outputDir[platform]&lt;/code&gt; 를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외 플랫폼별 기본 경로를 따르는데, 경로는 각각 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Android: &lt;code&gt;&amp;lt;ROOT&amp;gt;/android/app/build/generated/source/codegen&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;iOS: &lt;code&gt;&amp;lt;ROOT&amp;gt;/build/generated/ios&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 생성되어야 할 위치를 찾은 뒤에는 Codegen을 위한 스키마(Schema) 정보를 수집한다. (&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/scripts/codegen/generate-artifacts-executor/generateSchemaInfos.js#L18-L20&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;generateSchemaInfos&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3324&quot; data-origin-height=&quot;3360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AzYIo/btsOAHrMsMO/9Q4izOEKN3kWdbmkLkaPp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AzYIo/btsOAHrMsMO/9Q4izOEKN3kWdbmkLkaPp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AzYIo/btsOAHrMsMO/9Q4izOEKN3kWdbmkLkaPp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAzYIo%2FbtsOAHrMsMO%2F9Q4izOEKN3kWdbmkLkaPp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;728&quot; data-origin-width=&quot;3324&quot; data-origin-height=&quot;3360&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스키마 정보에는 첫 번째로 애플(Apple) 기기의 지원 플랫폼이 포함된다. &lt;code&gt;extractSupportedApplePlatforms&lt;/code&gt; 함수를 통해 라이브러리 내에 존재하는 &lt;code&gt;*.podspec&lt;/code&gt; 파일들을 탐색한다. 파일 내에서 다음과 같은 형식을 찾은 뒤 ios, macos, tvos, visionos에 해당하는 플랫폼만 추출한다.&lt;/p&gt;
&lt;pre id=&quot;code_1749983490199&quot; class=&quot;ruby&quot; data-ke-language=&quot;ruby&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spec.platforms = { :ios =&amp;gt; &quot;11.0&quot;, :tvos =&amp;gt; &quot;11.0&quot; }
# or
s.ios.deployment_target = &quot;11.0&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플 플랫폼 추출 이후에는 &lt;code&gt;CodegenUtils.getCombineJSToSchema().combineSchemasInFileList()&lt;/code&gt;를 호출하여 &lt;code&gt;codegenConfig.jsSrcsDir&lt;/code&gt; 경로 내에 포함된 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native-codegen/src/cli/combine/combine-js-to-schema.js#L51-L71&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;.js, .ts, .tsx 파일&lt;/a&gt;을 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾을 파일들은 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native-codegen/src/cli/combine/combine-js-to-schema.js#L24-L49&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;combineSchemas&lt;/a&gt; 함수로 전달되는데, 파일 내에 다음과 같은 코드가 있는지 확인하고, 조건에 충족하는 파일만 남긴다.&lt;/p&gt;
&lt;pre id=&quot;code_1749983806251&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. `export default codegenNativeComponent`
// 2. `extends TurboModule`

// e.g.
export default codegenNativeComponent(/* ... */);

interface Spec extends TurboModule {
  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 과정을 통해 걸러진 사양(Spec) 파일은 언어에 맞는 파서(Parser)에게 전달되고, 이를 통해 모듈의 스키마 정보를 추출한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native-codegen/src/parsers/typescript/parser.js&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript Parser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native-codegen/src/parsers/flow/parser.js&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Flow Parser&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스키마는 다음과 같은 형태로 구성되어 있다. (&lt;a href=&quot;https://github.com/react-native-webview/react-native-webview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;react-native-webview&lt;/a&gt; 라이브러리의 실제 스키마)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;modules: 정의된 모듈 정보&lt;/li&gt;
&lt;li&gt;type: 모듈의 유형 (NativeModule, Component)&lt;/li&gt;
&lt;li&gt;NativeModule
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;moduleName: 모듈의 이름 (TurboModuleRegistry 에서 가져올 때 참조될 이름)&amp;nbsp;&lt;/li&gt;
&lt;li&gt;aliasMap: Primitive 타입 외의 사용자 정의 타입&lt;/li&gt;
&lt;li&gt;enumMap: Enum 타입&lt;/li&gt;
&lt;li&gt;spec: Event Emitter, Methods 에 대한 사양&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Component
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;components: 컴포넌트 정보 (Properties, Events 등 포함)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749984661898&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;{
  &quot;modules&quot;: {
    &quot;NativeRNCWebViewModule&quot;: {
      &quot;type&quot;: &quot;NativeModule&quot;,
      &quot;aliasMap&quot;: {},
      &quot;enumMap&quot;: {},
      &quot;spec&quot;: {
        &quot;eventEmitters&quot;: [],
        &quot;methods&quot;: [
          {
            &quot;name&quot;: &quot;isFileUploadSupported&quot;,
            &quot;optional&quot;: false,
            &quot;typeAnnotation&quot;: {
              &quot;type&quot;: &quot;FunctionTypeAnnotation&quot;,
              &quot;returnTypeAnnotation&quot;: {
                &quot;type&quot;: &quot;PromiseTypeAnnotation&quot;,
                &quot;elementType&quot;: {
                  &quot;type&quot;: &quot;BooleanTypeAnnotation&quot;
                }
              },
              &quot;params&quot;: []
            }
          },
          // ...
        ]
      },
      &quot;moduleName&quot;: &quot;RNCWebViewModule&quot;
    },
    &quot;RNCWebView&quot;: {
      &quot;type&quot;: &quot;Component&quot;,
      &quot;components&quot;: {
        &quot;RNCWebView&quot;: {
          &quot;extendsProps&quot;: [
            {
              &quot;type&quot;: &quot;ReactNativeBuiltInType&quot;,
              &quot;knownTypeName&quot;: &quot;ReactNativeCoreViewProps&quot;
            }
          ],
          &quot;events&quot;: [
            {
              &quot;name&quot;: &quot;onLoadingProgress&quot;,
              &quot;optional&quot;: false,
              &quot;bubblingType&quot;: &quot;direct&quot;,
              &quot;typeAnnotation&quot;: {
                &quot;type&quot;: &quot;EventTypeAnnotation&quot;,
                &quot;argument&quot;: {
                  &quot;type&quot;: &quot;ObjectTypeAnnotation&quot;,
                  &quot;properties&quot;: [
                    {
                      &quot;name&quot;: &quot;url&quot;,
                      &quot;optional&quot;: false,
                      &quot;typeAnnotation&quot;: {
                        &quot;type&quot;: &quot;StringTypeAnnotation&quot;
                      }
                    },
                    {
                      &quot;name&quot;: &quot;loading&quot;,
                      &quot;optional&quot;: false,
                      &quot;typeAnnotation&quot;: {
                        &quot;type&quot;: &quot;BooleanTypeAnnotation&quot;
                      }
                    },
                    {
                      &quot;name&quot;: &quot;title&quot;,
                      &quot;optional&quot;: false,
                      &quot;typeAnnotation&quot;: {
                        &quot;type&quot;: &quot;StringTypeAnnotation&quot;
                      }
                    },
                    {
                      &quot;name&quot;: &quot;canGoBack&quot;,
                      &quot;optional&quot;: false,
                      &quot;typeAnnotation&quot;: {
                        &quot;type&quot;: &quot;BooleanTypeAnnotation&quot;
                      }
                    },
                    {
                      &quot;name&quot;: &quot;progress&quot;,
                      &quot;optional&quot;: false,
                      &quot;typeAnnotation&quot;: {
                        &quot;type&quot;: &quot;DoubleTypeAnnotation&quot;
                      }
                    },
                    // ...
                  ]
                }
              }
            },
            // ...
          ],
          &quot;props&quot;: [
            {
              &quot;name&quot;: &quot;pullToRefreshEnabled&quot;,
              &quot;optional&quot;: true,
              &quot;typeAnnotation&quot;: {
                &quot;type&quot;: &quot;BooleanTypeAnnotation&quot;,
                &quot;default&quot;: false
              }
            },
            {
              &quot;name&quot;: &quot;scrollEnabled&quot;,
              &quot;optional&quot;: true,
              &quot;typeAnnotation&quot;: {
                &quot;type&quot;: &quot;BooleanTypeAnnotation&quot;,
                &quot;default&quot;: true
              }
            },
            // ...
          ],
          &quot;commands&quot;: [
            {
              &quot;name&quot;: &quot;goBack&quot;,
              &quot;optional&quot;: false,
              &quot;typeAnnotation&quot;: {
                &quot;type&quot;: &quot;FunctionTypeAnnotation&quot;,
                &quot;params&quot;: [],
                &quot;returnTypeAnnotation&quot;: {
                  &quot;type&quot;: &quot;VoidTypeAnnotation&quot;
                }
              }
            },
            {
              &quot;name&quot;: &quot;reload&quot;,
              &quot;optional&quot;: false,
              &quot;typeAnnotation&quot;: {
                &quot;type&quot;: &quot;FunctionTypeAnnotation&quot;,
                &quot;params&quot;: [],
                &quot;returnTypeAnnotation&quot;: {
                  &quot;type&quot;: &quot;VoidTypeAnnotation&quot;
                }
              }
            },
            // ...
          ]
        }
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스키마를 살펴보면 네이티브 메서드의 인자 수, 인자의 타입, 반환 타입 / 컴포넌트의 프로퍼티 이름, 타입, 커맨드(Ref)에 대한 세부 정보가 포함되어있는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈의 스키마를 모두 수집한 뒤에는 스키마 정보를 기반으로 네이티브 코드를 생성한다 (&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/scripts/codegen/generate-artifacts-executor/generateNativeCode.js&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;generateNativeCode&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3592&quot; data-origin-height=&quot;4028&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUD09r/btsOCiqAtwW/jIIeLukcH4ZkF3mKYqaKlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUD09r/btsOCiqAtwW/jIIeLukcH4ZkF3mKYqaKlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUD09r/btsOCiqAtwW/jIIeLukcH4ZkF3mKYqaKlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUD09r%2FbtsOCiqAtwW%2FjIIeLukcH4ZkF3mKYqaKlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;807&quot; data-origin-width=&quot;3592&quot; data-origin-height=&quot;4028&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체적으로 살펴보면, 라이브러리별 임시 디렉터리를 생성하고 해당 경로에 코드를 생성, 마지막에 실제 output 경로로 복사하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 부분은 스키마 정보를 기반으로 코드를 생성하는 부분(&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/scripts/codegen/generate-specs-cli-executor.js#L66-L105&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;generateSpecFromInMemorySchema&lt;/a&gt;)이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4188&quot; data-origin-height=&quot;4256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8K5iI/btsOAVKh2Ky/n8gUIoILIZBTsqSleBXuh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8K5iI/btsOAVKh2Ky/n8gUIoILIZBTsqSleBXuh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8K5iI/btsOAVKh2Ky/n8gUIoILIZBTsqSleBXuh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8K5iI%2FbtsOAVKh2Ky%2Fn8gUIoILIZBTsqSleBXuh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;732&quot; data-origin-width=&quot;4188&quot; data-origin-height=&quot;4256&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/scripts/codegen/codegen-utils.js#L22-L33&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;getCodegen&lt;/a&gt;을 통해 &lt;code&gt;@react-native/codegen&lt;/code&gt; 패키지 혹은 React Native 내에 포함된 Codegen 모듈을 로드하고, Codegen 모듈의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native-codegen/src/generators/RNCodegen.js#L237-L298&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;generate&lt;/a&gt; 메서드를 호출함으로써 네이티브 코드가 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codegen 내에는 여러 가지 제너레이터(Generator)들이 구현되어 있는데, 이를 통해 사전 정의된 템플릿 기준의 코드들이 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GenerateModuleCpp&lt;/a&gt; 모듈에서는 C++ 코드를 생성하도록 구현된 것을 확인해 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로, 마지막 부분에서 플랫폼이 안드로이드인 경우, 생성된 &lt;code&gt;.cpp&lt;/code&gt;, &lt;code&gt;.h&lt;/code&gt; 파일들을 &lt;code&gt;{OUTPUT_DIR}/jni/react/renderer/components/{LIBRARY_NAME}&lt;/code&gt; 경로로 이동시키는 부분도 확인해 볼 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 저런 과정이 많긴 하지만 간단히 요약해보면 다음과 같은 흐름으로 동작하는 것을 알 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;루트 package.json 로드 &amp;amp; codegenConfig 읽기&lt;/li&gt;
&lt;li&gt;의존성 라이브러리의 package.json 로드 &amp;amp; codegenConfig 읽기&lt;/li&gt;
&lt;li&gt;사양(Spec) 파싱하여 스키마(Schema) 정보 생성&lt;/li&gt;
&lt;li&gt;네이티브 코드 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;2404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFuoTW/btsOBgtx6gS/dnhRsyVk1BIq20pkFtnLM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFuoTW/btsOBgtx6gS/dnhRsyVk1BIq20pkFtnLM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFuoTW/btsOBgtx6gS/dnhRsyVk1BIq20pkFtnLM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFuoTW%2FbtsOBgtx6gS%2FdnhRsyVk1BIq20pkFtnLM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;1151&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;2404&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Auto Linking&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 사용자가 정의한 모듈의 사양이 네이티브 코드로 탄생하게 되는 과정에 대해 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 네이티브 코드들은 어떤 모습이며, 이렇게 생성된 코드는 어떻게 React Native 환경에 통합되는지 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codegen을 통해 생성된 네이티브 코드들은 빌드시 거치는 &lt;a href=&quot;https://reactnative.dev/docs/linking-libraries-ios&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Auto Linking&lt;/a&gt; 과정에서 React Native 환경에 통합된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Android&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드 환경에서는 빌드시 Gradle 플러그인(&lt;a href=&quot;https://github.com/facebook/react-native/tree/v0.80.0/packages/gradle-plugin&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@react-native/gradle-plugin&lt;/a&gt;)을 통해 Auto Linking 과정이 진행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 구현체 중 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GeneratePackageListTask.kt&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GeneratePackageListTask&lt;/a&gt;,&amp;nbsp;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateAutolinkingNewArchitecturesFileTask.kt&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GenerateAutolinkingNewArchitecturesFileTask&lt;/a&gt; 에서 Auto Linking을 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Auto Linking 과정을 통해 생성되는 코드들은 &lt;code&gt;&amp;lt;ROOT&amp;gt;/android/build/generated/autolinking&lt;/code&gt; 및 &lt;code&gt;&amp;lt;PACKAGE_PATH&amp;gt;/android/build/generated/autolinking&lt;/code&gt; 내에 생성되며, Java 파일은 &lt;code&gt;/java&lt;/code&gt;, JNI 통합을 위한 C++ 구현체는 &lt;code&gt;/jni&lt;/code&gt; 하위 디렉터리에 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;GeneratePackageListTask&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 태스크를 통해 Auto Linking 되어야 할 패키지 목록들이 자동으로 생성된다. 수동 링킹을 할 때와 유사한 것을 확인해 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2856&quot; data-origin-height=&quot;2044&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd3ozi/btsOCkotZXi/MAmum9edcZKSotfnRv6Zu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd3ozi/btsOCkotZXi/MAmum9edcZKSotfnRv6Zu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd3ozi/btsOCkotZXi/MAmum9edcZKSotfnRv6Zu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd3ozi%2FbtsOCkotZXi%2FMAmum9edcZKSotfnRv6Zu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;429&quot; data-origin-width=&quot;2856&quot; data-origin-height=&quot;2044&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;GenerateAutolinkingNewArchitecturesFileTask&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 빌드 통합을 위한 CMake 생성, TurboModule, View(Component)를 제공하기 위한 Provider 코드들을 생성하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 네이티브 모듈을 제공하기 위해 autolinking 파일을 생성하는데, 헤더 파일을 보면 다음과 같이 정의되어있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;5452&quot; data-origin-height=&quot;1832&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/V8Dyw/btsOCf1H5tW/EPyd2d7JpwmripnbhQefgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/V8Dyw/btsOCf1H5tW/EPyd2d7JpwmripnbhQefgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/V8Dyw/btsOCf1H5tW/EPyd2d7JpwmripnbhQefgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FV8Dyw%2FbtsOCf1H5tW%2FEPyd2d7JpwmripnbhQefgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;242&quot; data-origin-width=&quot;5452&quot; data-origin-height=&quot;1832&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;autolinking_ModuleProvider&lt;/code&gt;, &lt;code&gt;autolinking_cxxModuleProvider&lt;/code&gt;, &lt;code&gt;autolinking_registerProviders&lt;/code&gt; 함수들이 정의되어 있고, 각 함수의 구현체는 다음과 같은 형식으로 자동 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;5496&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beH6pi/btsOB1o8RvK/kj8GCKPKvdr83B8cnwb0W1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beH6pi/btsOB1o8RvK/kj8GCKPKvdr83B8cnwb0W1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beH6pi/btsOB1o8RvK/kj8GCKPKvdr83B8cnwb0W1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeH6pi%2FbtsOB1o8RvK%2Fkj8GCKPKvdr83B8cnwb0W1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;528&quot; data-origin-width=&quot;5496&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 생성된 Provider를 통해 TurboModule, Fabric에서 모듈을 참조하기 때문에 이 부분을 잘 기억하고 있자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;iOS&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iOS의 경우에도 안드로이드와 유사하게 처리되는데, React Native 프로젝트의 &lt;code&gt;Podfile&lt;/code&gt;을 열어보면 다음과 같은 코드를 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3012&quot; data-origin-height=&quot;2272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvsWzu/btsOA9VNoLk/gV6mecR2Rur3KEXA21DjAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvsWzu/btsOA9VNoLk/gV6mecR2Rur3KEXA21DjAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvsWzu/btsOA9VNoLk/gV6mecR2Rur3KEXA21DjAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvsWzu%2FbtsOA9VNoLk%2FgV6mecR2Rur3KEXA21DjAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;543&quot; data-origin-width=&quot;3012&quot; data-origin-height=&quot;2272&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 주의 깊게 볼 부분은 사전 정의된 &lt;code&gt;use_native_modules&lt;/code&gt;, &lt;code&gt;use_react_native&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;use_native_modules&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Code: &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/scripts/cocoapods/autolinking.rb#L161&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/scripts/cocoapods/autolinking.rb#L161&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 Auto Linking을 처리한다. 구현체를 조금 살펴보면 네이티브 모듈들을 찾은 뒤 해당 네이티브 모듈의 podspec을 찾아 빌드 타겟에 추가하는 것을 확인할 수 있으며, 이 덕분에 직접 빌드 구성을 손대지 않고도 외부에 설치한 네이티브 모듈을 손쉽게 빌드할 수 있게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3340&quot; data-origin-height=&quot;868&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p7isC/btsOBFs3bwo/rFnLy9nJxfnvIPo6vfM3x0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p7isC/btsOBFs3bwo/rFnLy9nJxfnvIPo6vfM3x0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p7isC/btsOBFs3bwo/rFnLy9nJxfnvIPo6vfM3x0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp7isC%2FbtsOBFs3bwo%2FrFnLy9nJxfnvIPo6vfM3x0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;187&quot; data-origin-width=&quot;3340&quot; data-origin-height=&quot;868&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;use_react_native&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Code: &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/scripts/react_native_pods.rb&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/scripts/react_native_pods.rb&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 구현을 따라가 보면 Codegen을 처리하는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/scripts/cocoapods/codegen.rb&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;run_codegen&lt;/a&gt; 을 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2988&quot; data-origin-height=&quot;2372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMTY37/btsOB7v7ifp/8YEJSL5LFVVZStWetoqol1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMTY37/btsOB7v7ifp/8YEJSL5LFVVZStWetoqol1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMTY37/btsOB7v7ifp/8YEJSL5LFVVZStWetoqol1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMTY37%2FbtsOB7v7ifp%2F8YEJSL5LFVVZStWetoqol1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;572&quot; data-origin-width=&quot;2988&quot; data-origin-height=&quot;2372&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;run_codegen에서는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/scripts/cocoapods/codegen_utils.rb#L26-L66&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;use_react_native_codegen_discovery&lt;/a&gt; 를 호출하는데, 이는 앞서 살펴본 &lt;code&gt;generate-codegen-artifacts&lt;/code&gt; 모듈을 실행시켜 코드를 생성하는 역할을 수행한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 Spec이 Codegen을 거치면 어떤 결과물이 나오는지 참고차 공유하니 참고 바란다.&lt;/p&gt;
&lt;pre id=&quot;code_1755115237978&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;// NativeBasic.ts
import { TurboModuleRegistry, type TurboModule } from 'react-native';

export interface Spec extends TurboModule {
  numericMethod(arg: number): number;
  booleanMethod(arg: boolean): boolean;
}

export default TurboModuleRegistry.getEnforcing&amp;lt;Spec&amp;gt;('Basic');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Android&lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;caret-color: auto; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;1. &amp;lt;PACKAGE_ROOT&amp;gt;/android/build/generated/source/codegen/jni/BasicSpec-generated.cpp&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1755115445849&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace facebook::react {

static facebook::jsi::Value __hostFunction_NativeBasicSpecJSI_numericMethod(facebook::jsi::Runtime&amp;amp; rt, TurboModule &amp;amp;turboModule, const facebook::jsi::Value* args, size_t count) {
  static jmethodID cachedMethodId = nullptr;
  return static_cast&amp;lt;JavaTurboModule &amp;amp;&amp;gt;(turboModule).invokeJavaMethod(rt, NumberKind, &quot;numericMethod&quot;, &quot;(D)D&quot;, args, count, cachedMethodId);
}

static facebook::jsi::Value __hostFunction_NativeBasicSpecJSI_booleanMethod(facebook::jsi::Runtime&amp;amp; rt, TurboModule &amp;amp;turboModule, const facebook::jsi::Value* args, size_t count) {
  static jmethodID cachedMethodId = nullptr;
  return static_cast&amp;lt;JavaTurboModule &amp;amp;&amp;gt;(turboModule).invokeJavaMethod(rt, BooleanKind, &quot;booleanMethod&quot;, &quot;(Z)Z&quot;, args, count, cachedMethodId);
}

NativeBasicSpecJSI::NativeBasicSpecJSI(const JavaTurboModule::InitParams &amp;amp;params)
  : JavaTurboModule(params) {
  methodMap_[&quot;numericMethod&quot;] = MethodMetadata {1, __hostFunction_NativeBasicSpecJSI_numericMethod};
  methodMap_[&quot;booleanMethod&quot;] = MethodMetadata {1, __hostFunction_NativeBasicSpecJSI_booleanMethod};
}

std::shared_ptr&amp;lt;TurboModule&amp;gt; BasicSpec_ModuleProvider(const std::string &amp;amp;moduleName, const JavaTurboModule::InitParams &amp;amp;params) {
  if (moduleName == &quot;Basic&quot;) {
    return std::make_shared&amp;lt;NativeBasicSpecJSI&amp;gt;(params);
  }
  return nullptr;
}

} // namespace facebook::react&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;iOS&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &amp;lt;ROOT&amp;gt;/ios/build/generated/ios/BasicSpec/BasicSpec-generated.mm&lt;/p&gt;
&lt;pre id=&quot;code_1755117930365&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;namespace facebook::react {

static facebook::jsi::Value __hostFunction_NativeBasicSpecJSI_numericMethod(facebook::jsi::Runtime&amp;amp; rt, TurboModule &amp;amp;turboModule, const facebook::jsi::Value* args, size_t count) {
  return static_cast&amp;lt;ObjCTurboModule&amp;amp;&amp;gt;(turboModule).invokeObjCMethod(rt, NumberKind, &quot;numericMethod&quot;, @selector(numericMethod:), args, count);
}

static facebook::jsi::Value __hostFunction_NativeBasicSpecJSI_booleanMethod(facebook::jsi::Runtime&amp;amp; rt, TurboModule &amp;amp;turboModule, const facebook::jsi::Value* args, size_t count) {
  return static_cast&amp;lt;ObjCTurboModule&amp;amp;&amp;gt;(turboModule).invokeObjCMethod(rt, BooleanKind, &quot;booleanMethod&quot;, @selector(booleanMethod:), args, count);
}

NativeBasicSpecJSI::NativeBasicSpecJSI(const ObjCTurboModule::InitParams &amp;amp;params)
  : ObjCTurboModule(params) {
      methodMap_[&quot;numericMethod&quot;] = MethodMetadata {1, __hostFunction_NativeBasicSpecJSI_numericMethod};
      methodMap_[&quot;booleanMethod&quot;] = MethodMetadata {1, __hostFunction_NativeBasicSpecJSI_booleanMethod};
}

} // namespace facebook::react&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Module Binding&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codegen, Auto Linking을 통해 네이티브 모듈들이 빌드될 수 있게 되었다. 이렇게 생성된 네이티브 모듈들이 React Native 환경에 어떻게 통합되는지 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Android&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 내용에서 &lt;code&gt;autolinking.h&lt;/code&gt;, &lt;code&gt;autolinking.cpp&lt;/code&gt; 파일이 생성되는 것을 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드에서 React Native 애플리케이션이 실행될 때 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.80.0/packages/react-native/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;JNI가 초기화되는데,&lt;/a&gt; 이 코드를 잠시 살펴보자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3272&quot; data-origin-height=&quot;1776&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b41998/btsOAYfX2pq/VxBLe6IiRQkFGQbjcDwqR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b41998/btsOAYfX2pq/VxBLe6IiRQkFGQbjcDwqR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b41998/btsOAYfX2pq/VxBLe6IiRQkFGQbjcDwqR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb41998%2FbtsOAYfX2pq%2FVxBLe6IiRQkFGQbjcDwqR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;391&quot; data-origin-width=&quot;3272&quot; data-origin-height=&quot;1776&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;익숙한 이름들이 보이는가? 잘 기억하고 있으라고 했던 그 부분이다 (autolinking.h 내에 정의된 함수들). Codegen을 통해 생성된 C++ 기반 Provider들은 여기를 통해 참조된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;TurboModuleManagerDelegate&lt;/code&gt; 그리고 &lt;code&gt;ComponentsRegistry&lt;/code&gt;에 Codegen을 통해 생성된 Provider 들이 바인딩된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바인딩된 제공자를 통해 React Native가 네이티브 모듈 그리고 뷰(View)에 접근할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;iOS&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 NativeModules와 동일하게, RCT_EXPORT_MODULE 매크로를 통해 전역 모듈 저장소에 클래스가 등록된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 TurboModule을 참조할 때, Class를 찾은 뒤 getTurboModule 메서드를 호출함으로써 TurboModule에 접근할 수 있게 된다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 codegenConfig에 별도로 componentProvider, modulesProvider 를 지정한 경우 &lt;code&gt;build/generated/ios&lt;/code&gt; 경로에 &lt;code&gt;RCTAppDependencyProvider&lt;/code&gt; 파일이 생성된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4080&quot; data-origin-height=&quot;3676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chXj3O/btsOByOw5Y2/5njt7NQzllkEai6PUoVzo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chXj3O/btsOByOw5Y2/5njt7NQzllkEai6PUoVzo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chXj3O/btsOByOw5Y2/5njt7NQzllkEai6PUoVzo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchXj3O%2FbtsOByOw5Y2%2F5njt7NQzllkEai6PUoVzo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;649&quot; data-origin-width=&quot;4080&quot; data-origin-height=&quot;3676&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현체를 보면 내부에서 Provider 들을 제공하고 있는 것을 확인할 수 있다. 각 Provider는 동일한 Codegen 경로 내에 생성(&lt;code&gt;RCTModuleProviders&lt;/code&gt;, &lt;code&gt;RCTThirdPartyComponentsProvider&lt;/code&gt;)되어있으며 각 파일은 다음과 같이 네이티브 모듈 혹은 뷰(View) 구현체를 바인딩하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4080&quot; data-origin-height=&quot;2812&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Xxq1S/btsOBY0ema6/kWmUxoyvEvM6I584lp0oK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Xxq1S/btsOBY0ema6/kWmUxoyvEvM6I584lp0oK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Xxq1S/btsOBY0ema6/kWmUxoyvEvM6I584lp0oK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXxq1S%2FbtsOBY0ema6%2FkWmUxoyvEvM6I584lp0oK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;496&quot; data-origin-width=&quot;4080&quot; data-origin-height=&quot;2812&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4192&quot; data-origin-height=&quot;4472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pBxTN/btsOBirn1M2/j7DtlqbVq2uqch9xnSKtxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pBxTN/btsOBirn1M2/j7DtlqbVq2uqch9xnSKtxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pBxTN/btsOBirn1M2/j7DtlqbVq2uqch9xnSKtxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpBxTN%2FbtsOBirn1M2%2Fj7DtlqbVq2uqch9xnSKtxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;768&quot; data-origin-width=&quot;4192&quot; data-origin-height=&quot;4472&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드와 동일하게 Codegen된 네이티브 모듈들을 각 Provider가 제공하고 있음을 확인할 수 있고, Provider 들은 &lt;code&gt;RCTAppDependencyProvider&lt;/code&gt;에 포함되는 것을 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로, &lt;code&gt;AppDelegate&lt;/code&gt; 파일을 보면 Codegen으로 생성된 &lt;code&gt;RCTAppDependencyProvider&lt;/code&gt;를 참조하고 있는 것을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3952&quot; data-origin-height=&quot;3292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MhaU4/btsOB8ofzco/5bwqHP34LNpgCRlNJxoBok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MhaU4/btsOB8ofzco/5bwqHP34LNpgCRlNJxoBok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MhaU4/btsOB8ofzco/5bwqHP34LNpgCRlNJxoBok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMhaU4%2FbtsOB8ofzco%2F5bwqHP34LNpgCRlNJxoBok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;600&quot; data-origin-width=&quot;3952&quot; data-origin-height=&quot;3292&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마무리하며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 Codegen이 어떤 과정을 거쳐 모듈 사양 정보를 수집하고, 스키마를 파싱하며, 어떤 네이티브 코드들이 생성되는지 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codegen이 도입되면서 개발 경험과 안정성을 챙기며, 모듈의 핵심 구현에만 집중할 수 있게 되었기 때문에 전반적인 구조에 대해 잘 알고 있으면 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 Codegen과 연관되어 있는 터보 모듈(Turbo Module)에 대해 자세히 살펴보도록 하자.&lt;/p&gt;</description>
      <category>언어 &amp;amp; 프레임워크/React Native</category>
      <author>이근둥</author>
      <guid isPermaLink="true">https://codevkr.tistory.com/137</guid>
      <comments>https://codevkr.tistory.com/137#entry137comment</comments>
      <pubDate>Sun, 15 Jun 2025 20:59:26 +0900</pubDate>
    </item>
    <item>
      <title>React Native Under The Hood :: New Architecture</title>
      <link>https://codevkr.tistory.com/136</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;img1.daumcdn.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sWWzM/btsOA47Z304/CwMyc0lghEkcDaln3Mr2Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sWWzM/btsOA47Z304/CwMyc0lghEkcDaln3Mr2Jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sWWzM/btsOA47Z304/CwMyc0lghEkcDaln3Mr2Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsWWzM%2FbtsOA47Z304%2FCwMyc0lghEkcDaln3Mr2Jk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;800&quot; data-filename=&quot;img1.daumcdn.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://geundung.dev/126&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;React Native Under The Hood&lt;/a&gt; 시리즈의 후속작을 소개하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 시리즈에서도 React Native의 전반적인 부분을 살펴보긴 했지만, 이제 레거시(Legacy)가 되어버린 브릿지(Bridge), 네이티브 모듈(NativeModules) 등에 대한 내용이라 참고할 일이 점차 줄어들게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 팀에서도 앞으로 레거시 아키텍처에 대한 지원을 더이상 진행하지 않는다고 공지하기도 했고, 최근 React Native 0.80.0 버전이 정식으로 릴리스된 기념으로 새로운 시리즈를 준비하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native Under The Hood 시리즈의 두 번째 내용은 New Architecture에 관한 내용이 주를 이루게 될 것이며,&amp;nbsp;React Native 0.80.0 버전을 기준으로 소개하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://geundung.dev/137&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;코드젠(Codegen)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://geundung.dev/138&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;터보 모듈(Turbo Module)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>언어 &amp;amp; 프레임워크/React Native</category>
      <author>이근둥</author>
      <guid isPermaLink="true">https://codevkr.tistory.com/136</guid>
      <comments>https://codevkr.tistory.com/136#entry136comment</comments>
      <pubDate>Sun, 15 Jun 2025 18:18:37 +0900</pubDate>
    </item>
    <item>
      <title>Hot Module Replacement (aka. Hot Reload)</title>
      <link>https://codevkr.tistory.com/135</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgXXhU/btsBOgNaHpf/5KnhCHoqqqgYHNntHyycYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgXXhU/btsBOgNaHpf/5KnhCHoqqqgYHNntHyycYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgXXhU/btsBOgNaHpf/5KnhCHoqqqgYHNntHyycYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgXXhU%2FbtsBOgNaHpf%2F5KnhCHoqqqgYHNntHyycYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;320&quot; height=&quot;320&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;개요&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Hot Module Replacement(HMR)의 기본적인 동작 메커니즘에 대해 살펴보고, 간단한 예제를 통해 살펴보려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예제와 설명은 esbuild 번들러에서 HMR 기능을 구현하는 데모를 기반으로 한다. 해당 구현 사항은 아래 저장소에서 확인 가능하니 참고하길 바란다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://github.com/leegeunhyeok/esbuild-hmr&quot;&gt;https://github.com/leegeunhyeok/esbuild-hmr&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1702373114206&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - leegeunhyeok/esbuild-hmr: PoC of Hot Module Replacement(HMR) for Esbuild&quot; data-og-description=&quot;PoC of Hot Module Replacement(HMR) for Esbuild. Contribute to leegeunhyeok/esbuild-hmr development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/leegeunhyeok/esbuild-hmr&quot; data-og-url=&quot;https://github.com/leegeunhyeok/esbuild-hmr&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/u8P66/hyUL2OEneq/21kPGwpOnm6E56t37VLmE0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/leegeunhyeok/esbuild-hmr&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/leegeunhyeok/esbuild-hmr&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/u8P66/hyUL2OEneq/21kPGwpOnm6E56t37VLmE0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - leegeunhyeok/esbuild-hmr: PoC of Hot Module Replacement(HMR) for Esbuild&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;PoC of Hot Module Replacement(HMR) for Esbuild. Contribute to leegeunhyeok/esbuild-hmr development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;좋은 개발 경험이란&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;제품을 개발하는데 중요시 되는 것 중 하나는 사용자 경험(User Experience, UX)이다. 물론 제품을 사용하게 되는 엔드 유저는 사용자이기 때문에 가장 우선적으로 고려되어야 한다. 그렇다면 제품은 누가 만들어내는 것인가? 물론 기획, 디자인 등 여러 결과물들이 종합되어야 하지만, 이를 만들어내는 것은 결국 개발자의 몫이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇기에 좋은 개발자 경험(Developer Experience, DX)에도 신경을 써야 한다고 생각한다. 개발자 경험에 따라 개발자들의 업무 효율이 크게 차이가 날 것이기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번 글의 주제인 Hot Module Replacement(HMR)은 이 개발자 경험을 증진시키기 위한 개발 편의 기능이다. 이에 대해 자세히 살펴보도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Hot Module Replacement(HMR)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;Hot Module Replacement(HMR)는 특정 모듈(코드)의 변경이 발생했을 때 해당 변경 사항을 런타임(Runtime)에 교체하는 편의 기능이다. 비슷한 말로 핫 리로드(Hot Reload)가 있는데 이는 같은 기능이라고 봐도 무방하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아마 많은 개발자들은 해당 편의 기능을 사용해본 경험이 있을 것이다. &lt;a href=&quot;https://react.dev&quot;&gt;React&lt;/a&gt; 웹 애플리케이션을 예로 들면, 컴포넌트를 수정했을 때 페이지 전체가 새로고침 되는 것이 아니라 해당 컴포넌트만 변경되는 것 말이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;한 번 스스로에게 질문을 해보자. 만약 특정 컴포넌트를 수정했는데 페이지 전체가 새로고침 된다면? 별로 생각하고 싶지 않을 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;HMR 기능은 주요 번들러(&lt;a href=&quot;https://webpack.js.org&quot;&gt;Webpack&lt;/a&gt;, &lt;a href=&quot;https://www.snowpack.dev&quot;&gt;Snowpack&lt;/a&gt;, &lt;a href=&quot;https://vitejs.dev&quot;&gt;Vite&lt;/a&gt; 등)에 구현되어있고, 우리는 별다른 구현 혹은 구성 없이 개발 편의 기능을 누리고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;동작 원리 살펴보기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;HMR의 동작 원리를 살펴보기에 앞서 자바스크립트 모듈과 번들링에 대해 간략히 살펴보도록 하겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;모듈과 번들링&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;자바스크립트의 모듈 체계를 통해 코드를 분리하고 재사용할 수 있다. 이를 통해 궁극적으로는 유지보수성을 향상할 수 있는 중요한 개념 중 하나이다. 일반적으로 소스코드 파일이 곧 모듈이 되는데, 모듈은 전역 범위와 달리 개별적인 네임스페이스를 갖는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이러한 모듈 체계는 단일화 되어있는 것이 아니라 여러 가지 유형으로 나누어져 있다. 대표적으로 &lt;code&gt;CommonJS&lt;/code&gt;, &lt;code&gt;ESM(ECMAScript Modules)&lt;/code&gt;, &lt;code&gt;AMD&lt;/code&gt; 등이 있다. (이번 글에서는 ESM 기준으로 설명할 예정이다)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;아래와 같은 자바스크립트 애플리케이션이 존재한다고 가정하자. 시작점(Entry Point)은 &lt;code&gt;main.js&lt;/code&gt; 파일로 가정한다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;1256&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brslIk/btsBQpiMGy1/H2VlB94SpoXtUP9QQTQy00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brslIk/btsBQpiMGy1/H2VlB94SpoXtUP9QQTQy00/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brslIk/btsBQpiMGy1/H2VlB94SpoXtUP9QQTQy00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrslIk%2FbtsBQpiMGy1%2FH2VlB94SpoXtUP9QQTQy00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;543&quot; data-origin-width=&quot;1666&quot; data-origin-height=&quot;1256&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;모듈은 개별적인 네임스페이스를 갖고 있기 때문에 다른 모듈의 내부를 알 수 없다. 단, 모듈에서 외부로 노출시키고 싶은 대상을 &lt;code&gt;export&lt;/code&gt; 하면 외부에서도 참조가 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;애플리케이션은 총 3개의 모듈들(&lt;code&gt;constant&lt;/code&gt;, &lt;code&gt;sub&lt;/code&gt;, &lt;code&gt;main&lt;/code&gt;)로 구성되어있는 모습을 볼 수 있는데, 외부로 노출할 대상을 &lt;code&gt;export&lt;/code&gt; 하고 &lt;code&gt;import&lt;/code&gt; 하여 참조하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;A 모듈에서 내보내고(&lt;code&gt;export&lt;/code&gt;), B 모듈에서 불러와서 참조(&lt;code&gt;import&lt;/code&gt;)하는 경우, A 모듈과 B 모듈은 의존 관계에 있다고 볼 수 있다. 위 모듈들 간의 의존 관계를 정리하면 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;main -&amp;gt; sub -&amp;gt; constants&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;여기서 중요한 포인트는 내보내고 참조하는 모듈들간의 &quot;의존 관계&quot;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앞서 이야기 했던 대부분의 번들러는 모듈들 간의 의존 관계를 분석하고 정리하여 번들을 생성한다. 이러한 과정을 번들링이라고 하는데, 번들러의 주요 역할에 따른 과정을 간단히 살펴보자면 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;모듈 탐색(Resolution)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드 변환(Transformation)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;최적화(Optimization)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;모듈 탐색 과정을 거쳐 어느 모듈이 어느 다른 모듈을 참조하는지 그래프로 의존 관계를 구성하고, 이 그래프를 순회하며 코드를 구성에 맞게 변환한다. 그리고 마지막으로 최적화 과정을 거치는데 트리 셰이킹(Tree shaking), 압축(Minify), 난독화(Mangling) 등의 과정을 거친다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드 변환의 경우 런타임 환경에 맞게 코드를 변환해야 하는 경우(예: 타입스크립트를 자바스크립트로 변환 등) 이 과정에서 처리를 진행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;오늘날의 모던 브라우저들은 대부분 ECMAScript Modules를 지원하지만, 아직 완벽하지는 않다. 그리고 널리 사용하는 타입스크립트는 자바스크립트 엔진에서 바로 실행시킬 수 없다. 그렇기 때문에 ESM을 브라우저에서 실행 가능하도록 변환하고, 자바스크립트로 변환하는 등의 과정은 불가피한 것이 현실이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앞서 살펴본 애플리케이션을 &lt;a href=&quot;https://esbuild.github.io&quot;&gt;esbuild&lt;/a&gt;로 번들링하면 아래와 같은 형태로 변환될 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3004&quot; data-origin-height=&quot;1250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boMF1L/btsBQytDUnt/Ud2u8CygnXyTKyhLoUR9G0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boMF1L/btsBQytDUnt/Ud2u8CygnXyTKyhLoUR9G0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boMF1L/btsBQytDUnt/Ud2u8CygnXyTKyhLoUR9G0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboMF1L%2FbtsBQytDUnt%2FUd2u8CygnXyTKyhLoUR9G0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;300&quot; data-origin-width=&quot;3004&quot; data-origin-height=&quot;1250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이러한 과정을 거쳐 비로소 자바스크립트 런타임에서 실행 가능한 형태의 코드(번들)로 거듭나게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;런타임 교체를 위한 모듈 체계&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;번들 코드를 보면 단순히 모듈들이 하나의 파일로 통합되어 있는 상태이다. HMR은 번들 코드가 실행되어 있는 상태에서 특정 모듈 코드를 동적으로 교체해야 하는데, 위와 같은 번들 코드에서는 구현하기 어렵다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;심지어 esbuild는 HMR를 지원하지 않기에, 해당 기능이 필요한 경우 직접 구현해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;한 번 간단히 생각해보자. ESM로 구현된 코드는 결국 내보내고(&lt;code&gt;export&lt;/code&gt;), 불러오는(&lt;code&gt;import&lt;/code&gt;) 행위를 통해 서로 참조한다. 모듈을 특정 공간에 등록(&lt;code&gt;export&lt;/code&gt;)해두고, 저장된 모듈을 참조(&lt;code&gt;import&lt;/code&gt;)하는 것으로 이러한 동작을 모방할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;모듈은 개별적인 네임스페이스를 갖고 있지만, 전역 객체는 그렇지 않다. 모듈에 대한 참조를 전역 객체에 등록해 두고, 전역 객체에서 이를 참조하는 방식으로 구현이 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;예를 들면 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;2144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0QYsP/btsBQr1IwL3/GKLmrADqPFKlWL6kVyzojK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0QYsP/btsBQr1IwL3/GKLmrADqPFKlWL6kVyzojK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0QYsP/btsBQr1IwL3/GKLmrADqPFKlWL6kVyzojK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0QYsP%2FbtsBQr1IwL3%2FGKLmrADqPFKlWL6kVyzojK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;523&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;2144&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;여기에 구현된 &lt;code&gt;global.export&lt;/code&gt; 는 이해를 돕기 위해 추가한 &quot;가상&quot;의 전역 모듈 체계 구현체이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;모듈에서 &lt;code&gt;export&lt;/code&gt; 하는 대상을 전역 객체에도 동일하게 등록한다. 이렇게 등록된 대상은 이제 ESM 대신 전역 모듈을 통해 참조할 수 있을 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2568&quot; data-origin-height=&quot;2976&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyiV1a/btsBJeomuQk/CTW3hMDHyFARnd3FZxdC6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyiV1a/btsBJeomuQk/CTW3hMDHyFARnd3FZxdC6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyiV1a/btsBJeomuQk/CTW3hMDHyFARnd3FZxdC6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyiV1a%2FbtsBJeomuQk%2FCTW3hMDHyFARnd3FZxdC6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;579&quot; data-origin-width=&quot;2568&quot; data-origin-height=&quot;2976&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;ESM의 모듈 구문 대신 전역에서 참조할 수 있도록 대체하면 등록된 모듈을 그대로 참조할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이와 같이 런타임 교체를 위한 커스텀 모듈 체계는 번들러를 통해 처리되며, 번들러마다 구현되어있는 방식이 조금씩 다르다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이처럼 커스텀 모듈 체계를 위해 위해 swc 플러그인을 구현해두었다. 관심이 있다면 아래 라이브러리를 참고하길 바란다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://github.com/leegeunhyeok/swc-plugin-global-module&quot;&gt;https://github.com/leegeunhyeok/swc-plugin-global-module&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1702373218080&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - leegeunhyeok/swc-plugin-global-module:   Transform ESM &amp;amp; CJS to global scoped module&quot; data-og-description=&quot;  Transform ESM &amp;amp; CJS to global scoped module. Contribute to leegeunhyeok/swc-plugin-global-module development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/leegeunhyeok/swc-plugin-global-module&quot; data-og-url=&quot;https://github.com/leegeunhyeok/swc-plugin-global-module&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bQnKU0/hyUL1WvxOu/9DyDVN7kz5eGVIBHOz8PWk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/leegeunhyeok/swc-plugin-global-module&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/leegeunhyeok/swc-plugin-global-module&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bQnKU0/hyUL1WvxOu/9DyDVN7kz5eGVIBHOz8PWk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - leegeunhyeok/swc-plugin-global-module:   Transform ESM &amp;amp; CJS to global scoped module&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  Transform ESM &amp;amp; CJS to global scoped module. Contribute to leegeunhyeok/swc-plugin-global-module development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지금까지 살펴본 모듈과 번들링에 대한 내용은 후술할 내용에서 다시 언급할 예정이니 잘 숙지하고 있도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;모듈 변경 감지&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;다시 HMR로 돌아오면, 모듈의 변경이 발생했을 때 이를 인지하고 해당 모듈만 다시 변환하여 런타임에 전달해주어야 한다. 과정을 보면 &quot;모듈의 변경&quot;을 감지하는 것으로부터 시작된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;모듈 변경의 감지는 곧 파일의 변경을 감지하는 것과 동일하다. (모듈은 소스코드 파일로 존재하기 때문에)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;파일 시스템의 변경 사항 감지는 운영체제(OS)에서 제공하는 API를 통해 쉽게 받아볼 수 있다. 그리고 Node의 경우 잘 만들어진 오픈소스 라이브러리가 존재하기에 이를 활용하면 쉽게 구현할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;본 글에서는 &lt;a href=&quot;https://github.com/paulmillr/chokidar&quot;&gt;chokidar&lt;/a&gt; 라이브러리를 사용하여 모듈 변경 감지 기능을 구현할 예정이다. (실제 Vite 에서 이를 사용하고 있기도 하다) &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;구현 자체는 아주 간단하다. 감시할 대상 경로를 지정하고 환경에 맞게 옵션을 추가하고 이벤트 핸들러만 등록하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2272&quot; data-origin-height=&quot;1856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtAbzq/btsBUJ741hu/rhAZaCtzyyz2hD9CCQUnv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtAbzq/btsBUJ741hu/rhAZaCtzyyz2hD9CCQUnv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtAbzq/btsBUJ741hu/rhAZaCtzyyz2hD9CCQUnv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtAbzq%2FbtsBUJ741hu%2FrhAZaCtzyyz2hD9CCQUnv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;588&quot; data-origin-width=&quot;2272&quot; data-origin-height=&quot;1856&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이벤트 종류가 여려가지인데, 어느 이벤트가 발생했을 때 HMR 기능이 처리되어야 할까? 정답은 간단하다. 답은 모듈의 의존 관계가 변경될 수 있는 상황의 이벤트가 트리거 되었을 때이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이에 해당하는 이벤트는 &lt;code&gt;add&lt;/code&gt;, &lt;code&gt;change&lt;/code&gt;, &lt;code&gt;unlink&lt;/code&gt;, &lt;code&gt;unlinkDir&lt;/code&gt; 이다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;code&gt;add&lt;/code&gt;: 파일 추가 (모듈이 추가되었을 수 있음)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;code&gt;change&lt;/code&gt;: 파일 내용 변경 (모듈 내용이 변경되었을 수 있음)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;code&gt;unlink&lt;/code&gt;: 파일 제거 (모듈이 제거되었을 수 있음)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;code&gt;unlinkDir&lt;/code&gt;: 디렉토리 제거 (모듈이 포함된 디렉토리가 제거되었을 수 있음)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;위 코드를 통해 어느 파일(혹은 디렉토리)이 변경되었는지 전달받을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;모듈 변환&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;변경이 감지되었을 때 고려해야 할 점은 역시 &quot;의존 관계&quot;이다. &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;새로운 모듈이 의존 관계 내에 포함되거나, 제거되는 경우에는 의존 관계 그래프를 다시 구성해야 한다.&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;단순히 모듈 내용이 변경된 경우에도 추가적인 고려가 필요하다. 그것은 바로 역 의존성(Reverse dependencies)이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;code&gt;constants&lt;/code&gt; 모듈 파일에서 변경이 감지되었다고 가정하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇다면 이 모듈의 변경사항만 반영하면 되는 것일까? 당연히 모듈의 변경 사항은 반영되어야 하고, 이를 참조하고 있는 부모 모듈에서도 이에 대한 변경 사항이 반영되어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;처음 모듈 의존 관계에 대해 살펴보았을 때, 시작점(Entry Point)에서 출발하여 하위 모듈로 탐색했던 것을 기억하는가? 역 의존성은 말 그대로 의존성 관계를 뒤집은 것과 동일하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이번에는 &lt;code&gt;constants&lt;/code&gt; 가 변경되었으니 여기서부터 출발해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;1094&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z6WYf/btsBR8m3cvm/D30ouaMg2XlL1LqmkkdOz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z6WYf/btsBR8m3cvm/D30ouaMg2XlL1LqmkkdOz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z6WYf/btsBR8m3cvm/D30ouaMg2XlL1LqmkkdOz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ6WYf%2FbtsBR8m3cvm%2FD30ouaMg2XlL1LqmkkdOz0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;549&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;1094&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;의존 관계를 보면 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;constants -&amp;gt; sub -&amp;gt; main&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이 말인즉슨, &lt;code&gt;constants&lt;/code&gt; 의 변경 사항이 발생할 경우 &lt;code&gt;sub&lt;/code&gt;, &lt;code&gt;main&lt;/code&gt; 모듈도 새로 업데이트되어야 한다는 점이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그런데 한 가지 의문점이 생긴다. &lt;code&gt;constants&lt;/code&gt; 에서 &lt;code&gt;export&lt;/code&gt; 한 값은 &lt;code&gt;sub&lt;/code&gt; 모듈에서만 참조하고 있는데, 왜 &lt;code&gt;main&lt;/code&gt; 도 업데이트 되어야 할까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이유는 간단하다. &lt;code&gt;main&lt;/code&gt; 모듈에서 &lt;code&gt;sub&lt;/code&gt; 모듈을 참조하고 있기 때문이다. 하위 모듈의 변경 사항으로 인해 어떠한 부수 효과(Side effect)가 생길지 알 수 없기 때문에 모듈의 시작점(Entry Point)까지 거슬러 올라가며 새로 업데이트해주어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;런타임에 실행되어야 하는 코드는 곧 아래와 같이 변환된 &lt;code&gt;constants&lt;/code&gt; + 변환된 &lt;code&gt;sub&lt;/code&gt; + 변환된 &lt;code&gt;main&lt;/code&gt; 가 합쳐진 코드 조각이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2648&quot; data-origin-height=&quot;2264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1644K/btsBJalXFIm/z3k94eVkTyL6JbcOmqpeuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1644K/btsBJalXFIm/z3k94eVkTyL6JbcOmqpeuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1644K/btsBJalXFIm/z3k94eVkTyL6JbcOmqpeuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1644K%2FbtsBJalXFIm%2Fz3k94eVkTyL6JbcOmqpeuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;616&quot; data-origin-width=&quot;2648&quot; data-origin-height=&quot;2264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;변경 사항 전달 및 교체&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;앞서 변환된 코드 조각은 런타임으로 전달되어야 한다. 대부분의 번들러에서는 이 과정을 웹 소켓을 통해 실현한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/drsSo5/btsBUBoIayT/SnKdkQPp65HioOA1uwy1xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/drsSo5/btsBUBoIayT/SnKdkQPp65HioOA1uwy1xk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/drsSo5/btsBUBoIayT/SnKdkQPp65HioOA1uwy1xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdrsSo5%2FbtsBUBoIayT%2FSnKdkQPp65HioOA1uwy1xk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;171&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;362&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;전달된 코드 조각은 런타임에서 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval&quot;&gt;eval&lt;/a&gt; 과 같은 함수를 통해 동적으로 실행되며 변경 사항 교체가 성공적으로 이루어진 경우와 그렇지 않은 경우로 나눠진다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;1336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pcVqa/btsBTW7WkbI/xEcfN2wJcRARflhrBj2hN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pcVqa/btsBTW7WkbI/xEcfN2wJcRARflhrBj2hN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pcVqa/btsBTW7WkbI/xEcfN2wJcRARflhrBj2hN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpcVqa%2FbtsBTW7WkbI%2FxEcfN2wJcRARflhrBj2hN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;361&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;1336&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;만약 변경 사항에 대한 코드 조각을 동적으로 실행시켰을 때 문제가 발생하는 경우 모듈을 찾을 수 없거나, 코드가 잘못되어있는 등 HMR에 문제가 발생한 것으로 판단할 수 있다&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이런 상황이 발생하는 경우에는 전체 새로고침(Fully reload)을 수행하는 것이 일반적이다. 이 역시 &lt;code&gt;try-catch&lt;/code&gt; 구문 내에서 HMR 변경 사항을 동적으로 실행시키고, 예외가 발생한 경우에만 전체 새로고침 하도록 구현 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이러한 과정을 거쳐 최종적으로 모듈이 동적으로 교체되고, 실행되어 변경사항이 실시간으로 반영되게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지금까지 살펴본 HMR 동작을 간단히 그림으로 나타내면 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;864&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3c7gu/btsBQs7q6JY/b1QEFlhPYBAveYT9xh4Gl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3c7gu/btsBQs7q6JY/b1QEFlhPYBAveYT9xh4Gl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3c7gu/btsBQs7q6JY/b1QEFlhPYBAveYT9xh4Gl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3c7gu%2FbtsBQs7q6JY%2Fb1QEFlhPYBAveYT9xh4Gl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;498&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;864&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;더 살펴보기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; text-align: start;&quot;&gt;앞서 살펴본 내용에서는 단순히 변경된 모듈을 감지하고, 역 의존성 모듈을 모두 변환한 코드를 런타임에 실행하는 과정에 대해서만 살펴보았다. 설명에 포함되지 않은 내용 역시 상당히 많이 있는데 간단히 알아보도록 하자.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;모듈 교체 생명 주기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;요즘날 번들러에 구현된 HMR의 경우 훨씬 고도화되어있다. HMR 변경 사항을 수락(반영)하거나 거절(무시)하는 등 생명 주기를 갖고 있고, 이를 통해 더 세부적인 모듈 교체 작업을 수행한다. 예를 들면 모듈의 변경 사항을 반영하기에 앞서 상태를 재설정해야 하는 등 교체 전, 후로 수행해야 할 작업을 생명주기를 통해 처리한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;관련된 코드는 직접 번들러 코드를 열어보면 알 수 있겠으나, 간단하게 정리되어 있는 구현 스펙 문서 또한 존재한다. 스펙을 제안한 사람은 아래와 같으며 실제 이 분야에서 뛰어난 역량을 갖고 있는 분들이기 때문에 참고하면 큰 도움이 될 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://github.com/FredKSchott&quot;&gt;Fred K. Schott&lt;/a&gt; (Snowpack)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://github.com/JoviDeCroock&quot;&gt;Jovi De Croock&lt;/a&gt; (Preact)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://github.com/yyx990803&quot;&gt;Evan You&lt;/a&gt; (Vue)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;스펙 자료는 아래 저장소에서 확인 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://github.com/FredKSchott/esm-hmr&quot;&gt;https://github.com/FredKSchott/esm-hmr&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1702373339856&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - FredKSchott/esm-hmr: a Hot Module Replacement (HMR) API for your ESM-based dev server.&quot; data-og-description=&quot;a Hot Module Replacement (HMR) API for your ESM-based dev server. - GitHub - FredKSchott/esm-hmr: a Hot Module Replacement (HMR) API for your ESM-based dev server.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/FredKSchott/esm-hmr&quot; data-og-url=&quot;https://github.com/FredKSchott/esm-hmr&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/AYVBH/hyUIEonUoo/nZM8iVavIJniu5uVmRffJk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/FredKSchott/esm-hmr&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/FredKSchott/esm-hmr&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/AYVBH/hyUIEonUoo/nZM8iVavIJniu5uVmRffJk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - FredKSchott/esm-hmr: a Hot Module Replacement (HMR) API for your ESM-based dev server.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;a Hot Module Replacement (HMR) API for your ESM-based dev server. - GitHub - FredKSchott/esm-hmr: a Hot Module Replacement (HMR) API for your ESM-based dev server.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;React&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;React의 경우 변경 사항을 단순히 전달하는 것으로 HMR을 구현할 수 없다. 왜냐하면 React의 컴포넌트는 단순히 코드가 평가되는 것으로 해결되는 것이 아니라 컴포넌트 내부에서 참조하고 있는 여러 가지 상태와 부모로부터 주입받은 props 등 고려할 대상이 훨씬 많고 복잡하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그렇다면 다른 번들러들은 이를 어떻게 해결한 것일까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;React에서 Fast Refresh를 구현한 &lt;a href=&quot;https://www.npmjs.com/package/react-refresh&quot;&gt;react-refresh&lt;/a&gt; 라이브러리가 오픈소스로 공개되어 있다. 다른 번들러들도 이를 기반으로 플러그인을 구현해 두고 사용하는 방식으로 문제를 해결했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;React 컴포넌트인 경우 &lt;a href=&quot;https://github.com/facebook/react/blob/v18.2.0/packages/react-refresh/src/ReactFreshRuntime.js#L306-L349&quot;&gt;register&lt;/a&gt; 를 호출하여 &lt;code&gt;react-refresh&lt;/code&gt; 컨텍스트에 해당 컴포넌트를 미리 등록해 두고, 추후 해당 컴포넌트의 변경이 발생했을 때 Re-render 혹은 Re-mount 처리를 내부적으로 수행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;참고로 이는 모듈 교체 생명 주기와 연관되어있기도 한데, 모듈 변경을 수신하고 수락(accept)했을 시점에 &lt;code&gt;react-refresh&lt;/code&gt; 내에 구현되어 있는 &lt;a href=&quot;https://github.com/facebook/react/blob/v18.2.0/packages/react-refresh/src/ReactFreshRuntime.js#L191-L304&quot;&gt;performReactRefresh&lt;/a&gt; 를 호출하여 React Reconciler에게 변경 사항에 대한 반영을 요청한다. 덕분에 복잡한 구현 없이, 컴포넌트가 갖고 있던 상태를 유지한 채 Re-render 처리를 수행할 수 있게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;이와 관련하여 기술 검증을 해본 기록이 있는데, 궁금하다면 아래 저장소를 참고하길 바란다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://github.com/leegeunhyeok/esbuild-react-refresh-poc&quot;&gt;https://github.com/leegeunhyeok/esbuild-react-refresh-poc&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1702373355372&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - leegeunhyeok/esbuild-react-refresh-poc: Esbuild with react-refresh for custom HMR&quot; data-og-description=&quot;Esbuild with react-refresh for custom HMR. Contribute to leegeunhyeok/esbuild-react-refresh-poc development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/leegeunhyeok/esbuild-react-refresh-poc&quot; data-og-url=&quot;https://github.com/leegeunhyeok/esbuild-react-refresh-poc&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bQOOUJ/hyULVILfbJ/x8qL2swDFnGE3GctNbyJyK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/leegeunhyeok/esbuild-react-refresh-poc&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/leegeunhyeok/esbuild-react-refresh-poc&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bQOOUJ/hyULVILfbJ/x8qL2swDFnGE3GctNbyJyK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - leegeunhyeok/esbuild-react-refresh-poc: Esbuild with react-refresh for custom HMR&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Esbuild with react-refresh for custom HMR. Contribute to leegeunhyeok/esbuild-react-refresh-poc development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;마무리&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;지금까지 개발하는 데 있어 상당히 중요한 비중을 차지하고 있는 HMR에 대해 간략히 살펴보았다. 직접 번들러를 구현하거나 개발 환경을 구축하는 일은 매우 드물겠으나, 혹시나 진행하게 된다면 개발자 경험을 위해서라도 HMR 기능은 반드시 필요하다고 본다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;필자 본인은 esbuild에서 HMR를 구현하기 위해 여러 가지 시도를 해보았는데, 관련하여 정리했던 내용을 글로 작성해 본다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;글은 이만 마무리하도록 하고, 궁금한 부분은 댓글 혹은 이메일로 전달해 주길 바란다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>언어 &amp;amp; 프레임워크/JavaScript</category>
      <author>이근둥</author>
      <guid isPermaLink="true">https://codevkr.tistory.com/135</guid>
      <comments>https://codevkr.tistory.com/135#entry135comment</comments>
      <pubDate>Tue, 12 Dec 2023 18:34:51 +0900</pubDate>
    </item>
    <item>
      <title>2023년도 회고 (feat. 이직 그리고 토스)</title>
      <link>https://codevkr.tistory.com/134</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cover-2023.png&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CotLr/btsBGnE08iv/x8OiRzZ2YuvkL04cMWgyNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CotLr/btsBGnE08iv/x8OiRzZ2YuvkL04cMWgyNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CotLr/btsBGnE08iv/x8OiRzZ2YuvkL04cMWgyNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCotLr%2FbtsBGnE08iv%2Fx8OiRzZ2YuvkL04cMWgyNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;320&quot; height=&quot;320&quot; data-filename=&quot;cover-2023.png&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;벌써 2023년의 마지막, 12월이다. 지금까지 공부를 하고, 일을 해오면서 개인 회고를 진행했던 적이 없었는데, 이제부터 연말에 한 번이라도 개인 회고를 진행할까 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;첫 직장에서부터 지금까지 단 한 번의 공백기 없이, 쉬지 않고 달려왔다. 이번 회고를 통해 지난날들을 돌이켜보고, 앞으로의 계획을 세우고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;본론으로 넘어가기 전에, 회고글은 필자 본인의 매우 주관적인 견해가 포함되어 있으니 감안하고 읽어주면 감사하겠다. 내용 관련해서 다른 생각을 갖고 있거나, 이야기를 해보고 싶다면 언제든지 메일로 연락 주길 바란다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;커리어의 시작&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;먼저, 필자에 대해 소개하자면 특성화 고졸 출신 개발자이다. 그리고 고등학생때에는 &quot;도제학교&quot;라는 정부 정책의 지원을 받아 고등학교 2학년 2학기부터 졸업까지 실제 개발 업체에 출근하는 조금 독특한 학창 생활을 보냈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;졸업 이후 해당 업체에 정규직으로 채용되어 2년 반 정도의 시간을 보냈다. 당시 다니던 회사는 중소기업 SI 업체였다. 사회생활을 시작한 발판이었기 때문에 접하는 것들 모든 것이 옳은 것인 줄만 알았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;첫 고민&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;점차 시간이 지나 사회 생활에 &lt;s&gt;찌들어가다&lt;/s&gt; 적응하다 보니 모든것이 마냥 옳은 것이 아니다는 것을 인지하기 시작했다. 회사가 추구하는 목적과 방향성이 자주 바뀌었고, 그러한 조직 내에서 나는 어느 목표를 갖고 업무에 임해야 하나 걱정도 많이 되었다. 설상가상 R&amp;amp;R이 정확히 정해져 있지 않아 개발뿐만 아니라 아니라 제안서 작성 등 전반적인, 잡다한 업무를 모두 수행하기도 했고, 개발 문화도 잘 잡혀있지 않은 환경 속에서 혼자서 개발을 하다 보니 기술적인 성장을 갈구하던 필자로썬 불만이 점점 커져만 갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이 시점부터 회의감이 들기 시작했고, 개발자로써 어떤 커리어를 쌓아나가야 할지, 앞으로 뭘 어떻게 해야 할지 고민이 생기기 시작했다. 돌이켜보면 사회생활을 하면서 처음으로 방황했던 시기였던 것 같기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그렇지만 필자 본인이 포기하지 않은 것이 있다. 그것은 바로 &quot;꾸준하게 노력&quot;하는 것이다. 개인적으로 개발 자체를 정말 즐거워하고 취미 생활로 할 정도로 나와 적성이 잘 맞는다고 생각했기에 방황하던 시기에도 이를 놓치지 않았다. 오히려 나만의 사이드 프로젝트를 진행한다거나, 오픈소스 라이브러리를 만들어 보는 등 스스로 동기부여를 지속했던 것 같다. 물론 누가 시킨 것도 아니고, 누군가에게 보여주기 위한 목적은 아니었다. 단지, 즐거워서 했을 뿐이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;새로운 기회&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;사이드 프로젝트와 오픈소스 프로젝트들은 모두 필자의 &lt;a href=&quot;https://github.com/leegeunhyeok&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;깃허브(GitHub)&lt;/a&gt;에 모두 공개해두었는데, 이를 보고 클래스팅이라는 에듀테크 스타트업에서 나에게 먼저 손을 내밀어주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;커피챗을 하며 조직 문화와, 개발 문화 등에 대해 간접적으로 전해 들을 수 있었고, 이때 신선한 충격을 받았다. 다니고 있던 직장과 비교했을 때 정말 개발자로서 성장할 수 있겠다는 기대감이 들었고, 기회가 되면 합류하고 싶다는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;당시 산업기능요원으로 재직 중이었기에 바로 이직을 할 수 있는 상황은 아니었다. 1년 정도 지난 후 이직을 할 수 있었기에 다음을 기약하며 더욱더 노력해야겠다고 다짐했다. 1년이라는 시간 동안 회사의 업무도 진행하며 개인적인 성장을 위해 정말 많은 노력을 했던 것 같다. 그중 7개월가량은 집필을 하며 시간을 보냈고 성공적으로 출간까지 마무리하기도 했다. 물론 개인적인 오픈소스 활동도 꾸준히 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주니어 개발자의 첫 번째 이직, 클래스팅&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이렇게 1년 정도의 시간이 흘렀을까, 이직할 수 있는 시점이 되었을 무렵 클래스팅 HR 담당자분께서 1년이라는 시간을 잊지 않고 나에게 다시 한 번 손을 내밀어 주셨고, 필자 역시 흔쾌히 지원을 결심했다. 프론트엔드 엔지니어 포지션으로 지원했고 빠르게 채용 절차가 진행되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기술 면접 (1시간 30분)&lt;/li&gt;
&lt;li&gt;임원 면접 w/Dave (1시간)&lt;/li&gt;
&lt;li&gt;레퍼런스 체크&lt;/li&gt;
&lt;li&gt;처우 협의 및 입사&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;서류 전형과 과제 등의 절차는 진행되지 않았는데, 정확한 이유는 잘 기억이 나지 않는다. 아마 커피챗을 통해 추천 전형으로 진행된 것이 아닌가 싶긴 한데, 어쨌든 바로 기술 면접을 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;기술 면접&lt;/b&gt;&lt;br /&gt;벌써 3년 가까이 지난 일이라 정확히 기억나진 않지만, 면접 경험 자체는 좋았던 것으로 기억한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;주로 자바스크립트의 기본적인 동작 방식과 Vue 에 대한 내용에 대해 질문을 받았었지만, SI 업체에서 업무를 하다 보니 이런저런 언어들을 통해 풀스택 개발을 진행했었기에 여러 분야에 대해 질문을 받았던 것으로 기억한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;클래스팅의 주요 기술 스택인 리액트(React)를 다룰 줄도 몰랐고 사용해 본 경험도 없었으며, 면접을 진행하며 나의 부족함에 대해 알 수 있었기에 더더욱 좋은 결과를 받긴 어렵겠구나 싶 었다. 그래도 부족함에 대해 객관적으로 돌아볼 수 있던 경험이었다 생각하고 더 열심히 해야겠다는 다짐을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1주일 정도의 시간이 흘렀을까, 다행히도 기술 면접에 통과되었다는 결과를 받았다. 부족한 부분이 많았음에도 불구하고 꾸준히 노력하는 열정과, 가능성을 보고 좋은 결과를 준 것이 아닌가 생각된다. 그리고 이어서 다음 임원 면접 과정에 대해 안내받을 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;임원 면접&lt;/b&gt;&lt;br /&gt;두 번째 면접은 CEO인 데이브(Dave)와 진행했다. 클래스팅 문화와 가치관이 필자와 잘 맞는지 살펴보는 그런 자리였는데, 필자 본인의 업무 성향과 동료를 대하는 자세 등에 대해 이야기 할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;여러가지 이야기 중 필자 본인은 대외적인 활동을 많이 하며 남들에게 인정받고 싶다는 뉘앙스의 이야기를 했었는데, 이에 대해 조언을 해준 내용이 아직도 머릿속에서 잊혀지지 않는다. 대외적으로 활동하는 것 또한 중요하지만 가장 먼저 신경써야 할 것은 속해있는 조직 내에서 인정받는 사람이 되는 것이 오히려 더 중요할 수도 있다는 내용이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;사회생활도 적고 경력도 적었기에 남들에게 잘 보여야 한다는 막연한 생각을 가졌던 것 같은데, 지금까지도 데이브의 조언을 마음속에 잘 새겨두고 있다. 면접이 끝난 후, 다음날에 바로 통과 소식을 접할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;새로운 팀으로의 합류, 그러나&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;레퍼런스 체크와 처우 협의 등의 과정은 생략하겠다. 결국 최종 합격이라는 소식을 접하게 되었고 정말 열심히 하자는 다짐을 했다. 기존에 다니던 회사에 사직서를 제출하고 클래스팅으로의 합류를 손꼽아 기다리고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그러나 당시 필자 본인은 산업기능요원으로 재직 중이었고, 아직 훈련소를 다녀오지 않은 상황이었다. (...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;클래스팅 최종 합격 시점쯤 입영통지서를 받았기에 해당 상황을 클래스팅측에 전달하여 양해를 구했고, 그렇게 3주간의 기초 군사 훈련을 받으러 논산 훈련소로 떠났다. 훈련소 안에서의 3주 마저 정말 3년 같은 시간이었다. 산업기능요원으로 편입하지 않았더라면 18개월을 지냈어야 하는데, 정말 인생에 있어 잘한 선택 중 하나이지 않나 싶다. (&lt;span style=&quot;text-align: start;&quot;&gt;모든 기간을 복무한 군필자 분들에게는 죄송하다)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3주라는 시간이 흐른 후 바로 클래스팅으로 합류를 하게 되었다. 당시 &lt;s&gt;빡빡&lt;/s&gt;머리였기 때문에 항상 모자를 쓰고 다녔던 기억이 아직도 생생하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클래스팅에서의 성장&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;SI 업체에서 스타트업으로 이직을 하다보니 초반에는 적응하는데 어려움이 있기도 했다. 업무적인 환경에 적응하는 것이 어려운 것이 아니라 필자 본인의 무지함에서 나오는 어려움이 더 컸던 것 같다. 처음 접하고 모르는 것이 많았기에 스스로가 작아져만 갔다. 그래도 주변에 좋은 동료들이 많았고 필자를 잘 도와주기도 했다. 동료들의 지원이 있었기에 내가 해야할 일은 절대 포기하지 않는 것이었다. 부족한 부분을 빠르게 채우고 적응하고자 새벽까지 남아서 일을 했던 적도 있고, 퇴근 이후 새벽까지 공부하는 등 부족한 만큼 더 열심히 노력했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;코드 리뷰를 통해 동료로부터 많은 피드백을 받을 수 있었고, 팀 회고를 거쳐 문제점을 파악하고 개선할 수 있는 방향을 찾는 등 점차 클래스팅의 업무 문화에 녹아들게 되었다. 길다면 길고, 짧다면 짧은 3개월 수습기간 동안 엄청 많은 것들을 배울 수 있었고, 좋은 동료들 사이에서 필자 또한 좋은 영향력을 줄 수 있는 그런 개발자가 되고자 다짐했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;시작은 리액트(React)를 몰랐던 상태로 합류했지만, 2년 반 넘는 시간동안 리액트 네이티브(React Native) 개발을 담당했다. 그리고 자연스레 프론트엔드 개발자 커리어를 쌓기 위한 방향을 어느정도 결정할 수 있었다. 단, 한 가지 고민이 있었는데 그것은 바로 웹 프론트엔드로 다시 복귀할 것인가, 리액트 네이티브 개발을 계속 할 것인가 였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;개발이라는 것 자체는 회사의 비즈니스 목표를 달성하기 위한 &quot;수단&quot;일 뿐이라고 생각하긴 하지만, 결국 개발을 하는 필자 입장에서는 선택이 필요한 것은 분명했다. 같은 프론트엔드 범주에 속해있다고 하더라도, 웹과 네이티브라는 플랫폼이 갖는 특성은 전혀 다르기에 고민이 필요한 시점이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;두 번째 기회 그리고 토스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이러한 고민을 하던 중, 필자의 깃허브(GitHub), 블로그를 통해 최근 딥다이브했던 리액트 네이티브(React Native) 게시글 그리고 오픈소스로 공개해두었던 esbuild 기반 번들러를 보고 토스에서 먼저 연락을 보내주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;토스의 개발 문화도 나름 궁금하기도 했고 토스팀에서는 리액트 네이티브를 어떻게 쓰고 있었는지 궁금했기에 커피챗을 진행하게 되었다. 사무실로 초대받아 사무실 투어도 진행했었고, 기술적인 이야기 뿐만 아니라 문화에 대해서도 이야기를 나눌 수 있었다. 당시 이직 생각은 없던 상태였기에 나중에 기회가 되면 다시 연락 드리겠다고 하고, 사무실을 나오게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;리액트 네이티브를 정말 잘 사용(하려고 하는)하는 회사는 손에 꼽을 정도로 적다. 토스가 그런 회사 중 하나라고 생각하는데, 토스에서 리액트 네이티브를 더 깊게 파보는 것도 커리어적으로 큰 도움이 될 것이라는 생각이 조금씩 떠오르게 되었다. 토스팀에 합류하게 되더라도 프론트엔드 분야에서 많은 것을 시도해보며 성장할 수 있는 것은 분명했고, 사내 추천 제도를 통해 지원할 수 있는 기회가 있다보니 한 번 도전해보자는 결심을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이력서&lt;/li&gt;
&lt;li&gt;직무 인터뷰&lt;/li&gt;
&lt;li&gt;문화 적합성 인터뷰&lt;/li&gt;
&lt;li&gt;레퍼런스 체크&lt;/li&gt;
&lt;li&gt;처우 협의 및 입사&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;토스의 프론트엔드 플랫폼 팀으로 지원을 하기 위해 커피챗을 진행했던 분에게 연락을 드리니 지원을 반겨주시면서 토스 사내 시스템에 등록을 해주셨다. 서류는 당일에 바로 통과되었고, 사내 추천 전형으로 진행되는 채용 절차이다보니 코딩 테스트나 과제 평가 없이 바로 직무 인터뷰(기술 면접)를 진행하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;자세한 토스로의 이직기는 추후 별도 글로 작성하도록 하겠다. 결론만 이야기 하자면 최종 합격 후 12월 중순에 토스팀 합류 대기 중인 상황이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;여정의 쉼표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;클래스팅이라는 조직에서 에이든(Aiden)이라는 이름으로 3년 가까운 시간을 함께 하면서 정말 많이 성장했다. 밖에서보다 조직 내에서 인정받는 사람이 되는 것이 중요하고, 주변에 선한 영향력을 끼치며 무엇보다 자만하지 않고 항상 겸손하게 배움을 놓치지 않는 자세까지. 클래스팅에서 배운 것이 많았던 만큼 떠나게 되어 아쉬움도 많은데, 앞으로 클래스팅에서 배우고 성장했던 경험을 토대로 개발자 커리어를 쌓아가려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;필자는 앞으로 1주일 정도 리프레쉬 시간을 가지고, 바로 토스로 합류하려고 한다. 그 기간동안에는 분위기 좋은 카페 등을 돌아다니며 생각도 정리하고 취미 생활도 가질 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;다시 처음으로 돌아가자면, 고등학생 시절부터 SI 회사, 클래스팅을 거쳐 토스까지. 4년 조금 넘은 시간이 걸린 것 같다. 비록 시작은 고졸 개발자였지만, 꾸준히 노력하고 포기하지 않는 자세 하나만으로 좋은 회사에서, 좋은 동료들을 만나고, 좋은 경험을 했다는 것만으로도 나는 만족한다. (물론 운도 좋았다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;돌이켜보면 내가 먼저 어느 회사를 알아보고 이직한 적은 아직까지 단 한 번도 없다. 두 번의 이직 모두 먼저 회사로부터 연락을 받았고, 나는 여기에 응하여 최선을 다했을 뿐이다. 필자 본인 스스로는 남들과 다르다고 생각하지 않고, 실력이 뛰어다고 생각하지 않는다. 그런데 이렇게 두 번의 기회를 얻었다는 것은 두 회사 모두 필자의 끈기와 열정을 높게 평가해 준 것이 아닐까?&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;능력이란 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이런 경험을 하다보니 그 사람이 갖고 있는 능력이란 무엇일까 싶기도 하다. 대한민국 사회에서는 그 사람의 능력을 평가하는데 있어 &quot;스펙&quot;을 굉장히 중요시 하는 경향이 있다고 생각한다. 대표적으로 학력 그리고 출신 학교 등이 있다. &quot;좋은 대학을 나와야 성공한다&quot; 라는 말이 괜히 나온 것이 아니다. 당연히 좋은 대학을 가기 위해 수많은 노력이 필요하고, 이후 뛰어난 역량을 기반으로 사회에 기여할 수 있는 훌륭한 인재가 탄생하는 것도 맞는 말이다. 이처럼 학력만큼 객관적으로 판단할 수 있는 지표는 없다고 생각하지만, 모순적이게도 학력은 결국 학력일 뿐, 이것만 가지고 그 사람의 능력을 모두 평가하기에는 부족하다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;아이러니 하게도 상대방을 평가하는 것을 넘어 스펙이 부족하다고 남을 깎아내리고 조롱하는 모습을 온라인 커뮤니티 상에서 많이 봤다. 예를 들면 댓글에서 통해 어느 학교를 나왔느니, 그런 학교 나온게 자랑이라느니 서로 헐뜯고 남을 깎아 내리는데 시간을 버리는 사람들 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이처럼 온라인이라는 공간에서 익명성이라는 특성 뒤에 본인을 숨긴채 부정적인 영향을 미치는 사람들이 너무 많다. 도대체 그놈의 스펙이 무엇이라고 상대방의 잠재력과 가능성을 무시하고 그 사람의 경험들과 인생 자체를 폄하할 수 있을까 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;요즘 사람들은 소위 말하는 &quot;보여주기식&quot; 과시 성향이 있는 것인지 남들에게 보여줄 수 있는 것들만 쫒고, 남을 깎아내리면서까지 조금이라도 더 잘나보이려고 애를 쓰는 것 같기도 하다. 그럴 시간에 스스로의 가치를 올리기 위해 배움을 더하거나, 새로운 경험들에 투자하는 것이 본인에게나, 주변에게나 훨씬 의미 있을 것이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;좋은 대학을 나오고, 좋은 기업을 다니려고 하는 목적(물론 사람마다 목적은 다를 수 있다)이 무엇인지 다시 생각해보면 일반적으론 경제적인 풍요로움을 통해 &quot;행복&quot;을 찾기 위함이 아닐까? 스펙이 조금 뒤쳐지더라도, 경제적으로 아주 여유롭지 못하더라도, 스스로가 행복하다고 느끼면 그것 만으로 충분한게 아닐까? 왜 남의 행복을 짓밟아가면서 남들보다 높은 위치로 올라가고 과시해야 하는지 필자 본인은 전혀 이해를 할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;개인적으론 남들에게 보여주기 위한 사람보단, 본인 스스로가 즐거워하고 잘 할 수 있는 일을 하면서 좋은 영향을 끼치는 그런 사람들이 우리 주변에 많이 있어야 한다고 생각한다. 이런 사람들은 분명 자연스레 좋은 대우를 받고, 그만큼의 보상이 뒤따르고 있을 것이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;우리 사회를 보면 뛰어난 능력도 없고 남들만큼 노력을 하지도 않으면서 최저 시급 받는다고 불평하는 사람들(그리고 마냥 최저 시급을 올려달라고 요구하는), 막연히 많은 보상을 노리지만 막상 그만큼의 노력을 하지 않는 사람들, 본인의 부족함을 인지하지 못한채 사회에 대한 편향된 비판을 쏟아내는 사람들이 종종 보인다. 개인적으로는 절대 옆에 두고 싶지 않은 부류의 사람들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;보상이 많다는 것은 그만큼 그 사람의 역량이 사회적으로 인정받고 있다는 의미를 내포하고 있고, 이 보상 뒤에는 그 사람이 견뎌온 피나는 노력과 지나쳐온 수 없이 많은 경험들이 가려져 있을 뿐이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bThfGV/btsCMni8yRb/PpvN7mJZoOkVXioZuypIzK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bThfGV/btsCMni8yRb/PpvN7mJZoOkVXioZuypIzK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bThfGV/btsCMni8yRb/PpvN7mJZoOkVXioZuypIzK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbThfGV%2FbtsCMni8yRb%2FPpvN7mJZoOkVXioZuypIzK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;720&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;725&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;결론을 이야기 하자면, 보상은 본인 역량에 따른 댓가이기에 보상을 먼저 노리는 자세는 잘못 되었다고 생각한다. 우리 사회는 자본주의 사회이기 때문에, 본질은 본인의 역량을 키우고 그에 맞는 보상을 받아가는 것이지, 먼저 막연한 보상을 노리는 것은 잘못되었다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;왜 이런 이야기를 하냐면, 온라인상에서 서로 물고 뜯는 한심한 모습을 너무 많이 보기도 했고, 토스 합류가 결정된 후 주변의 시선이 조금 달라진 것을 느꼈다. 필자 본인은 토스라는 이름을 쫒아온 것이 아니라 단지 개발하는 것이 즐거워서 스스로 많은 시간을 투자했을 뿐이고, 운 좋게 좋은 기회를 얻었을 뿐인데 이 네임밸류 하나만으로 나의 평가가 달라졌다는 것이 다소 아쉽기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;앞으로의 계획&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;앞으로 본인은 지금처럼 &quot;즐겁게&quot; 개발하고 배움을 이어나가려고 한다. 필자의 목표라고 하면 우리 사회에 긍정적인 영향과 가치를 제공하는 것이다. 이를 달성하기 위한 수단은 개발이 될 것이다. 실력이 늘고 열심히 하는만큼 좋은 대우를 받고, 보상도 자연스레 따라올 것이라고 생각하기에 남들에게 잘 보이기 위한 마음은 전혀 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;최근 여러가지 작업을 하고 일을 하면서 부족함을 많이 느끼고 있지만 그중 하나가 외국어(영어)이다. 아무래도 특성화 고등학교를 나오다 보니 남들만큼 영어를 잘 하지는 못한다. 업무를 하다가 찾게되는 레퍼런스의 경우 깊이 파면 팔수록 영어로 된 자료들이 많은데, 앞으로 일을 하는데 있어 영어가 필자의 발목을 잡는 것을 원하지 않기 때문이다. 그래서 유창한 수준은 아니더라도 원활하게 읽고, 내 생각을 글로 풀어낼 수 있을 수준만큼이라도 공부를 열심히 해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;바쁜 일들도 많이 생기고 여유로운 시간이 점차 없어지겠지만, 지금처럼 개인적인 사이드 프로젝트나 오픈소스 활동도 계속 진행할 계획이고 무엇보다 내 위치에서 최선을 다할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>칼럼/회고</category>
      <author>이근둥</author>
      <guid isPermaLink="true">https://codevkr.tistory.com/134</guid>
      <comments>https://codevkr.tistory.com/134#entry134comment</comments>
      <pubDate>Sat, 9 Dec 2023 22:02:41 +0900</pubDate>
    </item>
    <item>
      <title>DROP + BIIP MT3 EXTENDED 2048 키캡 리뷰 &amp;amp; 후기</title>
      <link>https://codevkr.tistory.com/133</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ucjXb/btsAMrgK911/EDma9xbD4bCFBcgjXKCZdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ucjXb/btsAMrgK911/EDma9xbD4bCFBcgjXKCZdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ucjXb/btsAMrgK911/EDma9xbD4bCFBcgjXKCZdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FucjXb%2FbtsAMrgK911%2FEDma9xbD4bCFBcgjXKCZdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;720&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커스텀 키보드를 살펴보던 중 &lt;a href=&quot;https://drop.com/home&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DROP&lt;/a&gt;에서 이쁜 키캡을 발견하게 되어 구매하게 되었다. DROP은 처음 알게 된 해외 키보드 용품 쇼핑몰인데, 배송 대행지 없이 바로 직구가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 구매한 키캡은 &lt;u&gt;DROP + BIIP MT3 EXTENDED 2048이다.&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kit: Alphas ($52)&lt;/li&gt;
&lt;li&gt;Kit: Modifiers ($52)&lt;/li&gt;
&lt;li&gt;Kit: Accent Modifiers ($52)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배송비를 포함하여 총 &lt;b&gt;$186&lt;/b&gt;를 지불했다. + 관세 그리고 수수료로 약 &lt;b&gt;22,000원&lt;/b&gt; 가량 추가로 지불했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://drop.com/buy/drop-biip-mt3-extended-custom-keycap-set?defaultSelectionIds=969821&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://drop.com/buy/drop-biip-mt3-extended-custom-keycap-set?defaultSelectionIds=969821&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1700671876687&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;massdrop:group_buy&quot; data-og-title=&quot;Drop + biip MT3 Extended 2048 Custom Keycap Set | Dye-subbed PBT Keycaps | Custom Keycaps&quot; data-og-description=&quot;Our MT3 Extended 2048 keycap set features a minimal icons-based design with cream-colored alphas, black legends, and bright color accent keys.&quot; data-og-host=&quot;drop.com&quot; data-og-source-url=&quot;https://drop.com/buy/drop-biip-mt3-extended-custom-keycap-set?defaultSelectionIds=969821&quot; data-og-url=&quot;https://drop.com/buy/drop-biip-mt3-extended-custom-keycap-set&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/5LYvQ/hyUCdv45MN/4pRTq7zwOQemosIx6Wlcek/img.jpg?width=600&amp;amp;height=315&amp;amp;face=0_0_600_315,https://scrap.kakaocdn.net/dn/tFik1/hyUB6jrpEr/m5hWUz8WCjFlf9ulLJZjvK/img.jpg?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/eFrp5/hyUymVMVXD/DOogUvCd3wfuPO6QXzxthk/img.jpg?width=1080&amp;amp;height=720&amp;amp;face=0_0_1080_720&quot;&gt;&lt;a href=&quot;https://drop.com/buy/drop-biip-mt3-extended-custom-keycap-set?defaultSelectionIds=969821&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://drop.com/buy/drop-biip-mt3-extended-custom-keycap-set?defaultSelectionIds=969821&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/5LYvQ/hyUCdv45MN/4pRTq7zwOQemosIx6Wlcek/img.jpg?width=600&amp;amp;height=315&amp;amp;face=0_0_600_315,https://scrap.kakaocdn.net/dn/tFik1/hyUB6jrpEr/m5hWUz8WCjFlf9ulLJZjvK/img.jpg?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/eFrp5/hyUymVMVXD/DOogUvCd3wfuPO6QXzxthk/img.jpg?width=1080&amp;amp;height=720&amp;amp;face=0_0_1080_720');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Drop + biip MT3 Extended 2048 Custom Keycap Set | Dye-subbed PBT Keycaps | Custom Keycaps&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Our MT3 Extended 2048 keycap set features a minimal icons-based design with cream-colored alphas, black legends, and bright color accent keys.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;drop.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5Ps5E/btsAIaOzqqj/wk1k31PRe1hOZi3d2KoHik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5Ps5E/btsAIaOzqqj/wk1k31PRe1hOZi3d2KoHik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5Ps5E/btsAIaOzqqj/wk1k31PRe1hOZi3d2KoHik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5Ps5E%2FbtsAIaOzqqj%2Fwk1k31PRe1hOZi3d2KoHik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;540&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;11월 9일에 주문 및 결제를 진행했고, 배송은 11월 23일에 도착했다. 해외 직구이기 때문에 여유롭게 기다리는 것이 좋을 듯하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mc0z0/btsALZ5PQOQ/Md2yg1XOjjt5Buj2NSjSk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mc0z0/btsALZ5PQOQ/Md2yg1XOjjt5Buj2NSjSk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mc0z0/btsALZ5PQOQ/Md2yg1XOjjt5Buj2NSjSk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmc0z0%2FbtsALZ5PQOQ%2FMd2yg1XOjjt5Buj2NSjSk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;540&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키캡에 대해 간략히 설명하자면, PBT 재질의 키캡이고, MX 스위치와 호환된다. 프로파일은 이름에서도 알 수 있듯이 MT3 프로파일의 키캡이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자 본인은 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;체리 프로파일 키캡은 많이 사용해 보았으나, &lt;/span&gt;MT3 프로파일 키캡을 사용해 본 적은 없다. 여러 가지 후기들을 찾아보면 키캡의 경사와 높이가 상당하다는 이야기를 쉽게 찾아볼 수 있다. 잘 맞지 않으면 어떡하나 싶었는데, 지금 변경한 키캡으로 글을 작성하고 있고 나름 만족스럽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 타건을 해보니 키캡의 경사와 높이 차이가 가장 크게 체감되었고, 손가락 끝 부분이 닿는 키캡의 윗부분이 좁다는 느낌이 든다. 다만, 키캡 상단이 움푹 파여있어 적응만 하면 안정적으로 타건이 가능할 것으로 보인다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfezX1/btsAL6qpd4F/ihIVwaI2tQZx64iXHq8Bb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfezX1/btsAL6qpd4F/ihIVwaI2tQZx64iXHq8Bb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfezX1/btsAL6qpd4F/ihIVwaI2tQZx64iXHq8Bb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfezX1%2FbtsAL6qpd4F%2FihIVwaI2tQZx64iXHq8Bb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;540&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키캡을 바꿀 키보드는 집에서 사용하고 있는 &lt;u&gt;&lt;b&gt;Keychron Q60&lt;/b&gt;&lt;/u&gt;(스위치: 게이트론 &lt;b&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;저소음 적축&lt;/span&gt; &lt;/b&gt;+ &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;공장 윤활&lt;/span&gt;)이다. 기본 키캡의 퀄리티도 좋고 타건감도 준수하지만, 커스터마이징이 목적이기에 바꾸게 되었다. 무각 키캡으로 꾸밀까 했는데, 이미 회사에서 사용하고 있는 해피해킹(HHKB)이 무각이기에 색다른 시도를 하게 되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k5hqn/btsANJ81tIr/X4dcV7lmmYtD25JMJ7J2N1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k5hqn/btsANJ81tIr/X4dcV7lmmYtD25JMJ7J2N1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k5hqn/btsANJ81tIr/X4dcV7lmmYtD25JMJ7J2N1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk5hqn%2FbtsANJ81tIr%2FX4dcV7lmmYtD25JMJ7J2N1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;540&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 키캡을 제거하고, 제거한김에 속에 있던 먼지도 모두 청소해 주었다. 이제 새로 구매한 키캡을 위치에 맞게 잘 끼우면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MCC3u/btsALZSiU1G/DCHKXLnOE4biHz44idaQ90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MCC3u/btsALZSiU1G/DCHKXLnOE4biHz44idaQ90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MCC3u/btsALZSiU1G/DCHKXLnOE4biHz44idaQ90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMCC3u%2FbtsALZSiU1G%2FDCHKXLnOE4biHz44idaQ90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;540&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuxSPB/btsAM0XwhfS/I6rgvtK7qc89Ujisc4R060/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuxSPB/btsAM0XwhfS/I6rgvtK7qc89Ujisc4R060/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuxSPB/btsAM0XwhfS/I6rgvtK7qc89Ujisc4R060/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuxSPB%2FbtsAM0XwhfS%2FI6rgvtK7qc89Ujisc4R060%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;540&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하얀 베이스 키캡에 포인트 키캡들로 구성해 보았다. 사진으로 보았을 땐 색상이 진하게 보이지만, 실제로는 연한 파스텔 색에 더 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 가지 아쉬운 부분이라면, 키보드 알루미늄 프레임은 흰색이 아니라 레트로 화이트(?)이기 때문에 약간 누리끼리해 보이는데, 최대한 신경 쓰지 않으려고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;1500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BowFs/btsAL5yfP44/PilAZXnCvNyN7f6mIxkeSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BowFs/btsAL5yfP44/PilAZXnCvNyN7f6mIxkeSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BowFs/btsAL5yfP44/PilAZXnCvNyN7f6mIxkeSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBowFs%2FbtsAL5yfP44%2FPilAZXnCvNyN7f6mIxkeSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;500&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;1500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키캡을 측면에서 본 사진이다. 확실히 체리 프로파일 키캡보다 경사가 가파르고 높이도 훨씬 높은 모습을 볼 수 있다. 처음 사용해 보는 프로파일이지만, 본인 기준 첫 타건 느낌은 만족스럽다. 하루 이틀 타건하다보면 금방 적응할 것 같다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DROP에서 꽤 많은 키보드 상품을 판매하고 있어 앞으로도 종종 이용하려고 한다. 제품에 대해 궁금한 점이 있다면 댓글 남겨주길 바라며, 이번 글은 여기서 마무리하도록 하겠다.&lt;/p&gt;</description>
      <category>칼럼/제품 리뷰</category>
      <author>이근둥</author>
      <guid isPermaLink="true">https://codevkr.tistory.com/133</guid>
      <comments>https://codevkr.tistory.com/133#entry133comment</comments>
      <pubDate>Thu, 23 Nov 2023 02:26:26 +0900</pubDate>
    </item>
    <item>
      <title>esbuild 기반 React Native 번들러 개발기</title>
      <link>https://codevkr.tistory.com/132</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cover_rne.png&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B6ZIz/btszAnzwdGD/SweWrHMOK25rHQiPuQF7eK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B6ZIz/btszAnzwdGD/SweWrHMOK25rHQiPuQF7eK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B6ZIz/btszAnzwdGD/SweWrHMOK25rHQiPuQF7eK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB6ZIz%2FbtszAnzwdGD%2FSweWrHMOK25rHQiPuQF7eK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;320&quot; height=&quot;320&quot; data-filename=&quot;cover_rne.png&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;esbuild&amp;nbsp;기반의 React Native 번들러를 개발하게 된 계기와 개발을 진행하면서 겪었던 수많은 문제들 그리고 이들을 해결하기까지의 과정을 간략히 소개하려고 한다. 자체 개발한 번들러는 아래 링크에서 확인할 수 있고, 오픈소스로 모두 공개되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://react-native-esbuild.vercel.app&quot;&gt;https://react-native-esbuild.vercel.app&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1698754717328&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Getting Started | React Native Esbuild&quot; data-og-description=&quot;An extremely fast bundler + React Native&quot; data-og-host=&quot;react-native-esbuild.vercel.app&quot; data-og-source-url=&quot;https://react-native-esbuild.vercel.app&quot; data-og-url=&quot;https://react-native-esbuild.vercel.app/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/49PUc/hyUnPPOGCM/AUraQNb69RbIsbFcyCK2DK/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640,https://scrap.kakaocdn.net/dn/b3jaZc/hyUnWIaEtm/56HK3yJnqsuKaXJUQKU7O1/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640,https://scrap.kakaocdn.net/dn/c7Hj3Y/hyUlvFdssE/NZZsG5fuJS7KSDBMEyEqMk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://react-native-esbuild.vercel.app&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://react-native-esbuild.vercel.app&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/49PUc/hyUnPPOGCM/AUraQNb69RbIsbFcyCK2DK/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640,https://scrap.kakaocdn.net/dn/b3jaZc/hyUnWIaEtm/56HK3yJnqsuKaXJUQKU7O1/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640,https://scrap.kakaocdn.net/dn/c7Hj3Y/hyUlvFdssE/NZZsG5fuJS7KSDBMEyEqMk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Getting Started | React Native Esbuild&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;An extremely fast bundler + React Native&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;react-native-esbuild.vercel.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;번들러를 개발하면서 느낀 점은 생각했던 것보다 넓고 깊은 지식을 요구한다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;번들러가 동작하는 방식, 트랜스파일링과 최적화 과정, 번들 코드가 실행되는 런타임 그리고 플랫폼에 대한 이해, 프로그래밍 언어의 특성 등 일반적인 상황에서 접하기 어려운 내용들이 요구된다는 것을 이번 계기를 통해 제대로 느낄 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 글은 React Native의 숨겨진 부분들을 정리하여 소개한 내용인데, 이를 참고하면 본 글을 이해하는데 도움이 될 것이다.&lt;/p&gt;
&lt;figure id=&quot;og_1698754730747&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;React Native Under The Hood&quot; data-og-description=&quot;새로운 시리즈 게시글을 작성하기에 앞서 소개하고자 한다. 이번 시리즈의 제목은 React Native Under The Hood 이다. React Native는 보다 쉽게 여러가지 플랫폼의 애플리케이션을 만들 수 있도록 도와준&quot; data-og-host=&quot;geundung.dev&quot; data-og-source-url=&quot;https://geundung.dev/126&quot; data-og-url=&quot;https://geundung.dev/126&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/OKrK4/hyUnJPDfm2/MjOcYf6qTGJKqnBFgk0g71/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/beUCdc/hyUnLNqnmW/sI5vxDKWUH94JjzkZKr451/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bbRHlk/hyUnQVuYlK/WkzYmrOIdnIWoQBK6H0Nd1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://geundung.dev/126&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://geundung.dev/126&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/OKrK4/hyUnJPDfm2/MjOcYf6qTGJKqnBFgk0g71/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/beUCdc/hyUnLNqnmW/sI5vxDKWUH94JjzkZKr451/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/bbRHlk/hyUnQVuYlK/WkzYmrOIdnIWoQBK6H0Nd1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;React Native Under The Hood&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;새로운 시리즈 게시글을 작성하기에 앞서 소개하고자 한다. 이번 시리즈의 제목은 React Native Under The Hood 이다. React Native는 보다 쉽게 여러가지 플랫폼의 애플리케이션을 만들 수 있도록 도와준&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;geundung.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프론트엔드 생태계와 번들러&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거 자바스크립트로 작성한 코드를 단순히 브라우저에서 로드하여 페이지에 동적인 기능을 제공했던 시절이 있었다. 기본적인 버튼 클릭, 입력 필드 처리 정도에 불과했지만, 자바스크립트 언어의 변화 그리고 &lt;a href=&quot;https://nodejs.org/en&quot;&gt;Node.js&lt;/a&gt; 의 등장 등 여러 가지 일들이 생기면서 오늘날 자바스크립트 생태계는 엄청나게 커졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 개발자라면 빠르게 변화하는 생태계를 누구보다 더 잘 느끼고 있을 것이다. 여러가지 라이브러리와 프레임워크들이 쏟아져 나오고, 심지어 개발을 위한 도구들도 넘쳐나고 있다. 그 중 현대 프론트엔드 개발에 있어 반드시 필요한 번들러도 포함되는데,현재 널리 사용되고 있는 번들러는 &lt;a href=&quot;https://webpack.js.org&quot;&gt;Webpack&lt;/a&gt;, &lt;a href=&quot;https://rollupjs.org&quot;&gt;Rollup&lt;/a&gt;, &lt;a href=&quot;https://turbo.build/pack&quot;&gt;Turbopack&lt;/a&gt;, &lt;a href=&quot;https://esbuild.github.io&quot;&gt;esbuild&lt;/a&gt;, &lt;a href=&quot;https://vitejs.dev&quot;&gt;Vite&lt;/a&gt; 등이 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://reactnative.dev&quot;&gt;React Native&lt;/a&gt; 애플리케이션의 경우 일반적인 웹 프론트엔드 애플리케이션과 같다고 볼 수는 없지만, 자바스크립트 기반 그리고 &lt;a href=&quot;https://react.dev&quot;&gt;React&lt;/a&gt; 를 축으로 하여 개발한다는 부분은 어느 정도 일치한다. 지금처럼 거대하고 잘 활성화되어있는 자바스크립트 생태계(Eco system)에서 개발할 수 있다는 것은 분명 큰 장점이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 도 코드를 작성한 후 실제 런타임(엔진)에서 실행하기 전에 번들링 과정을 거치게 되는데 이때 번들러가 필요하다. React Native의 경우 기본적으로 &lt;a href=&quot;https://facebook.github.io/metro&quot;&gt;Metro&lt;/a&gt; 라고 하는 번들러를 기반으로 번들링 되도록 구성되어 있으며, Meta(구 Facebook)에서 React Native 프로젝트를 타겟 한 자바스크립트 번들러이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Metro 번들러의 한계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 서론이지만 벌써 여러 가지 번들러 이름이 언급되었다. 그중 Metro 도 포함되는데 React Native 애플리케이션을 개발해 본, 혹은 개발하고 있는 개발자라면 여러 문제점을 겪어본 경험이 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자 본인의 경우에는 팀 내에서 빌드시 여러 가지 이슈가 있어 번들링 전에 항상 캐시를 제거(&lt;code&gt;--reset-cache&lt;/code&gt;)하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라이브러리 업데이트 같은 작업을 하지 않았음에도 종종 번들이 꼬이는 문제가 발생하여 조치한 사항인데, 이로 인해 Metro를 실행시키고 최초 번들링을 하게 되면 꽤 오랜 시간이 소요된다. 캐싱을 통해 누릴 수 있는 시간 단축이라는 생산성 향상 효과를 전혀 누리지 못하고 있는 상태이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Metro는 모듈 참조(Resolve) 시 상당히 느슨(Loose)하게 처리하는데, 존재하지 않는 대상을 &lt;code&gt;import&lt;/code&gt; 할 경우 빌드 타임에 이를 알려주지 않는다. 이는 개발자가 인지하지 못한 상태로 빌드하고, 프로덕션 환경에 배포까지 하게 될 경우 예기치 못한 충돌을 유발하는 원인이 되기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 빌드 속도이다. React Native가 갖고 있는 플랫폼 특성상 코드를 변환하는 과정은 반드시 필요한데, Metro는 이를 달성하기 위해 &lt;a href=&quot;https://babeljs.io&quot;&gt;Babel&lt;/a&gt; 을 주로 사용하고 있다. Babel 덕분에 프론트엔드 생태계가 여기까지 성장했다고 해도 과언이 아닐 정도로 훌륭한 도구임은 분명하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 오늘날의 개발 환경에서 상대적으로 느린 성능을 보여주고 있는데, Metro에서는 React Native 런타임에서 코드를 실행시킬 수 있는 형태로 변환하기 위해 수많은 Babel 플러그인들을 사용하고 있다. 프로덕션 애플리케이션은 수천, 수만 개의 모듈(코드)들이 존재하는데 이를 일일이 변환하고 있으니 느린 것은 당연하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐싱 이슈와 모듈 참조, 그리고 속도 문제가 있다. 그 외 Tree Shacking을 지원하지 않는 등 여러 기능적인 부족함 또한 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;토스(Toss)&lt;/b&gt;&lt;/span&gt;에서도 React Native 애플리케이션을 개발하며 겪었던 불편한 사항을 소개한 영상이 있는데, 여기에서도 번들링 과정과 Metro가 언급되고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(토스ㅣSLASH 23 - 달리는 토스 앱에 React Native 엔진 더하기)&lt;/b&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=6H9WQDRFZYg&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/bZsEtJ/hyUlrpgnjR/IMxkuXBmOyOHwyAHjKzIB1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=962_362_1052_460&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/6H9WQDRFZYg&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발하기에 앞서&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자 본인은 앞서 소개한 여러 가지 문제들이 개선된다면 분명 개발 경험이 크게 향상될 것이라고 생각했다. Metro의 경우 이미 거대한 프로젝트 중 하나이고 Metro 내부 팀도 성능을 개선하기 위해 swc를 도입하는 등 개선 작업을 진행하고 있지만, 구조적으로 단기간에 개선되기에는 시간이 걸릴 것이라고 생각했기에 직접 React Native 번들러를 개발해보자 라는 무모한 계획을 세우게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 목표는 아래와 같이 설정했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐싱 문제 해결 &amp;amp; 빌드 속도 개선으로 개발 경험 향상&lt;/li&gt;
&lt;li&gt;기존 React Native CLI 커맨드와 호환되는 도구 제공 (대체 가능하도록)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필자는 여러가지 사이드 프로젝트를 진행하면서 구성해 본 Webpack, Rollup 그리고 Rust 기반의 &lt;a href=&quot;https://www.rspack.dev&quot;&gt;Rspack&lt;/a&gt;를 사용해 본 경험이 있었고, 머릿속에는 주요 번들링 과정은 어차피 잘 만들어진 번들러가 수행하니 &quot;그냥 가져다가 사용하면 되겠다&quot;라는&lt;/p&gt;
&lt;p&gt;&lt;del&gt;어리석은&lt;/del&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;생각을 했었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 수많은 번들러 중 어떤 번들러를 사용할 것인가에 대해 고민이 필요했다. 주목적은 빌드 속도 개선인데, 실험적이지만 esbuild 가 부가적인 기능을 배제하는 대신 최고의 성능을 추구한다는 목표를 갖고 있다는 것을 알게 되었다. 그리고 이미 토스에서 esbuild 기반의 빌드 시스템 구현을 성공했다는 선례도 존재했기에 기반 번들러를 esbuild로 선정하게 되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;고려 사항&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;번들링을 수행하고 실제 애플리케이션에 실행시키는 데 있어 미리 인지하고 있어야 하는 사항들이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native는 기본적으로 &lt;a href=&quot;https://hermesengine.dev&quot;&gt;Hermes&lt;/a&gt; 그리고 JSC 런타임(엔진)을 지원하는데, 일단 Hermes는 ES6를 완전하게 지원하지 않고, 글 작성하는 시점을 기준으로 치명적인 블록 스코프 버그가 있다. 최근 React Native의 기본 런타임이 Hermes로 변경되었기에 기본적으로 Hermes를 지원하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hermes의 ECMA 스펙 이슈 말고도 고려해야 할 부분이 있는데, 바로 일부 Meta(구 Facebook)의 라이브러리 혹은 React Native 생태계의 경우 정적 타입 분석을 위해 &lt;a href=&quot;https://flow.org&quot;&gt;Flow&lt;/a&gt;를 사용한다는 것이다. 파일 확장자는 자바스크립트이지만, 실제 구문은 전혀 다르다. 이 역시 트랜스파일 과정에서 모두 제거해주어야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구조 설계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 가지 레퍼런스를 조사하고, React Native 그리고 Metro 내부 코드를 들여다보며 어떤 형태로 구조를 가져갈지 설계를 진행했다. 구상한 구조를 큰 그림으로 보면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2612&quot; data-origin-height=&quot;1498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ao6Fh/btszzCX1Irp/SLHtJjtxm5nqwGTkItWQG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ao6Fh/btszzCX1Irp/SLHtJjtxm5nqwGTkItWQG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ao6Fh/btszzCX1Irp/SLHtJjtxm5nqwGTkItWQG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAo6Fh%2FbtszzCX1Irp%2FSLHtJjtxm5nqwGTkItWQG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;413&quot; data-origin-width=&quot;2612&quot; data-origin-height=&quot;1498&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;소스코드와 에셋(이미지)등의 파일은 esbuild를 통해 로드하고, 코드 변환이나 에셋 전처리 등의 여러가지 커스텀 작업들은 플러그인으로 구현하고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;esbuild에는 기본적으로 &lt;a href=&quot;https://esbuild.github.io/api/#loader&quot;&gt;Loader&lt;/a&gt;가 구현되어 있는데, 자바스크립트 Loader의 경우 아쉽게도 ES6 미만을 지원하지 않는다. 즉, esbuild의 자바스크립트 Loader는 사용할 수 없다. 그래서 대안으로 선택한 것이 &lt;a href=&quot;https://swc.rs&quot;&gt;swc&lt;/a&gt;이다. Rust 언어 기반의 트랜스파일러인데 Babel을 대체할 수 있는 대안으로 떠오르고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;swc는 기본적으로 ES6 미만 타겟으로의 트랜스파일링을 지원하기에 좋은 방안이 될 것이라고 생각했다. 그리고 추가로 고려해야 할 Flow 구문의 경우 Babel 플러그인을 통해 처리할 수 있지만, Babel을 사용하는 순간 성능이 크게 떨어지기에, 보다 훨씬 빠른 &lt;a href=&quot;https://github.com/alangpierce/sucrase&quot;&gt;sucrase&lt;/a&gt; 라는 대안을 찾았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 ES6 미만으로 변환하는 작업은 swc, Flow 구문 제거는 sucrase를 사용하려고 한다. 그 외 변환할 수 없는 경우에는 Metro와 동일하게 Babel을 통해 처리하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 esbuild는 모듈 의존 관계를 분석하여 Tree Shaking을 진행하고, Minify, Sourcemap 생성 등의 작업을 끝으로 번들링 과정을 마무리하도록 구상했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;번들러 개발 시작&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발을 시작하기에 앞서 GitHub에 저장소를 생성하고 머릿속으로 구상한 내용을 기준으로 Yarn workspace 기반의 모노레포를 구성했다. 모노레포는 주요 기능들을 모듈화 하고 패키지로 분리하여 관리하기 위함이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지 이름은 &lt;code&gt;@react-native-esbuild&lt;/code&gt; 으로 결정했고, 패키지별로 이름을 갖도록 구성했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1956&quot; data-origin-height=&quot;1786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dYCMMn/btszySUjKjQ/bVYS5FguYA20QL5dmlDL1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dYCMMn/btszySUjKjQ/bVYS5FguYA20QL5dmlDL1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dYCMMn/btszySUjKjQ/bVYS5FguYA20QL5dmlDL1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdYCMMn%2FbtszySUjKjQ%2FbVYS5FguYA20QL5dmlDL1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;657&quot; data-origin-width=&quot;1956&quot; data-origin-height=&quot;1786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 패키지가 많이 늘어났지만 초기에는 아래와 같이 패키지가 나누어져 있었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주요 번들링 작업을 수행하는 &lt;code&gt;core&lt;/code&gt; 패키지&lt;/li&gt;
&lt;li&gt;CLI를 구현할 &lt;code&gt;cli&lt;/code&gt; 패키지&lt;/li&gt;
&lt;li&gt;개발 서버 기능을 구현할 &lt;code&gt;dev-server&lt;/code&gt; 패키지&lt;/li&gt;
&lt;li&gt;프로젝트에서 사용할 &lt;code&gt;utils&lt;/code&gt; 패키지&lt;/li&gt;
&lt;li&gt;전반적인 구성을 담당하는 &lt;code&gt;config&lt;/code&gt; 패키지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이외에도 타입스크립트, ESLint 등 기본적인 구성을 마친 후 본격적으로 개발을 진행하게 되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Module Resolution&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;번들링은 시작점(Entry point)으로부터 시작된다. React Native 프로젝트의 경우 프로젝트 루트 경로에 있는 &lt;code&gt;index.js&lt;/code&gt; 파일이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;index.js&lt;/code&gt; 에서 참조하고 있는 파일이 어디에 있는지 탐색하고, 탐색한 모듈은 또 어떤 모듈을 참조하고 있는지 반복적으로 찾아나간다. 이 과정을 Module Resolution이라고 하며, 과정이 마무리되면 모듈 간 의존 관계를 파악할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 프로젝의 경우 조금 특이한 부분이 있는데 일반적인 모듈(&lt;code&gt;.js&lt;/code&gt;, &lt;code&gt;.ts&lt;/code&gt;) 뿐만 아니라 &lt;a href=&quot;https://reactnative.dev/docs/platform-specific-code#platform-specific-extensions&quot;&gt;플랫폼 지정 확장자&lt;/a&gt;를 지원한다. 예를 들면 아래와 같은 경우이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G8ZcO/btszvg3dBvO/HpFc2ab2mqWCTOd1aqfUk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G8ZcO/btszvg3dBvO/HpFc2ab2mqWCTOd1aqfUk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G8ZcO/btszvg3dBvO/HpFc2ab2mqWCTOd1aqfUk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG8ZcO%2Fbtszvg3dBvO%2FHpFc2ab2mqWCTOd1aqfUk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;236&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;실제 파일의 확장자(&lt;code&gt;tsx&lt;/code&gt;) 앞에 특정 플랫폼이 포함되는 형태이다. Metro 번들러는 어느 플랫폼을 대상으로 번들링을 진행하는지에 따라 Resolution 과정에서 참조하는 파일이 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;esbuild에서는 옵션을 통해 쉽게 구현할 수 있는데, 아래와 같이 구성하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;2884&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qxbJJ/btszw8jkp1P/qwgrsyJMbCtb0MLdgl0Y11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qxbJJ/btszw8jkp1P/qwgrsyJMbCtb0MLdgl0Y11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qxbJJ/btszw8jkp1P/qwgrsyJMbCtb0MLdgl0Y11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqxbJJ%2Fbtszw8jkp1P%2FqwgrsyJMbCtb0MLdgl0Y11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;746&quot; data-origin-width=&quot;2784&quot; data-origin-height=&quot;2884&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구성을 살펴보면 코드의 시작점(&lt;a href=&quot;https://esbuild.github.io/api/#entry-points&quot;&gt;entryPoints&lt;/a&gt;)과 번들 결과로 저장(&lt;a href=&quot;https://esbuild.github.io/api/#outfile&quot;&gt;outfile&lt;/a&gt;)할 파일의 이름이 포함되어 있다. &lt;a href=&quot;https://esbuild.github.io/api/#main-fields&quot;&gt;mainFields&lt;/a&gt; 의 경우 모듈 참조 과정에서 사용되는데, 자세한 내용은 &lt;a href=&quot;https://geundung.dev/126&quot;&gt;처음에서 소개한 글&lt;/a&gt;의 번들러 파트를 참고하도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플랫폼 지정 확장자는 &lt;a href=&quot;https://esbuild.github.io/api/#resolve-extensions&quot;&gt;resolveExtenstions&lt;/a&gt;옵션 값으로 추가하면 된다. esbuild에는 기본적으로 구현된 &lt;a href=&quot;https://esbuild.github.io/api/#loader&quot;&gt;Loader&lt;/a&gt;가 있는데, 이는 Resolve 과정을 거친 모듈 혹은 파일을 처리하는 역할을 수행한다. Loader를 명시적으로 지정하지 않으면 확장자에 따라 기본 Loader로 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 번들 내에 포함되지 않는 대상(이미지 파일 등)은 &lt;code&gt;file&lt;/code&gt; Loader가 처리하도록 구성한 모습을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;resolveExtension&lt;/code&gt;에 추가한 확장자들은 기본 Loader에게 전달되는데, 우리는 소스코드를 React Native 런타임에서 구동 가능한 형태로 변환해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Plugins&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 프로젝트에 구현된 여러 플러그인들이 있는데, &lt;a href=&quot;https://esbuild.github.io/plugins&quot;&gt;esbuild&lt;/a&gt;의 플러그인은 아주 쉽게 구현할 수 있으며 강력하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;React_Native-RNE (2).png&quot; data-origin-width=&quot;1928&quot; data-origin-height=&quot;2628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XWoL1/btszyDQS1hD/U6osKbEkxwbY1aLyRKMEu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XWoL1/btszyDQS1hD/U6osKbEkxwbY1aLyRKMEu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XWoL1/btszyDQS1hD/U6osKbEkxwbY1aLyRKMEu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXWoL1%2FbtszyDQS1hD%2FU6osKbEkxwbY1aLyRKMEu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;682&quot; data-filename=&quot;React_Native-RNE (2).png&quot; data-origin-width=&quot;1928&quot; data-origin-height=&quot;2628&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;onResolve&lt;/code&gt;는 모듈 Resolution 과정에서 참조한 경로를 발견했을 때 트리거된다. 이 과정에서 모듈의 경로를 임의의 경로로 변경하는 등의 작업을 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;onLoad&lt;/code&gt; 는 모듈 Resolution 후 모듈의 Loader로 전달되기 전에 트리거 된다. 여기서 Loader로 전달하기 전에 모듈을 커스터마이징 할 수 있다. 현재 구현한 번들러의 경우 여기에서 코드를 변환하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인에 대한 자세한 내용은 esbuild 공식 문서를 참고하도록 하자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Transformer&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Resolution을 거쳐 코드가 Load 된 경우 본격적으로 트랜스파일링을 진행한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;React_Native-RNE (3).png&quot; data-origin-width=&quot;3136&quot; data-origin-height=&quot;1436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdzREe/btszwhgeTrJ/qBHPMINrnDIezXMNU1dHak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdzREe/btszwhgeTrJ/qBHPMINrnDIezXMNU1dHak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdzREe/btszwhgeTrJ/qBHPMINrnDIezXMNU1dHak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdzREe%2FbtszwhgeTrJ%2FqBHPMINrnDIezXMNU1dHak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;330&quot; data-filename=&quot;React_Native-RNE (3).png&quot; data-origin-width=&quot;3136&quot; data-origin-height=&quot;1436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 파일을 읽고, 코드를 변환하는  모듈로 전달하도록 구현했다. 코드 변환의 경우 앞서 이야기했던 것처럼 swc, sucrase를 통해 처리되고, 소개하지 않은 추가적인 처리 과정 또한 존재하는데 이를 파이프라인이라는 객체로 추상화하여 처리하도록 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파이프라인은 esbuild 번들링 과정뿐만 아니라 추후 소개할 Jest 트랜스포머에서도 다시 재사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이프라인을 거친 코드는 최종적으로 자바스크립트 형태의 코드를 반환한다. 자바스크립트 구문이기 때문에 esbuild로 결과를 반환할 때 자바스크립트 Loader를 사용하도록 명시적으로 추가해주고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 변환뿐만 아니라 폴리필(Polyfill) 적용, React Native 초기화 스크립트 주입 등 복잡하고 많은 과정이 필요한데 관련된 내용은 &lt;a href=&quot;https://geundung.dev/126&quot;&gt;처음에 소개한 글&lt;/a&gt;에서 확인 가능하다. 이 과정을 모두 거치면 자바스크립트로 변환된 번들 파일이 생성된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공적인 번들링까지의 과정도 사실 쉽지만은 않았다. 수많은 시행착오를 거쳐 정상적으로 번들링 되도록 구현했으나, 실제로 번들을 실행시켰을 때 발생한 런타임 에러 등, 더 많은 어려운 문제들이 기다리고 있었다. 필자 본인이 겪었던 몇 가지 문제들과 해결 방안을 간략히 소개하도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고로 소개할 이슈들은 Hermes 런타임에서 발생한 이슈이다)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;잘못된 블록 스코프 동작&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 번들은 자체 구현한 개발 서버(dev-server 패키지)를 통해 애플리케이션으로 전달되도록 구현했는데, 앱을 실행시켜 번들 을 로드했을 때 아래 에러를 마주했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;2796&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sdTLz/btszwi7k1Qt/ATWOOSKaFck8n62T29yUP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sdTLz/btszwi7k1Qt/ATWOOSKaFck8n62T29yUP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sdTLz/btszwi7k1Qt/ATWOOSKaFck8n62T29yUP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsdTLz%2Fbtszwi7k1Qt%2FATWOOSKaFck8n62T29yUP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;650&quot; data-origin-width=&quot;1290&quot; data-origin-height=&quot;2796&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러를 확인해보면 조금 이상한 부분이 보일 것이다. &lt;code&gt;React.createContext&lt;/code&gt;를 참조하여 호출하고 있는 코드에서 발생한 에러인데 값이 조금 이상하다. 함수가 아니라 &lt;code&gt;'18.2.0'&lt;/code&gt; 즉, 문자열이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 트랜스파일링 과정에 이슈가 있나 싶어 여러가지를 리서치 했었는데 관련된 내용을 찾지 못했다. 그래서 에러 내용에 포함되어있는 Stack trace를 통해 어느 지점에서 문제가 발생했는지 번들 코드를 직접 열어 살펴보았다. 호출 스택을 거슬러 올라가보아도 문제가 될만한 부분은 찾지 못했다. 아니, 정상적인 코드였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속 거슬러 올라가다 결국 첫 호출 스택에 도달했을 때, 조금 이상하게 동작하는 코드를 찾았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2078&quot; data-origin-height=&quot;1064&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FiMBl/btszuEwxVqP/w8WsKfiLL3KKYNgB6ZlBU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FiMBl/btszuEwxVqP/w8WsKfiLL3KKYNgB6ZlBU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FiMBl/btszuEwxVqP/w8WsKfiLL3KKYNgB6ZlBU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFiMBl%2FbtszuEwxVqP%2Fw8WsKfiLL3KKYNgB6ZlBU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;369&quot; data-origin-width=&quot;2078&quot; data-origin-height=&quot;1064&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;esbuild에서 빌드 런타임에 주입하는 모듈 함수 코드인데 강조해둔 부분을 잘 보자. 코드 자체는 객체의 속성을 다른 객체로 복사하는 역할을 수행하는 함수이다. 단순히 프로퍼티를 복사하는 것이 아니라 대상 객체의 &lt;code&gt;getter&lt;/code&gt; 로 원본 객체의 속성 값에 접근할 수 있도록 구현되어있는 모습을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 &lt;code&gt;for&lt;/code&gt; 반복문을 돌고 있는 &lt;code&gt;key&lt;/code&gt; 변수의 스코프가 함수 스코프라면 어떻게 될까? 반복이 모두 끝난 시점의 값을 들고 있을 것이다. 이 말인 즉슨, 객체의 마지막 속성 값에만 접근 가능한 상태가 된다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;let&lt;/code&gt; 키워드로 선언한 변수는 블록 스코프로 동작하는 것이 ES6의 사양이다. 그러나 Hermes에서는 위 코드를 실행했을 때 &lt;code&gt;let&lt;/code&gt; 키워드로 선언된 &lt;code&gt;key&lt;/code&gt; 변수가 블록 스코프를 갖는 것이 아니라 함수 스코프를 갖는 것처럼 동작하는 치명적인 버그가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;React.createContext&lt;/code&gt;가 잘못된 문자열 값을 참조하는 것이 문제였는데, &lt;code&gt;React.useState&lt;/code&gt; 등 모든 속성의 참조값은 마지막 속성의 값인 &lt;code&gt;'18.2.0'&lt;/code&gt;이 되어버리는 아이러니한 상황이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련된 이슈가 등록되었나 Hermes 저장소를 살펴보았는데, 동일한 원인으로 제보된 이슈가 존재했다. 그러나 아직 Hermes 정식 릴리즈로 출시된 상황은 아니라 현재 글을 작성하고 있는 시점에도 여전히 블록 스코프 이슈는 해결되지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/facebook/hermes/issues/575&quot;&gt;https://github.com/facebook/hermes/issues/575&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1698755624292&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;Lacking support for ES6 block scoping &amp;middot; Issue #575 &amp;middot; facebook/hermes&quot; data-og-description=&quot;Bug Description There is a bug in Object.defineProperty that always returns the last set property regardless of what property is being accessed. I have run gradle clean and confirmed this bug does ...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/facebook/hermes/issues/575&quot; data-og-url=&quot;https://github.com/facebook/hermes/issues/575&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kkPXD/hyUlu7qjFq/krSZfalBjofkwD08F1K0A0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/facebook/hermes/issues/575&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/facebook/hermes/issues/575&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kkPXD/hyUlu7qjFq/krSZfalBjofkwD08F1K0A0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Lacking support for ES6 block scoping &amp;middot; Issue #575 &amp;middot; facebook/hermes&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Bug Description There is a bug in Object.defineProperty that always returns the last set property regardless of what property is being accessed. I have run gradle clean and confirmed this bug does ...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해서는 반복문 내에서 새로운 함수 스코프를 갖도록 수정하면 되는데, esbuild 에서 주입해주는 코드였기에 외부에서 커스터마이징이 불가했다. esbuild &lt;a href=&quot;https://github.com/evanw/esbuild/blob/v0.19.5/internal/runtime/runtime.go#L194-L217&quot;&gt;구현 코드&lt;/a&gt;를 살펴보니 다행히도 특정 옵션을 활성화 하면 &lt;code&gt;let&lt;/code&gt; 을 사용하지 않고 &lt;code&gt;var&lt;/code&gt; 기반의 변수로 동작하도록 구현된 것을 확인했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;1412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brDkSs/btszyOEGTpY/pmsAL3tXVC6AgxlFX7MGuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brDkSs/btszyOEGTpY/pmsAL3tXVC6AgxlFX7MGuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brDkSs/btszyOEGTpY/pmsAL3tXVC6AgxlFX7MGuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrDkSs%2FbtszyOEGTpY%2FpmsAL3tXVC6AgxlFX7MGuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;371&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;1412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;esbuild&amp;nbsp;옵션 중 &lt;a href=&quot;https://esbuild.github.io/api/#supported&quot;&gt;supported&lt;/a&gt; 의 &lt;code&gt;for-of&lt;/code&gt; 를 지원하지 않도록 변경하면 문제가 되던 &lt;code&gt;__copyProp&lt;/code&gt; 의 블록 스코프 문제가 완벽하게 해결된다. 실제 프로젝트에 적용된 코드는 아래에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/leegeunhyeok/react-native-esbuild/blob/v0.1.0-beta.12/packages/core/lib/bundler/bundler.ts#L223&quot;&gt;https://github.com/leegeunhyeok/react-native-esbuild/blob/v0.1.0-beta.12/packages/core/lib/bundler/bundler.ts#L223&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;지역 스코프 최적화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 저런 이슈들이 많이 있었지만 모두 해결하여 번들 코드가 Hermes에서 구동될 수 있게 되었다. 기쁨도 잠시, esbuild를 통해 생성한 번들을 실행시켰을 때 심각한 성능 문제가 발생한다는 것을 발견했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱을 처음 실행 시켰을 때 번들을 로드하고 화면을 렌더링하게 되는데, 일반적인 상황이라면 수 초(2~3초) 내로 끝나야 한다. 그러나 20초가량 흰 화면 상태에 머물러 있다가 화면이 보여지는 심각한 성능 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 몇 가지 특이 사항이 있었는데, 20초 가량의 시간이 지나면 앱이 정상적으로 실행되고 더이상의 성능 이슈는 발생하지 않았다는 것, JSC 그리고 V8(Debugging Proxy) 런타임에서는 아무런 성능 문제 없이 잘 실행된다는 점이었다. 추가적으로 릴리즈 빌드에서 적용되는 Hermes 사전 바이트 코드 컴파일을 거쳐도 문제는 해결되지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 상황속에서 에러나 Stack trace 없이 수십만 줄의 번들 코드를 일일히 살펴본다는 것은 불가능에 가까웠다. 그래서 Chrome Debugger 를 켜서 초기 실행 시점에서 프로파일링(Profiling)을 진행하게 되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1540&quot; data-origin-height=&quot;858&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Mb0zt/btszAj4XzUb/fKwNaey3gxoJkA8VKFPXO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Mb0zt/btszAj4XzUb/fKwNaey3gxoJkA8VKFPXO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Mb0zt/btszAj4XzUb/fKwNaey3gxoJkA8VKFPXO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMb0zt%2FbtszAj4XzUb%2FfKwNaey3gxoJkA8VKFPXO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;401&quot; data-origin-width=&quot;1540&quot; data-origin-height=&quot;858&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로파일링을 통해 발견한 부분을 번들에서 찾으니 아래 코드에서 성능 문제가 발생하는 것임을 확인했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BMEZv/btszyILcv0P/kw3xjXK5wI4CHgHM9uPjx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BMEZv/btszyILcv0P/kw3xjXK5wI4CHgHM9uPjx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BMEZv/btszyILcv0P/kw3xjXK5wI4CHgHM9uPjx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBMEZv%2FbtszyILcv0P%2Fkw3xjXK5wI4CHgHM9uPjx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;179&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;code&gt;__esm&lt;/code&gt;, &lt;code&gt;__commonJS&lt;/code&gt; 둘 다 esbuild로부터 주입되는 함수인데, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames&quot;&gt;Object.getOwnPropertyNames&lt;/a&gt;가 Hermes에서 심각한 성능 문제를 일으키는 것이 아닌 이상 문제가 발생할 수 없는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록 스코프 버그처럼 혹시 화살표 함수 표현식에 성능 문제가 있는 것인가, 중첩된 함수 스코프 내의 매개변수에 접근할 때 느려지는 것인가, 매개변수 재할당 그리고 참조에 성능 문제가 있는 것인가 등 여러 가능성을 열어두고 해결하고자 노력했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속되는 가설과 검증, 그리고 완벽한 실패 과정을 반복하여 좌절하고 있던 중 esbuild 번들 포맷을 살펴보게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 당시 esbuild 번들 포맷을 IIFE(즉시 실행 함수 표현식)로 사용하도록 구성했기에 번들 코드 내에서 모든 모듈들은 아래와 같이 함수 내에 감싸여진 상태였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2800&quot; data-origin-height=&quot;1844&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bo0ZZJ/btszvDqAIuL/LKbgNs4FyJzgEkh8WqFgk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bo0ZZJ/btszvDqAIuL/LKbgNs4FyJzgEkh8WqFgk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bo0ZZJ/btszvDqAIuL/LKbgNs4FyJzgEkh8WqFgk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbo0ZZJ%2FbtszvDqAIuL%2FLKbgNs4FyJzgEkh8WqFgk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;395&quot; data-origin-width=&quot;2800&quot; data-origin-height=&quot;1844&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;여기서 이 IIFE를 제거한 후 번들을 실행해보니, 무슨 일이 있었나 싶을 정도로 빠른 속도로 번들이 실행되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2006&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/con2z1/btszyPDw7O6/7Zmuqf3S5jw8myodI2IwLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/con2z1/btszyPDw7O6/7Zmuqf3S5jw8myodI2IwLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/con2z1/btszyPDw7O6/7Zmuqf3S5jw8myodI2IwLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcon2z1%2FbtszyPDw7O6%2F7Zmuqf3S5jw8myodI2IwLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;260&quot; data-origin-width=&quot;2006&quot; data-origin-height=&quot;724&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;단지 감싸고 있는 함수를 제거했을 뿐인데, 심각했던 성능 문제가 한 번에 해결되었다. 한편으론 안도의 한숨을 내쉬었지만, 반대로 오랜시간 삽질하던 문제가 간단한 수정으로 해결되었다는 것이 다소 어이없기도 했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2274&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q0xze/btszwauU3GI/OGbrrNtpPh9kzGCJ7UwuYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q0xze/btszwauU3GI/OGbrrNtpPh9kzGCJ7UwuYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q0xze/btszwauU3GI/OGbrrNtpPh9kzGCJ7UwuYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq0xze%2FbtszwauU3GI%2FOGbrrNtpPh9kzGCJ7UwuYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;202&quot; data-origin-width=&quot;2274&quot; data-origin-height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;실제로 프로파일링을 다시 진행해보니 정말 성능 이슈가 깔끔하게 해결되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 해결 방법은 찾았지만, 왜 이런 동작을 하는지 근본적인 원인 파악을 해보고자 다시 문제를 찾아나섰다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;1530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vrrJ7/btszvBGnr9z/mToDeyiKMKQtoewbIZb8b1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vrrJ7/btszvBGnr9z/mToDeyiKMKQtoewbIZb8b1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vrrJ7/btszvBGnr9z/mToDeyiKMKQtoewbIZb8b1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvrrJ7%2FbtszvBGnr9z%2FmToDeyiKMKQtoewbIZb8b1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;1185&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;1530&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 발생했던 상황처럼 모듈을 함수로 한 번 감싼 형태, 그리고 함수 없이 모듈이 전역에 노출되어있는 형태의 데모 코드를 준비했다. 색상으로 강조해둔 부분은 각각 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파란색: esbuild 모듈 함수와 동일한 구현체&lt;/li&gt;
&lt;li&gt;노란색: 모듈 정의 및 구현&lt;/li&gt;
&lt;li&gt;빨간색: 모듈 참조&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/facebook/hermes&quot;&gt;Hermes 소스코드&lt;/a&gt;를 내려받아 빌드하고, 해당 환경에서 데모 코드를 직접 실행시켜보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 데모 코드의 코드 양이 많지 않아서인지 눈에 띄는 부분 성능 이슈는 발견하지 못했다. 코드는 우리가 눈으로 보기 쉬운 형태로 작성된 데이터일 뿐이고 실제로 코드가 어떻게 동작하는지 살펴보려면 조금 더 낮은 수준에서 분석을 진행해봐야겠다는 생각을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;코드는 런타임(엔진)에서 실행되고, 엔진은 코드를 바이트 코드(Bytecode)로 변환하여 실행하기 때문에 이 부분을 살펴보기로 했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1964&quot; data-origin-height=&quot;766&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lcmYk/btszvIrHUPi/szHLspsKCaNihGkiBkvXEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lcmYk/btszvIrHUPi/szHLspsKCaNihGkiBkvXEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lcmYk/btszvIrHUPi/szHLspsKCaNihGkiBkvXEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlcmYk%2FbtszvIrHUPi%2FszHLspsKCaNihGkiBkvXEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;195&quot; data-origin-width=&quot;1964&quot; data-origin-height=&quot;766&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hermes에서 기본적으로 지원하는 플래그를 추가하여 쉽게 바이트 코드를 확인할 수 있다. 먼저 성능 문제가 발생하던 IIFE로 감싼 코드의 바이트 코드를 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2188&quot; data-origin-height=&quot;1614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/co3Fed/btszw7kqyuF/vS53jebQHdxxfO6AX8yD41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/co3Fed/btszw7kqyuF/vS53jebQHdxxfO6AX8yD41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/co3Fed/btszw7kqyuF/vS53jebQHdxxfO6AX8yD41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fco3Fed%2Fbtszw7kqyuF%2FvS53jebQHdxxfO6AX8yD41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;531&quot; data-origin-width=&quot;2188&quot; data-origin-height=&quot;1614&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빨간색 부분은 전역(Global) 스코프, 파란색 부분은 IIFE 함수의 내부(Local) 스코프를 바이트 코드 내에서 확인할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이트 코드를 자세히 살펴보기에 앞서, Hermes 엔진에서 지원하는 명령어들은 아래 코드에서 확인해볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/facebook/hermes/blob/v0.12.0/include/hermes/BCGen/HBC/BytecodeList.def&quot;&gt;https://github.com/facebook/hermes/blob/v0.12.0/include/hermes/BCGen/HBC/BytecodeList.def&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2218&quot; data-origin-height=&quot;1590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGGT9R/btszvG1GZrZ/UkCDNkBnzQqwMyVVVkl1Uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGGT9R/btszvG1GZrZ/UkCDNkBnzQqwMyVVVkl1Uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGGT9R/btszvG1GZrZ/UkCDNkBnzQqwMyVVVkl1Uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGGT9R%2FbtszvG1GZrZ%2FUkCDNkBnzQqwMyVVVkl1Uk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;516&quot; data-origin-width=&quot;2218&quot; data-origin-height=&quot;1590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노란색 부분을 보면 IIFE 내부 스코프에서 &lt;code&gt;CreateEnvironment&lt;/code&gt; 명령어를 통해 환경(Environment)을 생성하고 있다. 여기서 이야기하는 환경은 자바스크립트의 실행 컨텍스트 즉, 클로저와 식별자 등이 저장되는 추상적인 개념 정도로 생각하면 될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파란색 부분을 보면 실제로 클로저를 생성하여 환경에 할당하는 명령어를 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 살펴보면 빨간색 부분이 모듈 정의 그리고 할당을 처리하는 명령어들이다. 객체와 클로저를 생성하고 &lt;code&gt;__commonJS&lt;/code&gt; 를 호출하고, 앞서 생성한 환경(Environment)에 할당하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 정의한 모듈이 호출되는 부분을 살펴보도록 하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1960&quot; data-origin-height=&quot;946&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cD1You/btszyL2gaZw/DaKkv2TYLKvK8m5HS1lB2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cD1You/btszyL2gaZw/DaKkv2TYLKvK8m5HS1lB2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cD1You/btszyL2gaZw/DaKkv2TYLKvK8m5HS1lB2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcD1You%2FbtszyL2gaZw%2FDaKkv2TYLKvK8m5HS1lB2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;348&quot; data-origin-width=&quot;1960&quot; data-origin-height=&quot;946&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노란색 부분을 살펴보면 &lt;code&gt;GetEnvionment&lt;/code&gt; 명령어를 통해 앞서 생성한 환경(Environment)을 가져온 후 빨간색 부분에서 &lt;code&gt;LoadFromEnvironment&lt;/code&gt; 명령어로 모듈을 참조하고 있는 모습을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 환경(Environment)을 가져온 후 해당 환경에서 모듈 참조를 로드하고 있다. 우선 이정도까지 확인하고 IIFE 가 없는 코드는 어떻게 동작하는지 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1926&quot; data-origin-height=&quot;1436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OAr3K/btszvh8R8xy/LUkdWkpf62BSON7V1PGUt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OAr3K/btszvh8R8xy/LUkdWkpf62BSON7V1PGUt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OAr3K/btszvh8R8xy/LUkdWkpf62BSON7V1PGUt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOAr3K%2Fbtszvh8R8xy%2FLUkdWkpf62BSON7V1PGUt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;537&quot; data-origin-width=&quot;1926&quot; data-origin-height=&quot;1436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;모듈을 감싸는 IIFE가 없다보니 모든 값들이 전역 스코프 내에서 정의되고 할당되어있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1926&quot; data-origin-height=&quot;1436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QxWTP/btszuEpMQSM/oxVtWzJdEpxJS5EKRMC4U1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QxWTP/btszuEpMQSM/oxVtWzJdEpxJS5EKRMC4U1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QxWTP/btszuEpMQSM/oxVtWzJdEpxJS5EKRMC4U1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQxWTP%2FbtszuEpMQSM%2FoxVtWzJdEpxJS5EKRMC4U1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;537&quot; data-origin-width=&quot;1926&quot; data-origin-height=&quot;1436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;초록색 부분을 보면 &lt;code&gt;GetGlobalObject&lt;/code&gt; 명령어를 통해 전역 객체 참조를 가져온 후 각 모듈을 전역 객체에 &lt;code&gt;PutById&lt;/code&gt; 명령어로 ID 값과 함께 할당하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 모듈을 참조하는 부분을 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2378&quot; data-origin-height=&quot;1142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w3fOn/btszwec3iJU/KBmMkKB0erzoZycXBIsOTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w3fOn/btszwec3iJU/KBmMkKB0erzoZycXBIsOTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w3fOn/btszwec3iJU/KBmMkKB0erzoZycXBIsOTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw3fOn%2Fbtszwec3iJU%2FKBmMkKB0erzoZycXBIsOTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;346&quot; data-origin-width=&quot;2378&quot; data-origin-height=&quot;1142&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조하는 과정 역시 전역 객체를 가져온 후 ID를 통해 모듈 참조를 가져오고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 프로젝트의 모듈은 서로 참조 가능하고, 중첩될 수 있다. 예를 들어 A 모듈을 B, C모듈에서 참조하고 C 모듈에서는 D, E 모듈을 참조하는 등 의존 관계 그래프는 복잡하고, 규모가 커질수록 모듈을 찾는데 필요한 깊이(Depth)는 더욱 더 깊어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IIFE로 감싼 코드의 경우 환경(Environment)을 생성하고 해당 컨텍스트에 모듈 참조를 할당한다. 그리고 참조할 때에는 환경(Environment)을 가져온 후(&lt;code&gt;GetEnvironment&lt;/code&gt;) 참조하고자 하는 모듈을 로드(&lt;code&gt;LoadFromEnvironment&lt;/code&gt;)한다. 복잡한 모듈 의존 관계에서는 모듈을 참조하기 위해 환경(Environment)을 가져오고 로드하는 과정을 계속해서 반복하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 IIFE가 없는 경우에는 무조건 전역 객체에 ID로 모듈 참조를 할당(&lt;code&gt;PutById&lt;/code&gt;)하고, 가져올 때에도 ID를 통해 바로 가져온다(&lt;code&gt;GetByIdShort&lt;/code&gt;). 즉, 모듈 의존 관계가 아무리 복잡하더라도 전역 객체에서 ID로 즉시 할당하고 접근하기 때문에 성능상 이득이 훨씬 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 문제로 돌아와보면 모듈들을 함수로 감싼것이 원인인가 싶을 수 있다. 다시 이야기 하자면 JSC, V8 런타임에서는 IIFE로 모듈들을 감쌌더라도 아무런 성능 이슈가 없었고 Hermes 환경에서만 이슈가 발생했다. 필자 개인적으로는 Hermes에서 바이트 코드 최적화를 제대로 수행하지 못한 것이 원인이라고 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 성능 이슈의 원인과 해결 방법 모두 발견했다. esbuild 번들에서 IIFE를 어떻게 없애는지가 관건인데, esbuild 번들 형식을 IIFE에서 CJS 혹은 ESM으로 구성하면 IIFE 없이 전역에 모든 모듈을 노출시키는 형태로 번들을 생성할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;1308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cebQeL/btszwd6chfs/ZhzNhaWuieT07W96WplbaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cebQeL/btszwd6chfs/ZhzNhaWuieT07W96WplbaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cebQeL/btszwd6chfs/ZhzNhaWuieT07W96WplbaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcebQeL%2Fbtszwd6chfs%2FZhzNhaWuieT07W96WplbaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;343&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;1308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;실제 프로젝트에서 단 한 줄 만으로 성능 이슈를 수정했고, 기록은 아래에서 확인 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/leegeunhyeok/react-native-esbuild/pull/7/files&quot;&gt;https://github.com/leegeunhyeok/react-native-esbuild/pull/7/files&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;성능 그리고 변화&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 Metro와 비교했을 때 성능이 꽤나 많이 향상되었다. 프로젝트 규모가 작은 경우 약 2배 가까운 성능을 보여주지만, 규모가 커지면 커질수록 성능 향상폭은 더 커졌다. 아래 참고 자료는 프로덕션에 배포되어있는 실제 프로젝트를 대상으로 진행한 초기 버전 테스트 결과이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1854&quot; data-origin-height=&quot;1302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zFlOm/btszuCrW7TA/Mgrpj5DHcIERyA1pl2KLm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zFlOm/btszuCrW7TA/Mgrpj5DHcIERyA1pl2KLm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zFlOm/btszuCrW7TA/Mgrpj5DHcIERyA1pl2KLm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzFlOm%2FbtszuCrW7TA%2FMgrpj5DHcIERyA1pl2KLm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;506&quot; data-origin-width=&quot;1854&quot; data-origin-height=&quot;1302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;pmhy7bjjz2hb1.png&quot; data-origin-width=&quot;1854&quot; data-origin-height=&quot;1302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crcLo9/btszRRoa3uy/eKdKL7oOIjKBMXG6AmXs9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crcLo9/btszRRoa3uy/eKdKL7oOIjKBMXG6AmXs9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crcLo9/btszRRoa3uy/eKdKL7oOIjKBMXG6AmXs9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrcLo9%2FbtszRRoa3uy%2FeKdKL7oOIjKBMXG6AmXs9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;506&quot; data-filename=&quot;pmhy7bjjz2hb1.png&quot; data-origin-width=&quot;1854&quot; data-origin-height=&quot;1302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두 캐시를 제거한 뒤 번들링을 진행했는데 Metro는 약 45초, 자체 개발한 번들러는 약 7초 가량 소요되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;대략 600%, 즉 6배의 성능 향상폭을 경험할 수 있었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AS IS&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;느린 속도&lt;/li&gt;
&lt;li&gt;캐시 문제로 인한 비일관적인 빌드&lt;/li&gt;
&lt;li&gt;느슨(Loose)한 모듈 Resolution&lt;/li&gt;
&lt;li&gt;트리 셰이킹 미지원&lt;/li&gt;
&lt;li&gt;개발 편의 기능(HMR, Hot Module Replacement) 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TO BE&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빠른 속도&lt;/li&gt;
&lt;li&gt;캐시 문제 해결로 일관적인 빌드&lt;/li&gt;
&lt;li&gt;엄격(Strict)한 모듈 Resolution&lt;/li&gt;
&lt;li&gt;트리 셰이킹 지원&lt;/li&gt;
&lt;li&gt;다소 부족한 개발 편의 기능(Live Reload) 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 부분에서 자체 개발한 번들러가 우세하지만, 치명적인 단점 또한 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그것은 바로 HMR을 지원하지 않는 것인데, esbuild은 HMR을 지원하지 않는다. 이로 인해 개발 중 코드를 수정했을 때, 해당 부분을 즉시 반영하지 못한다. 대신 실시간 새로고침 기능을 구현하여 추가해둔 상태이다. HMR에 대한 내용은 아래에서 다시 이야기 해보도록 하자.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;번들러 살펴보기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Custom CLI&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 목표 중 하나가 기존 React Native CLI 와 호환성을 갖추는 것이었는데, 어느정도 달성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 서버를 실행시키는 &lt;code&gt;start&lt;/code&gt; 커맨드, 번들을 생성하는 &lt;code&gt;bundle&lt;/code&gt; 커맨드 등을 구현했고, 주요 옵션들도 기존 CLI와 동일하게 구현했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;1274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dktfYW/btszvcGNBHQ/doU8wdgZAVZj1ciSD6ihPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dktfYW/btszvcGNBHQ/doU8wdgZAVZj1ciSD6ihPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dktfYW/btszvcGNBHQ/doU8wdgZAVZj1ciSD6ihPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdktfYW%2FbtszvcGNBHQ%2FdoU8wdgZAVZj1ciSD6ihPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;506&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;1274&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;1274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBqnae/btszwdFdyAZ/6KICEgHXww3TyseDYdug4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBqnae/btszwdFdyAZ/6KICEgHXww3TyseDYdug4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBqnae/btszwdFdyAZ/6KICEgHXww3TyseDYdug4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBqnae%2FbtszwdFdyAZ%2F6KICEgHXww3TyseDYdug4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;506&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;1274&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 Android Stuido, XCode 등에서 릴리즈 빌드를 수행할 때 자체 개발한 번들러를 사용할 수 있도록 간단하게 마이그레이션 할 수 있다. 자세한 내용은 아래 문서를 참고하도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://react-native-esbuild.vercel.app/native-setup/android&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://react-native-esbuild.vercel.app/native-setup/android&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://react-native-esbuild.vercel.app/native-setup/ios&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://react-native-esbuild.vercel.app/native-setup/ios&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dev server&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 모드에서 번들을 실행시킬 수 있도록 CLI를 통해 환경을 제공한다.&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;1274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cq8LQI/btszuFWyc29/J1iTmHbqcKb3Fjrbij9OSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cq8LQI/btszuFWyc29/J1iTmHbqcKb3Fjrbij9OSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cq8LQI/btszuFWyc29/J1iTmHbqcKb3Fjrbij9OSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcq8LQI%2FbtszuFWyc29%2FJ1iTmHbqcKb3Fjrbij9OSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;506&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;1274&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Hot Module Repacement(Hot Reload)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 esbuild가 HMR을 지원하지 않아 대신 실시간 새로고침 기능을 제공하고 있다고 이야기 했다. 실제 개발 환경에서 사용해보면, 코드 한 줄만 변경하더라도 변경한 부분이 아닌 앱 전체가 새로고침되어 꽤나 불편함을 느낄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빠른 빌드 속도 뿐만 아니라 개발 경험도 중요한 목표 중 하나였기에, 자체적으로 HMR 기능을 구현하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HMR은 모듈 단위로 변경된 부분을 대체하여 런타임에 반영하는 반면 Hot Reload는 변경한 코드 부분만 반영하는 방식이다. 조금은 다르지만 둘 다 동일한 기능을 수행한다고 봐도 무방하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 본론으로 돌아와서, 현재 작업 중인 내용은 완전한 Hot Module Replacement는 아니고 React에서 공식적으로 제공하는 &lt;a href=&quot;https://github.com/facebook/react/tree/main/packages/react-refresh&quot;&gt;react-refresh&lt;/a&gt; 를 기반으로 React 컴포넌트 대상의 Hot Reload를 구현하는 중이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PoC를 진행하여 동작하는 것을 확인했고 현재 계속해서 개발 중이다. &lt;code&gt;react-refresh&lt;/code&gt; 의 동작 방식은 컴포넌트를 런타임에 등록하고, 훅을 사용하는 경우 이를 구분하기 위한 시그니쳐를 생성하여 &lt;a href=&quot;https://github.com/facebook/react/blob/main/packages/react-refresh/src/ReactFreshRuntime.js&quot;&gt;react-fresh/runtime&lt;/a&gt; 컨텍스트에 저장해둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 변경된 컴포넌트를 발견하게 되면 React의 Reconciler로 해당 사항을 전달하여 상태(State)를 유지한체 컴포넌트를 Re-render 하거나, 아예 Re-mount 처리하여 UI에 변경 사항을 반영한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 부분은 &quot;변경된 컴포넌트(모듈)를 발견&quot; 그리고 &quot;변경 사항 전달&quot; 두 가지이다. esbuild는 HMR을 지원하지 않고, 코드가 수정되더라도 어느 코드(모듈)가 수정되었는지 알려주지 않고 전체 재빌드를 수행하기 때문에 esbuild에서 제공하는 &lt;a href=&quot;https://esbuild.github.io/api/#watch&quot;&gt;watch&lt;/a&gt; 기능을 포기하고 커스텀 파일 시스템 감시자를 구현하는 방향으로 작업을 진행하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 파일이 변경되면 해당 파일만 변환(Transform) 처리하고, 변환된 코드는 웹 소켓을 통해 React Native 애플리케이션 런타임으로 전달한 후 평가&lt;code&gt;eval()&lt;/code&gt;한다 (정확하게는 &lt;a href=&quot;https://github.com/facebook/metro/blob/v0.80.0/packages/metro-runtime/src/modules/HMRClient.js#L25&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;globalEvalWithSourceUrl()&lt;/a&gt; 이다). 여기서도 단순히 변환된 코드를 실행하면 문제가 발생하는데, 바로 모듈의 의존 관계이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트를 구현한 모듈 내에서 참조(&lt;code&gt;import&lt;/code&gt;)하고 있는 외부 모듈이 존재하는 경우 외부 모듈을 정상적으로 참조 수 있어야 한다. 이를 해결하기 위해 사전에 모듈의 참조(Reference)를 전역 객체에 저장해두고, 해당 모듈을 참조하는 방향으로 구현하고 있다. 단, 참조하고 있는 모듈이 변경될 경우에는 어쩔 수 없이 다시 전체 빌드를 진행해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3584&quot; data-origin-height=&quot;1636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ydvul/btszwfCL6y7/4XqZQ0q6mn93dGxrcShkI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ydvul/btszwfCL6y7/4XqZQ0q6mn93dGxrcShkI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ydvul/btszwfCL6y7/4XqZQ0q6mn93dGxrcShkI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fydvul%2FbtszwfCL6y7%2F4XqZQ0q6mn93dGxrcShkI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;329&quot; data-origin-width=&quot;3584&quot; data-origin-height=&quot;1636&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3568&quot; data-origin-height=&quot;1956&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnaA5p/btszxq5bMOv/Dagvc2KfHLmm3ihVfq3Sg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnaA5p/btszxq5bMOv/Dagvc2KfHLmm3ihVfq3Sg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnaA5p/btszxq5bMOv/Dagvc2KfHLmm3ihVfq3Sg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnaA5p%2Fbtszxq5bMOv%2FDagvc2KfHLmm3ihVfq3Sg0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;395&quot; data-origin-width=&quot;3568&quot; data-origin-height=&quot;1956&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;모듈 참조 구문을 변경하는 작업은 swc 기반의 커스텀 플러그인을 구현하여 처리하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;esbuild에서 HMR을 지원하게 된다면 이러한 작업은 대체될 수 있으나, esbuild가 추구하는 방향(부가적인 기능을 최소화하여 빠른 퍼포먼스 유지)과 맞지 않기에 공식적으로 HMR 기능은 &lt;a href=&quot;https://github.com/evanw/esbuild/issues/97#issuecomment-626183106&quot;&gt;제공되지 않을 가능성&lt;/a&gt;이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재까지 시도한 PoC를 기반으로 작업한 결과, 어느정도 HMR 기능이 동작하고 있다.&lt;/p&gt;

            &lt;figure class=&quot;unsupported component-kakaotv&quot; contenteditable=&quot;false&quot; style=&quot;background:#000;margin:16px 0;min-height:72px;padding:10px 16px;display:flex;align-items:center;justify-content:center;text-align:center;box-sizing:border-box;width:100%;max-width:100%;&quot;&gt;
                &lt;p contenteditable=&quot;false&quot; style=&quot;margin:0;color:#8a8a8a;font-size:13px;line-height:1.6;user-select:none;pointer-events:none;&quot;&gt;동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.&lt;/p&gt;
            &lt;/figure&gt;
        
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 작업은 저장소의 이슈로 생성되어있으며, 기능 구현 및 테스트가 완료될 경우 릴리즈 할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/leegeunhyeok/react-native-esbuild/issues/38&quot;&gt;https://github.com/leegeunhyeok/react-native-esbuild/issues/38&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HMR에 대해 조금 더 자세히 알고 싶다면 아래 글을 참고하도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://geundung.dev/135&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://geundung.dev/135&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1703570581392&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Hot Module Replacement (aka. Hot Reload)&quot; data-og-description=&quot;개요 Hot Module Replacement(HMR)의 기본적인 동작 메커니즘에 대해 살펴보고, 간단한 예제를 통해 살펴보려고 한다. 예제와 설명은 esbuild 번들러에서 HMR 기능을 구현하는 데모를 기반으로 한다. 해당 &quot; data-og-host=&quot;geundung.dev&quot; data-og-source-url=&quot;https://geundung.dev/135&quot; data-og-url=&quot;https://geundung.dev/135&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/RjDRD/hyUPAGhye9/jgidIiFTDPNTDvjfkqFp71/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/cjlFJ1/hyUTDuyxRF/8hZReRzEp4rQ1wxX8FEtNk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/dF0eqn/hyUTwCcYcJ/ezw6RjlZlk6E4JhXHBrlo0/img.png?width=1666&amp;amp;height=1256&amp;amp;face=0_0_1666_1256&quot;&gt;&lt;a href=&quot;https://geundung.dev/135&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://geundung.dev/135&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/RjDRD/hyUPAGhye9/jgidIiFTDPNTDvjfkqFp71/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/cjlFJ1/hyUTDuyxRF/8hZReRzEp4rQ1wxX8FEtNk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/dF0eqn/hyUTwCcYcJ/ezw6RjlZlk6E4JhXHBrlo0/img.png?width=1666&amp;amp;height=1256&amp;amp;face=0_0_1666_1256');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Hot Module Replacement (aka. Hot Reload)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;개요 Hot Module Replacement(HMR)의 기본적인 동작 메커니즘에 대해 살펴보고, 간단한 예제를 통해 살펴보려고 한다. 예제와 설명은 esbuild 번들러에서 HMR 기능을 구현하는 데모를 기반으로 한다. 해당&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;geundung.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Web&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native를 기반의 애플리케이션을 개발하는 경우 대부분 네이티브(Android, iOS)를 타겟으로 한다. 알다시피 React Native는 여러 플랫폼을 지원하는데 그중 웹이 포함된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹을 위한 개발 서버도 제공하고 있으며 추가적인 구성 없이, &lt;code&gt;serve&lt;/code&gt; 라는 커맨드를 통해&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;웹 개발 서버를 &lt;/span&gt;실행시킬 수 있다. 빌드를 할 경우에는 네이티브와 동일하게 &lt;code&gt;bundle&lt;/code&gt; 커맨드를 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 하나의 코드 베이스를 네이티브(iOS)와 웹에서 각각 실행시킨 결과물이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clbEyr/btszyKbat9g/T6Mxhwt8CSmE6R0Jb26oR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clbEyr/btszyKbat9g/T6Mxhwt8CSmE6R0Jb26oR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clbEyr/btszyKbat9g/T6Mxhwt8CSmE6R0Jb26oR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclbEyr%2FbtszyKbat9g%2FT6Mxhwt8CSmE6R0Jb26oR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;720&quot; data-origin-width=&quot;900&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Jest transformer&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;번들링을 수행하면서 코드 변환을 진행한다. 이를 파이프라인이라는 객체로 추상화 했다고 이야기 했는데 이 파이프라인을 그대로 가져와서 Jest 테스트에 사용할 수 있도록 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native는 기본적으로 &lt;a href=&quot;https://github.com/jestjs/jest/tree/main/packages/babel-jest&quot;&gt;babel-jest&lt;/a&gt;를 사용하도록 구성되어있는데, 이를 자체 구현한 변환 기능으로 대체할 수 있다. 이를 실제 프로덕션 프로젝트에 적용해 보았는데 빌드 시간과 동일하게 월등히 빠른 속도를 보여주고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GQFVO/btszvIrH6we/enxyctzIKNjKz6fmI1MVC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GQFVO/btszvIrH6we/enxyctzIKNjKz6fmI1MVC1/img.png&quot; data-alt=&quot;기존 (babel-jest)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GQFVO/btszvIrH6we/enxyctzIKNjKz6fmI1MVC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGQFVO%2FbtszvIrH6we%2FenxyctzIKNjKz6fmI1MVC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;132&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;196&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기존 (babel-jest)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfJSem/btszuEJ3TrH/VABHnFCfriFJai5BVLFNUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfJSem/btszuEJ3TrH/VABHnFCfriFJai5BVLFNUK/img.png&quot; data-alt=&quot;자체 구현 transformer&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfJSem/btszuEJ3TrH/VABHnFCfriFJai5BVLFNUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfJSem%2FbtszuEJ3TrH%2FVABHnFCfriFJai5BVLFNUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;133&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;202&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;자체 구현 transformer&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리하며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길다면 길고 짧다면 짧은 여정을 통해 여기까지 오게 되었다. 전해주고 싶은 이야기들은 훨씬 더 많은데, 정리하는 데 한계가 있어 이정도 공유하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 프로젝트는 본업이 있다보니 개인 시간이 있을때마다 틈틈히 개발을 진행했고, 번들링 그리고 완벽하게 번들을 실행하는데까지 약 1.5개월 가량 소요되었다. 중간에 사내 팀에게 공유를 했었는데, 도전하는 것에 있어 개방된 분위기였기에 업무에서 esbuild 번들러 개발하고 프로젝트에 적용할 수 있도록 어느정도의 시간을 할당 받기도 했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;돌이켜보면 필자 본인이 한 일은 잘 만들어진 오픈소스를 조합해서 React Native가 갖는 특성에 맞게 커스터마이징 한 것이 전부이다. 과거였다면 자체 번들러를 처음부터 개발하는 것이 불가능(상당히 어려운)에 가까운 일이었겠지만, 훌륭한 오픈소스들이 있었기에 여기까지 올 수 있었다. 개발자는 단순히 개발하는 것 뿐만 아니라 이처럼 오픈소스를 본인 코드에 잘 녹여내는 것도 중요한 능력이라는 생각을 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 개발하면서 마주할 수 있는 여러가지 문제들이 있는데 이는 예측이 불가할 뿐더러 플랫폼 종속적인 경우도 있어 상당히 깊은 수준을 요구하는 경우도 있었다. 필자 본인은 숨겨져있는 곳을 살펴보는걸 좋아한다. 예를 들면 &quot;자바스크립트가 어떻게, 왜 이렇게 동작할까?&quot; 등과 같은 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;프로그래밍에서 범용적으로 사용하는 디자인 패턴 같은 것 말고 왜 자바스크립트 언어를 파고 있나요?&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 실제로 본인이 과거에 받았던 질문이다. 일반적인 상황에서는 개발을 할 때 언어 혹은 플랫폼에 대한 깊은 이해가 불필요할 수 있고 오히려 시간 낭비가 될 수 있다는 것에 대한 의견 자체는 어느정도 공감한다. 하지만, 애플리케이션을 넘어 그 아래로 파고 들게 되면 이러한 지식들은 반드시 요구된다는 것을 이번 계기에 확실히 알게 되었고, 결코 헛된 노력이 아니라는 것을 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 번들러를 개발하면서 React Native 그리고 Metro 번들러의 구조를 분석해볼 수 있었고 기존에 자바스크립트의 동작을 조금이나마 파헤쳐 보고자 살펴본 경험이 있었기 때문에, 작업하면서 발생했던 문제들을 해결할 수 있었다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글은 여기까지 하고 마무리 하려고 한다. 참고로 지금까지 소개한 esbuild 기반의 React Native 번들러는 모두 오픈소스로 공개되어있으며 아래 저장소에서 확인 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/leegeunhyeok/react-native-esbuild&quot;&gt;https://github.com/leegeunhyeok/react-native-esbuild&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1698756574954&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - leegeunhyeok/react-native-esbuild: ⚡️ Esbuild based universal bundler for React Native&quot; data-og-description=&quot;⚡️ Esbuild based universal bundler for React Native - GitHub - leegeunhyeok/react-native-esbuild: ⚡️ Esbuild based universal bundler for React Native&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/leegeunhyeok/react-native-esbuild&quot; data-og-url=&quot;https://github.com/leegeunhyeok/react-native-esbuild&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/KHHkF/hyUnKOxbQt/LfnnkWcjQGR1GMnjKQZ3zK/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640&quot;&gt;&lt;a href=&quot;https://github.com/leegeunhyeok/react-native-esbuild&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/leegeunhyeok/react-native-esbuild&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/KHHkF/hyUnKOxbQt/LfnnkWcjQGR1GMnjKQZ3zK/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - leegeunhyeok/react-native-esbuild: ⚡️ Esbuild based universal bundler for React Native&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;⚡️ Esbuild based universal bundler for React Native - GitHub - leegeunhyeok/react-native-esbuild: ⚡️ Esbuild based universal bundler for React Native&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련하여 궁금한 점이 있거나, 이슈를 발견한 경우 아래 저장소에 이슈로 남겨주거나 이메일로 보내주길 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>언어 &amp;amp; 프레임워크/React Native</category>
      <author>이근둥</author>
      <guid isPermaLink="true">https://codevkr.tistory.com/132</guid>
      <comments>https://codevkr.tistory.com/132#entry132comment</comments>
      <pubDate>Tue, 31 Oct 2023 22:50:33 +0900</pubDate>
    </item>
    <item>
      <title>React Native Under The Hood - 개발(Development)</title>
      <link>https://codevkr.tistory.com/131</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w4UNJ/btsxrWyfstF/nKofLKHm8mIsDdtNhPg8TK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w4UNJ/btsxrWyfstF/nKofLKHm8mIsDdtNhPg8TK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w4UNJ/btsxrWyfstF/nKofLKHm8mIsDdtNhPg8TK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw4UNJ%2FbtsxrWyfstF%2FnKofLKHm8mIsDdtNhPg8TK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;320&quot; height=&quot;320&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 React Native 애플리케이션을 개발할 때 접하고 있는 기능들이 어떻게 동작하는지 살펴보려고 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Logging&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 번들러 글에서 살펴본 내용인데, 빌드 타임에 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/polyfills/console.js&quot;&gt;console 폴리필&lt;/a&gt;이 코드 최상단에 주입된다는 것을 기억하는가?&lt;br /&gt;해당 폴리필은 React Native 애플리케이션의 로그 포맷 개선 그리고 네이티브로 메시지를 전달하기 위한 기반을 마련하는 폴리필이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Native&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 React Native 애플리케이션 내에서 기록한 콘솔 로그가 어떻게 네이티브 로그에도 기록되는지 한 번 살펴보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;폴리필을 확인해 보면 아래와 같이 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/polyfills/console.js#L546-L600&quot;&gt;nativeLoggingHook&lt;/a&gt;이 존재할 경우 콘솔 객체의 메소드를 재정의(Override)한다. 코드를 살펴보면 알 수 있지만, 메시지를 로그 레벨과 함께 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/polyfills/console.js#L441&quot;&gt;nativeLoggingHook 으로 전달&lt;/a&gt;하도록 처리되어있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1510&quot; data-origin-height=&quot;1832&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uk07r/btsxihcuoiN/iAHHk6zVD5X6ZZP2635Agk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uk07r/btsxihcuoiN/iAHHk6zVD5X6ZZP2635Agk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uk07r/btsxihcuoiN/iAHHk6zVD5X6ZZP2635Agk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fuk07r%2FbtsxihcuoiN%2FiAHHk6zVD5X6ZZP2635Agk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;874&quot; data-origin-width=&quot;1510&quot; data-origin-height=&quot;1832&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;nativeLoggingHook&lt;/code&gt; 은 이전 글에서도 언급했지만, 네이티브에서 로그를 수신하여 네이티브 로그에 기록하기 위한 함수이다. 이는 &lt;code&gt;JSIExecutor&lt;/code&gt; 를 통해 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp#L544-L566&quot;&gt;자바스크립트 런타임에 주입&lt;/a&gt;되며 &lt;code&gt;bindNativeLogger&lt;/code&gt; 메소드에서 주입되는 모습을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;1814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bI4gdu/btsxibwAlOc/yFeHzEPoWPxIO6QKLPZkB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bI4gdu/btsxibwAlOc/yFeHzEPoWPxIO6QKLPZkB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bI4gdu/btsxibwAlOc/yFeHzEPoWPxIO6QKLPZkB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbI4gdu%2FbtsxibwAlOc%2FyFeHzEPoWPxIO6QKLPZkB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;714&quot; data-origin-width=&quot;1830&quot; data-origin-height=&quot;1814&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현부를 살펴보면 인자로 전달받은 &lt;code&gt;logger&lt;/code&gt; 로 자바스크립트 런타임으로부터 전달받은 메시지 데이터와 로그 레벨을 전달하고 있다. 여기서 참조하는 로거는 플랫폼별로 다르게 구현되어 있는데, 조금 더 자세히 살펴보도록 하자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Android&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드의 경우 &lt;code&gt;JSExecutor&lt;/code&gt; 가 JNI를 통해 로드될 때 처리되는 &lt;code&gt;OnLoad&lt;/code&gt;(&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactAndroid/src/main/jni/react/jscexecutor/OnLoad.cpp#L32&quot;&gt;JSC&lt;/a&gt;, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactAndroid/src/main/jni/react/hermes/reactexecutor/OnLoad.cpp#L57&quot;&gt;Hermes&lt;/a&gt;)에서 &lt;code&gt;bindNativeLogger&lt;/code&gt; 를 호출하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hermes 런타임을 기준으로 살펴보면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;866&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Sq2e4/btsxUbtP5v7/LLMUMqR4HRiXkBaDyo8kaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Sq2e4/btsxUbtP5v7/LLMUMqR4HRiXkBaDyo8kaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Sq2e4/btsxUbtP5v7/LLMUMqR4HRiXkBaDyo8kaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSq2e4%2FbtsxUbtP5v7%2FLLMUMqR4HRiXkBaDyo8kaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;335&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;866&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 네이티브의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactAndroid/src/main/jni/react/jni/JSLogging.cpp&quot;&gt;reactAndroidLoggingHook&lt;/a&gt;를 호출하도록 구현되어 있는데, 이를 통해 네이티브 로그를 기록하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2074&quot; data-origin-height=&quot;1594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dASb7g/btsxsRpW0LB/pnSU3htexLBRfWKgHfbsDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dASb7g/btsxsRpW0LB/pnSU3htexLBRfWKgHfbsDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dASb7g/btsxsRpW0LB/pnSU3htexLBRfWKgHfbsDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdASb7g%2FbtsxsRpW0LB%2FpnSU3htexLBRfWKgHfbsDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;553&quot; data-origin-width=&quot;2074&quot; data-origin-height=&quot;1594&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;iOS&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iOS도 Android와 유사하게 런타임이 초기화될 때 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/CxxBridge/RCTJSIExecutorRuntimeInstaller.mm#L22&quot;&gt;RCTJSIExecutorRuntimeInstaller&lt;/a&gt;에서 &lt;code&gt;bindNativeLogger&lt;/code&gt; 를 호출한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2204&quot; data-origin-height=&quot;1218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdlRCb/btsxqaqnXnp/ackLCGMa62pXGPbB7Bn1HK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdlRCb/btsxqaqnXnp/ackLCGMa62pXGPbB7Bn1HK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdlRCb/btsxqaqnXnp/ackLCGMa62pXGPbB7Bn1HK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdlRCb%2FbtsxqaqnXnp%2FackLCGMa62pXGPbB7Bn1HK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;398&quot; data-origin-width=&quot;2204&quot; data-origin-height=&quot;1218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/Base/RCTLog.mm#L328-L337&quot;&gt;_RCTLogJavaScriptInternal&lt;/a&gt; 를 호출하도록 구현되어 있는데, 이를 통해 네이티브 로그를 기록하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 런타임의 로그가 네이티브 로그로 전달되고 기록되는 과정을 정리하자면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2364&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QCNI2/btsxsSvCPK3/cEzf3sKz4Nbb81GesqcOF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QCNI2/btsxsSvCPK3/cEzf3sKz4Nbb81GesqcOF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QCNI2/btsxsSvCPK3/cEzf3sKz4Nbb81GesqcOF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQCNI2%2FbtsxsSvCPK3%2FcEzf3sKz4Nbb81GesqcOF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;239&quot; data-origin-width=&quot;2364&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSI(&lt;code&gt;nativeLoggingHook&lt;/code&gt;)를 통해 메시지 데이터를 네이티브에서 수신하고, 이를 각 플랫폼에 맞게 구현된 Logger에게 전달하여 로그를 기록하게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Terminal&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브에서 로그를 확인할 수 있지만, 우리가 번들러(Metro)를 실행한 터미널 환경에도 로그를 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 동작의 시작은 console 폴리필로 다시 거슬러 올라가는데, 폴리필이 적용된 경우 &lt;code&gt;console&lt;/code&gt; 객체에 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/polyfills/console.js#L570-L573&quot;&gt;_isPolyfilled&lt;/a&gt; 속성을 &lt;code&gt;true&lt;/code&gt; 로 정의한다. 해당 값에 따라 번들러로 로그 데이터를 전송할지 결정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 번들러 글에서 폴리필 주입 이후 초기화(&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/Core/InitializeCore.js&quot;&gt;initializeCore&lt;/a&gt;) 코드가 실행된다고 이야기했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에서 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/Core/setUpDeveloperTools.js&quot;&gt;setUpDeveloperTools&lt;/a&gt; 모듈이 호출되어 개발 환경이 구성되고, 콘솔 폴리필이 적용된 경우 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/Core/setUpDeveloperTools.js#L45-L74&quot;&gt;웹소켓을 통해 번들러 서버로 메시지 데이터를 전송&lt;/a&gt;하도록 구성된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1690&quot; data-origin-height=&quot;1562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bE66a3/btsxkjBnArT/lM7UaWMoZ8QtTkKSeItIk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bE66a3/btsxkjBnArT/lM7UaWMoZ8QtTkKSeItIk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bE66a3/btsxkjBnArT/lM7UaWMoZ8QtTkKSeItIk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbE66a3%2FbtsxkjBnArT%2FlM7UaWMoZ8QtTkKSeItIk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;665&quot; data-origin-width=&quot;1690&quot; data-origin-height=&quot;1562&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 살펴보면 기본 콘솔 메소드를 호출과 동시에 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/Utilities/HMRClient.js&quot;&gt;HMRClient&lt;/a&gt;의 log 메소드를 호출하도록 다시 재정의(Override)되는 모습을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;HMRClient&lt;/code&gt; 는 개발 환경에서 HMR(Hot Module Replacement) 기능과 관련된 동작을 처리하기 위한 Client-Side 모듈이다. 이는&amp;nbsp;Metro 번들러에 구현된 &lt;a href=&quot;https://github.com/facebook/metro/blob/v0.76.5/packages/metro/src/HmrServer.js&quot;&gt;HmrServer&lt;/a&gt; 와 웹소켓을 통해 상호작용 하게 되는데, &lt;code&gt;ws://host/hot&lt;/code&gt; &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/Core/setUpDeveloperTools.js#L45-L74&quot;&gt;주소로 웹소켓 커넥션&lt;/a&gt;을 맺게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3096&quot; data-origin-height=&quot;1088&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LKdMh/btsxz0fwM7j/k9quyXPioBACU7ljTwVyFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LKdMh/btsxz0fwM7j/k9quyXPioBACU7ljTwVyFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LKdMh/btsxz0fwM7j/k9quyXPioBACU7ljTwVyFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLKdMh%2Fbtsxz0fwM7j%2Fk9quyXPioBACU7ljTwVyFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;253&quot; data-origin-width=&quot;3096&quot; data-origin-height=&quot;1088&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세히 살펴보면 &lt;code&gt;HMRClient&lt;/code&gt; 내에서 &lt;code&gt;metro-runtime&lt;/code&gt; 패키지의 &lt;a href=&quot;https://github.com/facebook/metro/blob/v0.76.5/packages/metro-runtime/src/modules/HMRClient.js&quot;&gt;HMRClient&lt;/a&gt; 를 참조하고 있다. 이름이 동일하여 혼동될 수 있으니 이번 글에서는 &lt;code&gt;MetroHMRClient&lt;/code&gt; 라고 표기하도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;HMRClient.log&lt;/code&gt; 의 실체는 결국 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/Utilities/HMRClient.js#L108-L141&quot;&gt;MetroHMRClient.send&lt;/a&gt; 인 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;1602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lRLSv/btsxq4XE6oN/6lgUhwkTb18i7Wte8PQ7u0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lRLSv/btsxq4XE6oN/6lgUhwkTb18i7Wte8PQ7u0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lRLSv/btsxq4XE6oN/6lgUhwkTb18i7Wte8PQ7u0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlRLSv%2Fbtsxq4XE6oN%2F6lgUhwkTb18i7Wte8PQ7u0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;682&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;1602&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지의 과정을 정리하자면, 런타임에서 콘솔 로그를 출력하면 Metro 번들러의 &lt;code&gt;/hot&lt;/code&gt; 엔드포인트로 &lt;code&gt;{ type: 'log' }&lt;/code&gt; 형태의 JSON 데이터를 전송한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 전달된 메지시 데이터는 메트로의 &lt;a href=&quot;https://github.com/facebook/metro/blob/v0.76.5/packages/metro/src/HmrServer.js#L223-L230&quot;&gt;HmrServer 에서 수신&lt;/a&gt;하는데, &lt;code&gt;reporter&lt;/code&gt; 로 정보를 다시 전달하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1336&quot; data-origin-height=&quot;1608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYXK7E/btsxica80lK/KkmOrNpvAG05zx43E2o2jK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYXK7E/btsxica80lK/KkmOrNpvAG05zx43E2o2jK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYXK7E/btsxica80lK/KkmOrNpvAG05zx43E2o2jK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYXK7E%2Fbtsxica80lK%2FKkmOrNpvAG05zx43E2o2jK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;867&quot; data-origin-width=&quot;1336&quot; data-origin-height=&quot;1608&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;reporter&lt;/code&gt; 는 무엇일까? Metro 번들러 구성에서 확인 가능하며, 기본 값으로 &lt;a href=&quot;https://github.com/facebook/metro/blob/1fb04dea1982e73c1c1d25db0db81b2088a45bfc/packages/metro-config/src/defaults/index.js#L165&quot;&gt;TerminalReporter가 사용&lt;/a&gt;되도록 구현되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;TerminalReporter&lt;/code&gt; 구현을 살펴보면, 아래와 같이 &lt;a href=&quot;https://github.com/facebook/metro/blob/v0.76.5/packages/metro/src/lib/TerminalReporter.js#L274-L276&quot;&gt;수신한 메시지 데이터를 콘솔에 출력&lt;/a&gt;하도록 구현된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2026&quot; data-origin-height=&quot;1166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0fhsi/btsxUeD5SV6/2K2qhuFz4ZaT7k7VuLbLv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0fhsi/btsxUeD5SV6/2K2qhuFz4ZaT7k7VuLbLv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0fhsi/btsxUeD5SV6/2K2qhuFz4ZaT7k7VuLbLv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0fhsi%2FbtsxUeD5SV6%2F2K2qhuFz4ZaT7k7VuLbLv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;414&quot; data-origin-width=&quot;2026&quot; data-origin-height=&quot;1166&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/facebook/metro/blob/v0.76.5/packages/metro/src/lib/logToConsole.js&quot;&gt;logToConsole&lt;/a&gt; 로 데이터를 전달하여 색상을 입히고 포맷팅을 거친 후 최종적으로 터미널 상에 로그가 기록된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 살펴본 내용을 정리해 보면 아래와 같이 나타낼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2080&quot; data-origin-height=&quot;816&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mUVax/btsxqtwCHJT/OSkZACjid05jYE5K4z6aA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mUVax/btsxqtwCHJT/OSkZACjid05jYE5K4z6aA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mUVax/btsxqtwCHJT/OSkZACjid05jYE5K4z6aA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmUVax%2FbtsxqtwCHJT%2FOSkZACjid05jYE5K4z6aA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;282&quot; data-origin-width=&quot;2080&quot; data-origin-height=&quot;816&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flipper&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://fbflipper.com&quot;&gt;Flipper&lt;/a&gt; 는 Meta(구 Facebook)에서 개발한 디버깅 도구이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 애플리케이션을 개발할 때 유용하게 사용되는데, Flipper에서도 React Native의 로그를 확인할 수 있다. 동작 메커니즘은 터미널에 로그를 전달하는 것과 유사한데, 한 번 살펴보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flipper 내부에서는 로그 이벤트를 수신하기 위해 &lt;code&gt;ws://host/events&lt;/code&gt; 주소로 &lt;a href=&quot;https://github.com/facebook/flipper/blob/v0.180.0/desktop/flipper-server-core/src/devices/metro/metroDeviceManager.tsx#L68-L72&quot;&gt;웹소켓 커넥션&lt;/a&gt;을 맺는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Metro 번들러 코드를 살펴보면 &lt;code&gt;/events&lt;/code&gt; 에 해당하는 엔드포인트를 찾아볼 수 없다. 어디에 구현되어 있는 것일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론을 말하자면 &lt;a href=&quot;https://github.com/react-native-community/cli/blob/v11.3.2/packages/cli-plugin-metro/src/commands/start/runServer.ts#L75-L84&quot;&gt;@react-native-community/cli-plugin-metro&lt;/a&gt;에서 start 커맨드(개발 서버 실행)를 실행할 때 추가적인 웹 소켓을 구성하도록 Metro 구성에 포함하며, 구현체는 &lt;a href=&quot;https://github.com/react-native-community/cli/blob/v11.3.2/packages/cli-server-api/src/websocket/createEventsSocketEndpoint.ts&quot;&gt;@react-native-community/cli-server-api&lt;/a&gt; 패키지 내에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;a href=&quot;https://github.com/react-native-community/cli/blob/v11.3.2/packages/cli-plugin-metro/src/commands/start/runServer.ts#L48-L55&quot;&gt;아래와 같은 reporter 를 사용&lt;/a&gt;하도록 구현되어 있는데, 앞서 살펴본 &lt;code&gt;TerminalReporter&lt;/code&gt; 를 통해 터미널에 로그를 출력하는 것뿐만 아니라, 이벤트 소켓으로 데이터를 전송하도록 구현되어 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;1610&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yaJqj/btsxriuElJd/VVhQhBoG3EP3ssOE21zdZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yaJqj/btsxriuElJd/VVhQhBoG3EP3ssOE21zdZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yaJqj/btsxriuElJd/VVhQhBoG3EP3ssOE21zdZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyaJqj%2FbtsxriuElJd%2FVVhQhBoG3EP3ssOE21zdZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;685&quot; data-origin-width=&quot;1692&quot; data-origin-height=&quot;1610&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 전송된 메시지 데이터는 Flipper에서 수신하여 &lt;a href=&quot;https://github.com/facebook/flipper/blob/v0.180.0/desktop/flipper-server-core/src/devices/metro/MetroDevice.tsx#L80-L94&quot;&gt;로그 메시지를 UI 상에 렌더링&lt;/a&gt;&amp;nbsp;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flipper로 로그 메시지가 전달되는 과정을 정리하면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2638&quot; data-origin-height=&quot;1328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byte2B/btsxUbN74v5/cWflKaBxjl1MgXinpcqZIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byte2B/btsxUbN74v5/cWflKaBxjl1MgXinpcqZIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byte2B/btsxUbN74v5/cWflKaBxjl1MgXinpcqZIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbyte2B%2FbtsxUbN74v5%2FcWflKaBxjl1MgXinpcqZIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;362&quot; data-origin-width=&quot;2638&quot; data-origin-height=&quot;1328&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;HmrServer&lt;/code&gt; 로 메시지가 전달되는 부분까진 터미널 로그와 동일하다. 다만, 기본값이 아닌 커스터마이징 된 Reporter를 통해 이벤트 소켓으로 메시지를 다시 전달하는 과정이 추가되었고, Flipper에서는 해당 웹 소켓과 커넥션을 맺어 메시지를 수신하게 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HMR(Hot Module Replacement)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HMR(Hot Module Replacement)는 Metro 번들러에서 제공하는 핵심 기능 중 하나로, 개발 환경에서 코드의 변경 사항을 실시간으로 반영해 주는 기능이다. &lt;a href=&quot;https://www.npmjs.com/package/react-refresh&quot;&gt;react-refresh&lt;/a&gt; 기반으로 구현되어 있으며, 서버와 클라이언트 구현부 모두 상당히 복잡하기에 간략히 살펴보고 넘어가도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드의 변경 사항이 생길 경우 변경이 발생한 모듈과 의존성으로 참조하고 있는 모듈을 다시 빌드한 후 번들의 변경 사항 정보를 &lt;code&gt;HmrServer&lt;/code&gt; 를 통해 &lt;code&gt;HMRClient&lt;/code&gt; 로 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 전달되는 메시지의 형식은 &lt;code&gt;{ type: 'update' }&lt;/code&gt; 형태의 JSON 메시지를 &lt;a href=&quot;https://github.com/facebook/metro/blob/v0.76.5/packages/metro/src/HmrServer.js#L358-L376&quot;&gt;전달&lt;/a&gt;한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1768&quot; data-origin-height=&quot;1394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6MEeq/btsxp4X8wkS/A9ayzFisReOtG8KtlLimF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6MEeq/btsxp4X8wkS/A9ayzFisReOtG8KtlLimF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6MEeq/btsxp4X8wkS/A9ayzFisReOtG8KtlLimF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6MEeq%2Fbtsxp4X8wkS%2FA9ayzFisReOtG8KtlLimF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;568&quot; data-origin-width=&quot;1768&quot; data-origin-height=&quot;1394&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트에서는 이를 수신하여 런타임에 반영하며, 네이티브의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp#L124-L135&quot;&gt;globalEvalWithSourceUrl&lt;/a&gt; 를 호출하게 된다. 이 함수 역시 &lt;code&gt;JSIExecutor&lt;/code&gt; 로부터 런타임에 주입된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1686&quot; data-origin-height=&quot;1608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DLTwk/btsxkh4zTcA/tMI6qNJfszI72rzKjDqUHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DLTwk/btsxkh4zTcA/tMI6qNJfszI72rzKjDqUHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DLTwk/btsxkh4zTcA/tMI6qNJfszI72rzKjDqUHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDLTwk%2Fbtsxkh4zTcA%2FtMI6qNJfszI72rzKjDqUHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;687&quot; data-origin-width=&quot;1686&quot; data-origin-height=&quot;1608&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경에 대한 정보를 수신한 후 동적으로 평가된다는 것까지 살펴보았다. 자세한 HMR 구현은 &lt;code&gt;react-refresh&lt;/code&gt; 를 참조하길 바라며, HMR 메시지 타입과 동작 방식을 조금 더 살펴보고 싶다면 아래 코드를 참조하길 바란다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/metro/blob/v0.76.5/packages/metro-runtime/src/modules/types.flow.js#L28-L30&quot;&gt;메시지 타입&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/metro/blob/v0.76.5/packages/metro-runtime/src/modules/__tests__/HMRClient-test.js&quot;&gt;HMR 동작 테스트 코드&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Build Status&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 Metro 번들러를 실행시키고, 개발 모드의 애플리케이션을 실행시키면 Metro 번들러에서는 작업을 큐에 넣고 빌드를 시작하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NVvfx/btsxiefL4Yi/kkE79BAwfV1biNJFVAzSh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NVvfx/btsxiefL4Yi/kkE79BAwfV1biNJFVAzSh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NVvfx/btsxiefL4Yi/kkE79BAwfV1biNJFVAzSh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNVvfx%2FbtsxiefL4Yi%2FkkE79BAwfV1biNJFVAzSh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;270&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, 애플리케이션 상단에는 빌드 진행 상황(%)이 노출되는데 내부적으로 어떻게 동작하는지 한 번 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 모드의 애플리케이션을 실행하면 내부적으로 Metro 서버에 접근하여 번들을 로드하려고 시도한다. 이때 접근하는 프로토콜은 HTTP이며 접근하는 주소는 &lt;code&gt;GET /index.bundle&lt;/code&gt; 이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2350&quot; data-origin-height=&quot;260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ee3f0Z/btsxHZgko3h/Em1znGBacb2HF8JnvxU4Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ee3f0Z/btsxHZgko3h/Em1znGBacb2HF8JnvxU4Bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ee3f0Z/btsxHZgko3h/Em1znGBacb2HF8JnvxU4Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fee3f0Z%2FbtsxHZgko3h%2FEm1znGBacb2HF8JnvxU4Bk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;80&quot; data-origin-width=&quot;2350&quot; data-origin-height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소에는 쿼리 스트링이 포함되어 있는데 이를 살펴보면 플랫폼 정보, 개발 모드 여부, minify 처리 여부 등이 포함된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 헤더를 살펴보면 어떤 타입의 데이터를 요청하는지 확인 가능한데, 특이하게도 번들 데이터를 요청하는 것임에도 불구하고 타입은 &lt;code&gt;application/javascript&lt;/code&gt; 가 아닌 &lt;code&gt;multipart/mixed&lt;/code&gt; 이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lqtek/btsxiaEr826/lDoQHiKsqttVpDSgj0Zrk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lqtek/btsxiaEr826/lDoQHiKsqttVpDSgj0Zrk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lqtek/btsxiaEr826/lDoQHiKsqttVpDSgj0Zrk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flqtek%2FbtsxiaEr826%2FlDoQHiKsqttVpDSgj0Zrk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;160&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;multipart/mixed&lt;/code&gt; 타입은 한 번의 요청에서 여러 가지 타입이 결합된 응답을 수신할 수 있는데, Metro에서 번들 요청을 수신하면 어떤 형태로 데이터를 응답하는지 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 요청을 받게 되면 &lt;a href=&quot;https://github.com/facebook/metro/blob/v0.76.5/packages/metro/src/Server/MultipartResponse.js&quot;&gt;MultipartResponse&lt;/a&gt; 로 &lt;a href=&quot;https://github.com/facebook/metro/blob/v0.76.5/packages/metro/src/Server.js#L644&quot;&gt;wrapping&lt;/a&gt;한다. &lt;code&gt;MultipartResponse&lt;/code&gt; 는 &lt;code&gt;multipart/mixed&lt;/code&gt; 형태에 맞게 응답하도록 구현된 모듈이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3264&quot; data-origin-height=&quot;3040&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCMrfo/btsxvGaqf8l/CP7wWiKmhFGBugtCKuwFUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCMrfo/btsxvGaqf8l/CP7wWiKmhFGBugtCKuwFUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCMrfo/btsxvGaqf8l/CP7wWiKmhFGBugtCKuwFUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCMrfo%2FbtsxvGaqf8l%2FCP7wWiKmhFGBugtCKuwFUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;671&quot; data-origin-width=&quot;3264&quot; data-origin-height=&quot;3040&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;Content-Type: multipart/mixed; boundary=3beqjf3apnqeu3h5jqorms4i

--3beqjf3apnqeu3h5jqorms4i
header_key: header_value

data
--3beqjf3apnqeu3h5jqorms4i
header_key: header_value

data
--3beqjf3apnqeu3h5jqorms4i
header_key: header_value

data
--3beqjf3apnqeu3h5jqorms4i--&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 요청을 수신한 후 클라이언트로 데이터의 경계를 구분하기 위한 &lt;code&gt;boundary&lt;/code&gt; 값을 응답한다. 이후 응답이 완료될 때까지 클라이언트로 조각(Chunk) 난 응답을 전달할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 응답 조각은 &lt;code&gt;--{boundary}&lt;/code&gt; 로 구분되고, 응답의 종료는 &lt;code&gt;--{boundary}--&lt;/code&gt; 로 구분한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Metro 내부에서는 빌드 상태를 주기적으로 이벤트로 수신하고 이를 클라이언트로 응답한다. 이벤트에는 전체 모듈 수, 빌드된 모듈 수가 포함되어 있는데 이를 &lt;a href=&quot;https://github.com/facebook/metro/blob/v0.76.5/packages/metro/src/Server.js#L649-L690&quot;&gt;JSON 형태의 값으로 변환하여 응답&lt;/a&gt;한다. 이후 빌드가 완료되었을 때 최종적으로 번들 데이터를 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 응답되는 데이터를 살펴본다면 아래와 같은 값이 응답될 것이다.&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;Content-Type: multipart/mixed; boundary=3beqjf3apnqeu3h5jqorms4i

--3beqjf3apnqeu3h5jqorms4i
Contnet-Type: application/json

{&quot;done&quot;: 0, &quot;total&quot;: 10}
--3beqjf3apnqeu3h5jqorms4i
Contnet-Type: application/json

{&quot;done&quot;: 5, &quot;total&quot;: 10}
--3beqjf3apnqeu3h5jqorms4i
Contnet-Type: application/javascript

{BUNDLE_DATA}
--3beqjf3apnqeu3h5jqorms4i--&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/facebook/metro/blob/v0.76.5/packages/metro/src/Server.js#L921-L935&quot;&gt;번들링이 완료&lt;/a&gt;되면 번들 데이터를 전달하고 &lt;code&gt;multipart/mixed&lt;/code&gt; 응답 종료를 알리도록 구현되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 응답된 데이터는 각 플랫폼에서 수신하여 JSON 파싱을 거쳐 UI 상에 렌더링 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Android는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/BundleDownloader.java#L180-L218&quot;&gt;BundleDownloader&lt;/a&gt;, iOS는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/Base/RCTJavaScriptLoader.mm#L346-L359&quot;&gt;RCTJavaScriptLoader&lt;/a&gt;에서 처리되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리하며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 살펴본 내용은 개발과 관련된 내용들로 정리했다. 이미 개발을 하면서 접하고 있는 것들이지만 내부적으로 어떻게 동작하는지 궁금했던 사람들도 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 이번 글이 React Native Under The Hood 시리즈의 마지막 게시글이며 추후 기회가 된다면 더 다양한 내용에 대해 정리하여 공유할까 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여담이지만, 이 시리즈를 기획하게 된  계기는 사실 Metro 번들러가 아닌 자체적으로 번들러를 개발하는 과정으로부터 시작되었다. 작업을 하면서 엄청난 삽질을 하게 되었는데, 이 과정 속에서 알게 된 내용들을 정리한 결과가 바로 이번 시리즈의 게시글들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자체적으로 개발하던 번들러는 어느 정도 구현이 완료되어 현재 배포되어 있다. 관심이 있다면, 아래 저장소에서 확인 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/leegeunhyeok/react-native-esbuild&quot;&gt;https://github.com/leegeunhyeok/react-native-esbuild&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1696858428754&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - leegeunhyeok/react-native-esbuild: ⚡️ Esbuild based bundler for React Native&quot; data-og-description=&quot;⚡️ Esbuild based bundler for React Native. Contribute to leegeunhyeok/react-native-esbuild development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/leegeunhyeok/react-native-esbuild&quot; data-og-url=&quot;https://github.com/leegeunhyeok/react-native-esbuild&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/s16LJ/hyT9DoN3np/TCxK1DSKaI1GvHjZqfCWq0/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640&quot;&gt;&lt;a href=&quot;https://github.com/leegeunhyeok/react-native-esbuild&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/leegeunhyeok/react-native-esbuild&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/s16LJ/hyT9DoN3np/TCxK1DSKaI1GvHjZqfCWq0/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - leegeunhyeok/react-native-esbuild: ⚡️ Esbuild based bundler for React Native&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;⚡️ Esbuild based bundler for React Native. Contribute to leegeunhyeok/react-native-esbuild development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 글에서는 번들러를 개발하면서 겪었던 수많은 일들에 대해 간단하게 공유하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 이번 시리즈 글을 읽어준 독자들에게 감사하다는 인사말을 남기고 마무리하도록 하겠다.&lt;/p&gt;</description>
      <category>언어 &amp;amp; 프레임워크/React Native</category>
      <author>이근둥</author>
      <guid isPermaLink="true">https://codevkr.tistory.com/131</guid>
      <comments>https://codevkr.tistory.com/131#entry131comment</comments>
      <pubDate>Mon, 9 Oct 2023 22:35:33 +0900</pubDate>
    </item>
    <item>
      <title>React Native Under The Hood - 브릿지(Bridge)</title>
      <link>https://codevkr.tistory.com/130</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;rnh-bridge-cover.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bC3EO3/btsv8UhNP1Z/iz7pLmVAjuXZiUPh6r1Dek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bC3EO3/btsv8UhNP1Z/iz7pLmVAjuXZiUPh6r1Dek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bC3EO3/btsv8UhNP1Z/iz7pLmVAjuXZiUPh6r1Dek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbC3EO3%2Fbtsv8UhNP1Z%2Fiz7pLmVAjuXZiUPh6r1Dek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;320&quot; height=&quot;320&quot; data-filename=&quot;rnh-bridge-cover.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 글에서 구현된 네이티브 모듈이 어떻게 등록되고 런타임에서 접근할 수 있게 되는지 살펴보았다. 네이티브 모듈의 메소드가 호출되면 브릿지를 거치게 된다는 부분까지 살펴보았는데, 지난 내용에 이어 어떻게 네이티브로 전달되고, 자바스크립트 런타임에서는 결과를 어떻게 전달받게 되는지 살펴보려고 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;브릿지(Bridge)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 개발자라면 브릿지(Bridge)에 대해 들어본 적이 있을 것이다. 브릿지는 자바스크립트 런타임과 네이티브 간의 상호작용을 담당하는 React Native의 핵심 기능 중 하나이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 런타임에서 네이티브 모듈에 접근하여 동작을 요청하거나, 네이티브에서 자바스크립트 콜백 함수를 호출하여 결과를 전달하는 역할을 수행하는 개념이 곧 브릿지이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브릿지는 내부에서 어떻게 구현되어 있는지 한 번 살펴보도록 하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2752&quot; data-origin-height=&quot;1882&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M0W3W/btswbc3k3gW/EawQVPz792oTGbGH045adk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M0W3W/btswbc3k3gW/EawQVPz792oTGbGH045adk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M0W3W/btswbc3k3gW/EawQVPz792oTGbGH045adk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM0W3W%2Fbtswbc3k3gW%2FEawQVPz792oTGbGH045adk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;492&quot; data-origin-width=&quot;2752&quot; data-origin-height=&quot;1882&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈은 애플리케이션 초기 실행 시 모듈 저장소(Registry)에 등록되고, 프록시 객체를 통해 접근한다는 것은 지난 글에서 알아보았다. 모듈 접근 후 네이티브 메소드를 호출하고, 결과를 수신할 때에는 브릿지를 거치게 된다. 위 그림에서 강조한 부분을 조금 더 자세히 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2200&quot; data-origin-height=&quot;1182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dRaV4D/btsv9Jf8aaQ/y7mbD8bvbhzQbVOfGH91OK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dRaV4D/btsv9Jf8aaQ/y7mbD8bvbhzQbVOfGH91OK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dRaV4D/btsv9Jf8aaQ/y7mbD8bvbhzQbVOfGH91OK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdRaV4D%2Fbtsv9Jf8aaQ%2Fy7mbD8bvbhzQbVOfGH91OK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;387&quot; data-origin-width=&quot;2200&quot; data-origin-height=&quot;1182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;자바스크립트 런타임에서 네이티브 모듈에 접근하여 메소드를 호출할 때에는 &lt;code&gt;JSIExecutor&lt;/code&gt; 에서 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/cxxreact/JSExecutor.h#L32-L47&quot;&gt;ExecutorDelegate&lt;/a&gt; 로 작업을 위임하며, 실제 동작은 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/cxxreact/NativeToJsBridge.cpp#L39-L107&quot;&gt;JsToNativeBridge&lt;/a&gt;에서 처리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 결과를 네이티브에서 자바스크립트 런타임으로 전달할 때에는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/cxxreact/NativeToJsBridge.h&quot;&gt;NativeToJsBridge&lt;/a&gt; 에서 처리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림을 잘 기억하며 글을 살펴보도록 하자.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;네이티브 메소드 유형&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈 메소드의 유형은 총 3가지로 구분되며, 네이티브와 상호작용하는 로직은 &lt;code&gt;NativeModules&lt;/code&gt; 에 의해 추상화되어있다는 것은 지난 글에서 간단히 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈의 메소드가 호출되면 내부적으로 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/BatchedBridge/BatchedBridge.js&quot;&gt;BatchedBridge&lt;/a&gt; 로 네이티브 메소드 호출 요청이 전달되고 내부 구현에 따라 실제 네이티브 모듈로 전달되어 처리된다. 처리된 결과는 콜백 함수를 통해 자바스크립트로 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세히 알아보기에 앞서 네이티브 모듈이 호출되면 브릿지로 어떻게 전달되는지 내부를 한 번 들여다보도록 하자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;sync&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서에 따라 아래와 같이 명시적으로 동기 메소드로 정의한 경우, 메소드가 호출되면 &lt;code&gt;'sync'&lt;/code&gt; 유형으로 처리된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Android: &lt;a href=&quot;https://reactnative.dev/docs/native-modules-android#synchronous-methods&quot;&gt;https://reactnative.dev/docs/native-modules-android#synchronous-methods&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;iOS: &lt;a href=&quot;https://reactnative.dev/docs/native-modules-ios#synchronous-methods&quot;&gt;https://reactnative.dev/docs/native-modules-ios#synchronous-methods&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;@ReactMethod(isBlockingSynchronousMethod = true)
fun myMethod() { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(myMethod)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기 메소드가 호출되면 최종적으로 &lt;code&gt;BatchedBridge&lt;/code&gt; 의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/BatchedBridge/NativeModules.js#L141-L147&quot;&gt;callNativeSyncHook&lt;/a&gt; 메소드가 호출되고, 이때 메소드 인자와 모듈 정보가 함께 전달된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;async&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콜백 함수를 통해 동작하도록 구현한 경우, &lt;code&gt;'async'&lt;/code&gt; 유형으로 처리된다. 해당 타입의 메소드가 호출되면 최종적으로 &lt;code&gt;BatchedBridge&lt;/code&gt; 의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/BatchedBridge/NativeModules.js#L149-L154&quot;&gt;enqueueNativeCall&lt;/a&gt; 메소드가 호출된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;promise&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로미스를 통해 동작하도록 구현한 경우, &lt;code&gt;'promise'&lt;/code&gt; 타입으로 처리된다. 실제 동작은 &lt;code&gt;'async'&lt;/code&gt; 유형과 동일하게 콜백으로 동작하는데, 이를 프로미스로 한 번 wrapping 했을 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 역시 최종적으로 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/BatchedBridge/NativeModules.js#L107-L119&quot;&gt;enqueueNativeCall&lt;/a&gt; 메소드가 호출된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;BatchedBridge&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;BatchedBridge&lt;/code&gt; 는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/BatchedBridge/MessageQueue.js&quot;&gt;MessageQueue&lt;/a&gt; 인스턴스이며 이를 통해 자바스크립트 런타임에서 네이티브 모듈의 메소드를 호출하고, 네이티브로부터 결과를 수신하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 메소드가 호출되면 &lt;code&gt;BatchedBridge&lt;/code&gt; 의 메소드(&lt;code&gt;callNativeSyncHook&lt;/code&gt;, &lt;code&gt;enqueueNativeCall&lt;/code&gt;)를 통해 네이티브 모듈 메소드를 호출하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠시 후 자세히 살펴볼 내용인데, &lt;code&gt;BatchedBridge.callNativeSyncHook&lt;/code&gt; 은 전역에 정의되어 있는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/BatchedBridge/MessageQueue.js#L184&quot;&gt;nativeCallSyncHook&lt;/a&gt; 을 호출하고, &lt;code&gt;BatchedBridge.enqueueNativeCall&lt;/code&gt; 은 전역에 정의되어 있는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/BatchedBridge/MessageQueue.js#L320&quot;&gt;nativeFlushQueueImmediate&lt;/a&gt;를 호출하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이들은 런타임이 초기화될 때 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp#L91-L122&quot;&gt;JSIExecutor&lt;/a&gt;에 의해 전역 객체에 노출되는 함수이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3032&quot; data-origin-height=&quot;2312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bE1cox/btswBB1QKFC/YdK0f0lREfBLKyf7neBCOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bE1cox/btswBB1QKFC/YdK0f0lREfBLKyf7neBCOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bE1cox/btswBB1QKFC/YdK0f0lREfBLKyf7neBCOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbE1cox%2FbtswBB1QKFC%2FYdK0f0lREfBLKyf7neBCOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;549&quot; data-origin-width=&quot;3032&quot; data-origin-height=&quot;2312&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/BatchedBridge/BatchedBridge.js#L23-L26&quot;&gt;BatchedBridge&lt;/a&gt; 를 확인해 보면 &lt;code&gt;__fbBatchedBridge&lt;/code&gt; 라는 이름의 속성을 전역 객체에 정의하고 있는데, 네이티브에서 결과를 전달해 줄 때, 정의한 속성을 통해 &lt;code&gt;BatchedBridge&lt;/code&gt; 에 접근하고 이를 통해 결과를 전달하는 메커니즘으로 동작한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2480&quot; data-origin-height=&quot;1408&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zvjCl/btswaqHnmUX/gpSF3jhOJxMpYxPsBxg9sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zvjCl/btswaqHnmUX/gpSF3jhOJxMpYxPsBxg9sk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zvjCl/btswaqHnmUX/gpSF3jhOJxMpYxPsBxg9sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzvjCl%2FbtswaqHnmUX%2FgpSF3jhOJxMpYxPsBxg9sk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;409&quot; data-origin-width=&quot;2480&quot; data-origin-height=&quot;1408&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;브릿지가 동작하게 되는 흐름을 그림으로 정리해보면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2364&quot; data-origin-height=&quot;840&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/muJG5/btswatYqJsw/xA2YlfhKtcJ3kbYT9NyURk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/muJG5/btswatYqJsw/xA2YlfhKtcJ3kbYT9NyURk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/muJG5/btswatYqJsw/xA2YlfhKtcJ3kbYT9NyURk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmuJG5%2FbtswatYqJsw%2FxA2YlfhKtcJ3kbYT9NyURk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;256&quot; data-origin-width=&quot;2364&quot; data-origin-height=&quot;840&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 메소드가 호출되면 &lt;code&gt;JSIExecutor&lt;/code&gt; 에 의해 정의된 &lt;code&gt;nativeFlushQueueImmediate&lt;/code&gt;, &lt;code&gt;nativeCallSyncHook&lt;/code&gt; 함수가 호출되고, 네이티브 모듈 메소드의 결과는 &lt;code&gt;__fbBatchedBridge&lt;/code&gt; 를 통해 전달되는 구조이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MessageQueue&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;BatchedBridge&lt;/code&gt; 의 실체는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/BatchedBridge/MessageQueue.js&quot;&gt;MessageQueue&lt;/a&gt;라고 설명했다. &lt;code&gt;MessageQueue&lt;/code&gt; 는 네이티브 모듈을 호출하기 전에 호출 정보를 관리하고, 결과를 수신해 주는 역할을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈의 비동기(async, promise) 메소드가 호출되면 모듈 정보와 호출하고자 하는 메소드 정보가 큐(Queue)에 저장되고, 정해진 스케쥴링 조건에 따라 네이티브로 호출 정보를 전달하게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;callNativeSyncHook&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈의 동기 메소드가 호출되면 &lt;code&gt;callNativeSyncHook&lt;/code&gt; 메소드가 호출되는데 내부 구현을 살펴보면, 몇 가지 처리를 거친 후 &lt;code&gt;JSIExecutor&lt;/code&gt; 로 부터 주입된 &lt;code&gt;nativeCallSyncHook&lt;/code&gt; 함수가 호출된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1588&quot; data-origin-height=&quot;1556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AcDKY/btsv9P8vmeE/aA4hWLgZrd11ZKOri8kPP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AcDKY/btsv9P8vmeE/aA4hWLgZrd11ZKOri8kPP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AcDKY/btsv9P8vmeE/aA4hWLgZrd11ZKOri8kPP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAcDKY%2Fbtsv9P8vmeE%2FaA4hWLgZrd11ZKOri8kPP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;705&quot; data-origin-width=&quot;1588&quot; data-origin-height=&quot;1556&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;code&gt;nativeCallSyncHook&lt;/code&gt; 가 호출되기 전에 콜백 정보를 처리하고 있는데, 모든 네이티브 모듈의 메소드 호출에는 고유한 식별자(&lt;code&gt;_callID&lt;/code&gt;)가 존재하며 해당 식별자를 키로 갖는 콜백 정보를 &lt;code&gt;_successCallbacks&lt;/code&gt;, &lt;code&gt;_failureCallbacks&lt;/code&gt; 에 기록해 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 식별자는 결과를 네이티브로부터 수신할 때 사용되며, 식별자를 통해 결과를 전달할 콜백을 참조하고, 해당 콜백을 호출하여 결과를 전달하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주의 깊게 살펴볼 부분은 &lt;code&gt;global.nativeCallSyncHook&lt;/code&gt; 의 인자이다. 순서대로 모듈 ID, 메소드 ID, 네이티브 함수로 전달할 인자값인데 잘 기억해 두자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;enqueueNativeCall&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 메소드의 비동기(async, promise) 메소드가 호출되면 &lt;code&gt;enqueueNativeCall&lt;/code&gt; 메소드가 호출되는데 내부 구현을 살펴보면 역시 몇 가지 처리를 거친 후 &lt;code&gt;JSIExecutor&lt;/code&gt; 로 부터 주입된 &lt;code&gt; nativeFlushQueueImmediate&lt;/code&gt; 함수가 호출된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1634&quot; data-origin-height=&quot;1548&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0Wezv/btswkm5l3tI/zQGyZ85NYz7hXGM3tH2Rc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0Wezv/btswkm5l3tI/zQGyZ85NYz7hXGM3tH2Rc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0Wezv/btswkm5l3tI/zQGyZ85NYz7hXGM3tH2Rc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0Wezv%2Fbtswkm5l3tI%2FzQGyZ85NYz7hXGM3tH2Rc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;682&quot; data-origin-width=&quot;1634&quot; data-origin-height=&quot;1548&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;동기 메소드와 동일하게 콜백 정보가 호출 식별자와 함께 기록되고, 네이티브 메소드 호출 정보가 큐에 저장되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큐에 저장되는 값의 형태는 아래와 같으며 호출된 네이티브 모듈과 메소드의 ID, 그리고 호출할 때 전달된 인자값이 여기에 저장된다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;_queue = [
  [moduleId_1, moduleId_2, ...],
  [methodId_1, methodId_2, ...],
  [params_1, params_2, ...],
  _id
];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈의 비동기 메소드는 호출 즉시 정보가 전달되는 것이 아니라 &lt;code&gt;MessageQueue&lt;/code&gt; 내부의 큐에 저장되어 있다가 스케쥴링 조건에 따라 네이티브로 일괄 &lt;code&gt;flush&lt;/code&gt; 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케쥴링은 최소 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/BatchedBridge/MessageQueue.js#L315&quot;&gt;MIN_TIME_BETWEEN_FLUSHES_MS&lt;/a&gt; 간격으로 호출 정보가 전달되도록 구현되어 있다. 즉, 네이티브 모듈의 비동기 메소드가 짧은 시간 안에 여러 번 호출되더라도 큐에 저장되어 있다가 5ms 이후에 flush 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서도 주의 깊게 살펴볼 부분은 &lt;code&gt;global.nativeFlushQueueImmediate&lt;/code&gt; 호출 시 전달되는 인자이다. 인자 값으로 큐 데이터를 전달하는데, 큐에 저장되는 데이터 형태를 잘 기억해 두도록 하자.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Bridge Binding&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브릿지 동작은 &lt;code&gt;MessageQueue&lt;/code&gt; 를 통해 처리된다고 설명했다. 네이티브 모듈의 메소드가 호출된 후 등록된 콜백을 통해 결과를 수신하기 위해 &lt;code&gt;MessageQueue&lt;/code&gt; 가 네이티브에 바인딩되는데 이 부분을 한 번 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;1262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kzc0z/btsv8et47TJ/GBSkMZbExGIIkzKdkunGKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kzc0z/btsv8et47TJ/GBSkMZbExGIIkzKdkunGKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kzc0z/btsv8et47TJ/GBSkMZbExGIIkzKdkunGKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fkzc0z%2Fbtsv8et47TJ%2FGBSkMZbExGIIkzKdkunGKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;476&quot; data-origin-width=&quot;1908&quot; data-origin-height=&quot;1262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;JSIExecutor&lt;/code&gt; 를 살펴보면 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp#L384-L402&quot;&gt;bindBridge&lt;/a&gt; 메소드가 구현되어 있다. 이를 통해 앞서 살펴본 &lt;code&gt;__fbBatchedBridge&lt;/code&gt; 를 전역에서 참조한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 메소드 호출이 완료되어 결과를 전달할 시점이 찾아오면 네이티브에서는 바인딩된 MessageQueue의 &lt;code&gt;invokeCallbackAndReturnFlushedQueue&lt;/code&gt; 메소드를 호출하여 등록한 콜백을 통해 값을 전달하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 네이티브 메소드가 호출되면 내부적으로 어떤 흐름으로 동작하게 되는지 살펴보자.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JS to Native - Sync&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 메소드가 호출되면 &lt;code&gt;MessageQueue&lt;/code&gt;를 거쳐 메소드 유형에 따라 &lt;code&gt;JSIExecutor&lt;/code&gt; 에서 주입한 함수를 호출하고 있는 것을 확인했다. 동기 네이티브 메소드는 &lt;code&gt;BatchedBridge.callNativeSyncHook&lt;/code&gt; 를 거쳐 &lt;code&gt;global.nativeCallSyncHook&lt;/code&gt; 함수가 호출된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 호출되는 함수는 &lt;code&gt;JSIExecutor&lt;/code&gt; 에 구현되어 있는데, 내부적으로 어떤 동작을 수행하는지 파헤쳐보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(함수 이름이 &lt;code&gt;MessageQueue&lt;/code&gt; 의 메소드명과 유사하여 혼동될 수 있으니 주의하자)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;nativeCallSyncHook&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기 네이티브 메소드가 호출되면 &lt;code&gt;MessageQueue&lt;/code&gt; 를 거쳐 &lt;code&gt;nativeCallSyncHook&lt;/code&gt; 함수가 호출된다.&lt;br /&gt;해당 함수는 &lt;code&gt;JSIExecutor&lt;/code&gt; 로부터 주입되고 있는데, 코드를 살펴보면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2230&quot; data-origin-height=&quot;1468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T67UP/btsv7uxb13a/Q9gcTpX62A63V7qckzYLtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T67UP/btsv7uxb13a/Q9gcTpX62A63V7qckzYLtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T67UP/btsv7uxb13a/Q9gcTpX62A63V7qckzYLtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT67UP%2Fbtsv7uxb13a%2FQ9gcTpX62A63V7qckzYLtk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;474&quot; data-origin-width=&quot;2230&quot; data-origin-height=&quot;1468&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 상에 구현된 &lt;code&gt;nativeCallSyncHook&lt;/code&gt; 을 호출하고 있고, 해당 함수의 반환 값을 JSI를 통해 다시 자바스크립트 런타임으로 전달한다. 동기 메소드이기 때문에 별다른 비동기 콜백 처리 없이 반환 값을 바로 전달하는 모습을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브의 &lt;code&gt;nativeCallSyncHook&lt;/code&gt; 함수의 구현체는 &lt;code&gt;JSIExecutor&lt;/code&gt; 내에서 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;1558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JsLI4/btswkp131IJ/vtRU6PSskerBOUqz2ayagk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JsLI4/btswkp131IJ/vtRU6PSskerBOUqz2ayagk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JsLI4/btswkp131IJ/vtRU6PSskerBOUqz2ayagk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJsLI4%2Fbtswkp131IJ%2FvtRU6PSskerBOUqz2ayagk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;586&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;1558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;code&gt;MessageQueue&lt;/code&gt; 에서 &lt;code&gt;global.nativeCallSyncHook&lt;/code&gt; 함수를 호출할 때 전달하는 인자를 기억하는가?&lt;br /&gt;모듈 ID, 메소드 ID 그리고 네이티브 메소드로 전달할 인자값이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 순서에 따라 0, 1번 인덱스의 인자를 가져와 각각 모듈 ID, 메소드 ID로 사용하고, 네이티브 메소드를 호출할 때 2번 인덱스 값을 참조하여 전달하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 네이티브 메소드로 전달할 인자는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/jsi/jsi/JSIDynamic.cpp#L153-L202&quot;&gt;dynamicFromValue&lt;/a&gt; 메소드를 통해 JSI 호환 값으로 변환한다. 이전 글에서도 언급했지만 과거에는 이 부분에서 JSON 형태로 직렬화 처리 과정을 거쳤는데 지금은 그렇지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 사전 작업을 거친 후 네이티브 메소드를 호출하게 되는데, 네이티브 메소드 호출 후 결과값이 존재하지 않다면 &lt;code&gt;undefined&lt;/code&gt;, 존재한다면 JSI 인터페이스에 맞게 변환하여 반환하는 코드도 확인 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 메소드는 &lt;code&gt;_delegate&lt;/code&gt;(&lt;code&gt;ExecutorDelegate&lt;/code&gt;)의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/cxxreact/JSExecutor.h#L42-L46&quot;&gt;callSerializableNativeHook&lt;/a&gt; 메소드를 통해 호출하고 있는데, 이때 네이티브에서 호환가능한 형태로 직렬화가 이루어진다. 한 번 세부 구현을 살펴보도록 하자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;callSerializableNativeHook&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ExecutorDelegate&lt;/code&gt; 를 상속하고 있는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/cxxreact/NativeToJsBridge.cpp#L87-L94&quot;&gt;JsToNativeBridge&lt;/a&gt; 에서 실체를 확인할 수 있는데 구현된 코드는 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;1142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wcLP7/btswBDk4yA4/pMNNDyQF8HkkxqqVDJjfM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wcLP7/btswBDk4yA4/pMNNDyQF8HkkxqqVDJjfM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wcLP7/btswBDk4yA4/pMNNDyQF8HkkxqqVDJjfM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwcLP7%2FbtswBDk4yA4%2FpMNNDyQF8HkkxqqVDJjfM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;497&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;1142&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인자로 전달한 모듈 ID, 메소드 ID 그리고 네이티브 메소드로 전달할 인자 총 세 값을 모듈 저장소(&lt;code&gt;ModuleRegistry&lt;/code&gt;)의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/cxxreact/ModuleRegistry.h#L57-L60&quot;&gt;callSerializableNativeHook&lt;/a&gt; 메소드로 전달하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;1336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M0k8V/btsv9MjB1ct/rnczrCXeu9WLeW4Gdq6pj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M0k8V/btsv9MjB1ct/rnczrCXeu9WLeW4Gdq6pj1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M0k8V/btsv9MjB1ct/rnczrCXeu9WLeW4Gdq6pj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM0k8V%2Fbtsv9MjB1ct%2FrnczrCXeu9WLeW4Gdq6pj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;524&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;1336&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현된 코드를 살펴보면 모듈 ID에 대한 유효성 검증을 진행하고, 해당하는 모듈 ID를 참조하여 네이티브 모듈(&lt;code&gt;CxxNativeModule&lt;/code&gt;)의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/cxxreact/NativeModule.h#L39-L41&quot;&gt;callSerializableNativeHook&lt;/a&gt; 메소드를 호출하고 있는 모습을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;1414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lAvRn/btsv9OPeX9T/YAEQ3Bea2o6K9I367rzW4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lAvRn/btsv9OPeX9T/YAEQ3Bea2o6K9I367rzW4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lAvRn/btsv9OPeX9T/YAEQ3Bea2o6K9I367rzW4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlAvRn%2Fbtsv9OPeX9T%2FYAEQ3Bea2o6K9I367rzW4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;610&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;1414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 메소드의 유효성을 검증하고, 동기 함수(&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/cxxreact/CxxModule.h#L76&quot;&gt;syncFunc&lt;/a&gt;)를 호출하고 있다. 이때, 전달된 네이티브 메소드의 인자 값을 함께 넘겨주는 모습을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기 네이티브 메소드의 동작 순서를 정리하자면 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;BatchedBridge.callNativeSyncHook&lt;/code&gt; -&amp;gt; &lt;code&gt;global.nativeCallSyncHook&lt;/code&gt; -&amp;gt; &lt;code&gt;JsToNativeBridge.callSerializableNativeHook&lt;/code&gt; -&amp;gt; &lt;code&gt;ModuleRegistry.callSerializableNativeHook&lt;/code&gt; -&amp;gt; &lt;code&gt;CxxNativeModule.callSerializableNativeHook&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 메소드의 반환 값은 다시 거슬러 올라가 자바스크립트 런타임으로 전달될 것이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JS to Native - Async&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 네이티브 메소드는 &lt;code&gt;BatchedBridge.enqueueNativeCall&lt;/code&gt; 를 거쳐 &lt;code&gt;global.nativeFlushQueueImmediate&lt;/code&gt; 가 호출된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 호출되는 함수 역시 &lt;code&gt;JSIExecutor&lt;/code&gt; 에 구현되어 있는데, 내부적으로 어떤 동작을 수행하는지 파헤쳐보도록 하자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;nativeFlushQueueImmediate&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;global.nativeFlushQueueImmediate&lt;/code&gt; 의 실체는 &lt;code&gt;JSIExecutor&lt;/code&gt; 에서 확인할 수 있으며 구현된 코드는 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;1554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lIZZO/btsv9QfgqGg/sB9kE7Q91pgk6xG7zvKfSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lIZZO/btsv9QfgqGg/sB9kE7Q91pgk6xG7zvKfSk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lIZZO/btsv9QfgqGg/sB9kE7Q91pgk6xG7zvKfSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlIZZO%2Fbtsv9QfgqGg%2FsB9kE7Q91pgk6xG7zvKfSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;617&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;1554&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브의 &lt;code&gt;callNativeModules&lt;/code&gt; 를 호출하고 있는데, 인자로 &lt;code&gt;MessageQueue&lt;/code&gt; 에서 전달한 큐 값을 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 메소드이기 때문에 자바스크립트 런타임으로부터 메소드 호출 요청을 수신한 후 즉시 &lt;code&gt;undefined&lt;/code&gt; 를 반환하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;callNativeModule&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;JSIExecutor&lt;/code&gt; 내에서 구현된 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp#L404-L418&quot;&gt;callNativeModules&lt;/a&gt; 를 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rJN6g/btsv7wPkTyM/a3g4MFJc84zyXvusv5JG5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rJN6g/btsv7wPkTyM/a3g4MFJc84zyXvusv5JG5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rJN6g/btsv7wPkTyM/a3g4MFJc84zyXvusv5JG5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrJN6g%2Fbtsv7wPkTyM%2Fa3g4MFJc84zyXvusv5JG5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;229&quot; data-origin-width=&quot;1774&quot; data-origin-height=&quot;564&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;동기 메소드와 유사하게 인자 값을 JSI 호환 값으로 변환한 후&lt;code&gt;ExecutorDelegate&lt;/code&gt; 의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/cxxreact/NativeToJsBridge.cpp#L54-L85&quot;&gt;callNativeMethod&lt;/a&gt; 메소드를 호출하며 값을 전달하고 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;callNativeMethod&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1754&quot; data-origin-height=&quot;1554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYAFE0/btsv8QfpaGf/e7drYEF8EIZIZGckFyrkB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYAFE0/btsv8QfpaGf/e7drYEF8EIZIZGckFyrkB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYAFE0/btsv8QfpaGf/e7drYEF8EIZIZGckFyrkB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYAFE0%2Fbtsv8QfpaGf%2Fe7drYEF8EIZIZGckFyrkB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;638&quot; data-origin-width=&quot;1754&quot; data-origin-height=&quot;1554&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;여기서부터 동기 메소드와 구현된 부분이 조금 다른데 먼저 인자 값을 &lt;a href=&quot;https://github.com/facebook/react-native/blob/main/packages/react-native/ReactCommon/cxxreact/MethodCall.cpp&quot;&gt;MethodCall&lt;/a&gt; 형태로 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;MessageQueue&lt;/code&gt; 에서 &lt;code&gt;global.nativeFlushQueueImmediate&lt;/code&gt; 를 호출할 때 전달한 인자 값을 기억하는가? 모듈ID, 메소드ID, 인자값이 결합된 2차원 배열 형태의 값이었다. 이를 개별적인 &lt;code&gt;MethodCall&lt;/code&gt; 형태로 변환하여 모듈 저장소(&lt;code&gt;ModuleRegistry&lt;/code&gt;)의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/614afb598335de388d15f9d9f2ec67d1b6db2790/packages/react-native/ReactCommon/cxxreact/ModuleRegistry.cpp#L214-L224&quot;&gt;callNativeMethod&lt;/a&gt; 를 호출하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 조건문은 배치 처리 상태를 관리하고 React Native 내부의 스케줄링 처리를 수행하기 위한 부분이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;1320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Sg06r/btswBFiSE7I/YmG2MyM0wNFU7CgrjXTMDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Sg06r/btswBFiSE7I/YmG2MyM0wNFU7CgrjXTMDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Sg06r/btswBFiSE7I/YmG2MyM0wNFU7CgrjXTMDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSg06r%2FbtswBFiSE7I%2FYmG2MyM0wNFU7CgrjXTMDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;528&quot; data-origin-width=&quot;1800&quot; data-origin-height=&quot;1320&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;code&gt;ModuleRegistry&lt;/code&gt; 의 &lt;code&gt;callNativeMethod&lt;/code&gt; 에서는 동기 메소드와 동일하게 모듈 ID의 유효성을 확인한 후 네이티브 모듈의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/cxxreact/CxxNativeModule.cpp#L121-L224&quot;&gt;invoke&lt;/a&gt; 메소드를 호출한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;invoke&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;2298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9Hquu/btswcJs2rHO/7a9uQKhAsYShtyQao1oI6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9Hquu/btswcJs2rHO/7a9uQKhAsYShtyQao1oI6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9Hquu/btswcJs2rHO/7a9uQKhAsYShtyQao1oI6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9Hquu%2FbtswcJs2rHO%2F7a9uQKhAsYShtyQao1oI6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;912&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;2298&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 유효성 검사 조건이 구현되어 있으나 코드가 길어지는 관계로 일부분 생략했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인자로 전달받은 ID를 통해 대상 메소드를 참조한다. 이후 조건에서 콜백 수를 확인하여 네이티브 메소드 호출 시 사용할 인자를 미리 정리하는데 콜백 함수가 하나인 경우(onSucess), 콜백 함수가 두 개인 경우(onSuccess, onError)에 따라 처리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콜백 함수는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/cxxreact/CxxNativeModule.cpp#L25-L38&quot;&gt;makeCallback&lt;/a&gt; 메소드를 통해 생성되며, 네이티브 메소드는 &lt;code&gt;MessageQueueThread&lt;/code&gt; 를 통해 별도 스레드에서 호출되며 비동기적으로 처리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 거쳐온 과정을 순서대로 정리하면 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;BatchedBridge.enqueueNativeCall&lt;/code&gt; -&amp;gt; &lt;code&gt;global.nativeFlushQueueImmediate&lt;/code&gt; -&amp;gt; &lt;code&gt;JSIExecutor.callNativeModules&lt;/code&gt; -&amp;gt; &lt;code&gt;JsToNativeBridge.callNativeModules&lt;/code&gt; -&amp;gt; &lt;code&gt;ModuleRegistry.callNativeMethod&lt;/code&gt; -&amp;gt; &lt;code&gt;CxxNativeModule.invoke&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Native to JS&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기 메소드는 별다른 처리 없이 JSI를 통해 동기적으로 결과 값이 반환되는 것을 확인했다. 비동기 메소드의 경우에는 &lt;code&gt;MessageQueue&lt;/code&gt; 에서 등록된 콜백을 통해 결과 값이 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 메소드가 호출될 때 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/cxxreact/CxxNativeModule.cpp#L25-L38&quot;&gt;makeCallback&lt;/a&gt;을 통해 결과를 전달하기 위한 람다 함수가 생성되는 것을 확인했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3272&quot; data-origin-height=&quot;2304&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kJq8L/btsv8iXzE7x/oCnwQZGxpdU7eNskmL6PE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kJq8L/btsv8iXzE7x/oCnwQZGxpdU7eNskmL6PE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kJq8L/btsv8iXzE7x/oCnwQZGxpdU7eNskmL6PE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkJq8L%2Fbtsv8iXzE7x%2FoCnwQZGxpdU7eNskmL6PE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;507&quot; data-origin-width=&quot;3272&quot; data-origin-height=&quot;2304&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;비동기 네이티브 메소드의 실행이 완료되면 생성된 람다 함수가 호출되는데, 람다 함수의 매개변수(&lt;code&gt;folly::dynamic args&lt;/code&gt;) 값은 캡처해 둔 &lt;code&gt;Instnace&lt;/code&gt; 의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/cxxreact/Instance.cpp#L182-L186&quot;&gt;callJsCallback&lt;/a&gt; 메소드로 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 메소드의 결과 값 뿐만 아니라 콜백 함수의 식별값도 함께 전달되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;callJsCallback&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZb0LE/btswbCgvhtc/lUG3vsv1tzVTU8ML2BGAJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZb0LE/btswbCgvhtc/lUG3vsv1tzVTU8ML2BGAJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZb0LE/btswbCgvhtc/lUG3vsv1tzVTU8ML2BGAJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZb0LE%2FbtswbCgvhtc%2FlUG3vsv1tzVTU8ML2BGAJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;219&quot; data-origin-width=&quot;1834&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 &lt;code&gt;NativeToJsBridge&lt;/code&gt; 의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/cxxreact/NativeToJsBridge.cpp#L211-L240&quot;&gt;invokeCallback&lt;/a&gt; 메소드를 호출하여 값을 전달한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;invokeCallback&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1718&quot; data-origin-height=&quot;996&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfEKGF/btsv7nZdg32/wFPHIwhIICyT1ij1NBelh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfEKGF/btsv7nZdg32/wFPHIwhIICyT1ij1NBelh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfEKGF/btsv7nZdg32/wFPHIwhIICyT1ij1NBelh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfEKGF%2Fbtsv7nZdg32%2FwFPHIwhIICyT1ij1NBelh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;417&quot; data-origin-width=&quot;1718&quot; data-origin-height=&quot;996&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 자바스크립트 런타임과 상호작용 하는 &lt;code&gt;JSExecutor&lt;/code&gt; 의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp#L270-L289&quot;&gt;invokeCallback&lt;/a&gt; 을 호출하여 값을 전달하게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;1306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bY0cad/btsv7y0GhDH/qcKAnC9xa2tW2kSe4wfbBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bY0cad/btsv7y0GhDH/qcKAnC9xa2tW2kSe4wfbBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bY0cad/btsv7y0GhDH/qcKAnC9xa2tW2kSe4wfbBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbY0cad%2Fbtsv7y0GhDH%2FqcKAnC9xa2tW2kSe4wfbBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;524&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;1306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;JSIExecutor&lt;/code&gt; 내에서는 바인딩된 &lt;code&gt;MessageQueue&lt;/code&gt; 의 &lt;code&gt;invokeCallbackAndReturnFlushedQueue&lt;/code&gt; 를 호출하여 결과를 전달한다. 호출할 때에는 콜백 함수의 식별값(ID), 그리고 네이티브 메소드의 결과 값이 전달되며, 여기서도 JSON 역직렬화 과정 없이 JSI 기반 값으로 바로 전달된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;invokeCallbackAndReturnFlushedQueue&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;1558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ovjkp/btsv7sMXJ8Z/80Ihv8y0eGIsq96EEEtXhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ovjkp/btsv7sMXJ8Z/80Ihv8y0eGIsq96EEEtXhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ovjkp/btsv7sMXJ8Z/80Ihv8y0eGIsq96EEEtXhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fovjkp%2Fbtsv7sMXJ8Z%2F80Ihv8y0eGIsq96EEEtXhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;862&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;1558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSI를 통해 네이티브로부터 전달받은 인자(콜백 ID, 결과 값)를 수신하고, 등록된 콜백을 호출하여 값을 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 네이티브 메소드의&amp;nbsp;결과가 자바스크립트 런타임으로 돌아오기까지 여러 과정을 거쳐왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서를 한 번 정리해 보면 아래와 같은 순서로 처리된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Instance.callJsCallback&lt;/code&gt; -&amp;gt; &lt;code&gt;NativeToJsBridge.invokeCallback&lt;/code&gt; -&amp;gt; &lt;code&gt;MessageQueue.invokeCallbackAndReturnFlushedQueue&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리하며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈의 메소드가 호출되는 순간 수많은 모듈들을 거쳐 네이티브 모듈의 메소드가 호출되고, 결과를 자바스크립트 런타임으로 전달하는 과정 또한 간단하지만은 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 그림으로 보면 이전에 살펴본 런타임과 네이티브 모듈의 주요 구성 요소들을 거쳐 JSI를 통해 네이티브로 전달되고, 이들의 결과가 다시 JSI를 통해 자바스크립트 런타임으로 전달된다는 것이 브릿지의 전부일뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서 설명한 개념 외에도 여러 가지 처리들이 포함되어 있으나, 글에서 설명한 메커니즘 정도 이해했다면 나중에 React Native의 내부 코드를 들여다보았을 때 따라가는데 큰 어려움은 없을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 이러한 복잡한 구성 요소들로 묶여 있는 React Native 애플리케이션을 개발하는 데 있어 개발자들에게 제공되고 있는 기능들과 디버깅을 위한 기능들이 어떻게 동작하는지 간단히 살펴보려고 한다.&lt;/p&gt;</description>
      <category>언어 &amp;amp; 프레임워크/React Native</category>
      <author>이근둥</author>
      <guid isPermaLink="true">https://codevkr.tistory.com/130</guid>
      <comments>https://codevkr.tistory.com/130#entry130comment</comments>
      <pubDate>Tue, 3 Oct 2023 00:36:46 +0900</pubDate>
    </item>
    <item>
      <title>React Native Under The Hood - 네이티브 모듈(Native Modules)</title>
      <link>https://codevkr.tistory.com/129</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;rnh-native-modules-cover.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw80eg/btst7Udld5Y/2pUeJSKKDrrdOeyy46LlIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw80eg/btst7Udld5Y/2pUeJSKKDrrdOeyy46LlIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw80eg/btst7Udld5Y/2pUeJSKKDrrdOeyy46LlIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw80eg%2Fbtst7Udld5Y%2F2pUeJSKKDrrdOeyy46LlIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;320&quot; height=&quot;320&quot; data-filename=&quot;rnh-native-modules-cover.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 글에서는 코드가 번들링 되고 자바스크립트 엔진에서 실행되기까지 거치는 과정에 대해 살펴보았다. 이번 글에서는 React Native의 핵심 기능 중 하나인 네이티브 모듈이 어떻게 동작하는지 파헤쳐보자.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Native Modules&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈이란, 네이티브(Kotlin, Java, Swift, Objective-C)에서 구현한 클래스 인스턴스를 자바스크립트 런타임에 노출시켜 실행시킬 수 있도록 하는 React Native의 핵심 기능이다. 이를 통해 네이티브와 상호작용 하여 자바스크립트만으로 할 수 없는 동작을 구현할 수 있게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;새로운 아키텍처가 릴리즈된 이후, Native Modules는 이제 레거시(Legacy)가 되었다. 다만 아직 Deprecated 된 것은 아니고, 새로운 아키텍처와 기존 아키텍처가 구분되어 있을 뿐이다. 수년간 이어진 React Native 생태계의 크기는 무시할 수 없는 수준이기에, 지금 당장 대체될 일은 없다고 본다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;지난 글에서 소개했던 다이어그램이다. 이번 글에서는 빨간색으로 강조한&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;네이티브 모듈 부분을 살펴보려고 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2610&quot; data-origin-height=&quot;1810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eeOd29/btswbfZXu3Z/ADZ1gnymK7XqGfZdlNVguk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eeOd29/btswbfZXu3Z/ADZ1gnymK7XqGfZdlNVguk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eeOd29/btswbfZXu3Z/ADZ1gnymK7XqGfZdlNVguk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeeOd29%2FbtswbfZXu3Z%2FADZ1gnymK7XqGfZdlNVguk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;499&quot; data-origin-width=&quot;2610&quot; data-origin-height=&quot;1810&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;주요 구성 요소는 네이티브 모듈과 이들을 관리하는 저장소(Registry)가 되겠다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈은 공식 문서에 잘 설명되어있기에 쉽게 구현할 수 있다. React Native 내에서는 구현한 모듈이 어떻게 처리되는지 살펴보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 구현 관점에서 알아보고 싶다면, 공식 문서(&lt;a href=&quot;https://reactnative.dev/docs/native-modules-android&quot;&gt;Android&lt;/a&gt;, &lt;a href=&quot;https://reactnative.dev/docs/native-modules-ios&quot;&gt;iOS&lt;/a&gt;)를 참조하길 바란다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Android 모듈&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 의 Android 네이티브 모듈은 Kotlin 혹은 Java 언어를 통해 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈을 구현하는 방법은 생각보다 간단한데, &lt;code&gt;ReactContextBaseJavaModule&lt;/code&gt; 클래스를 상속하여 요구되는 인터페이스를 구현하는 것이다. &lt;code&gt;BaseJavaModule&lt;/code&gt; 클래스를 상속하거나, &lt;code&gt;NativeModule&lt;/code&gt; 인터페이스를 구현하는 방법도 있으나, 공식 문서에서는 &lt;code&gt;ReactContextBaseJavaModule&lt;/code&gt; 클래스를 상속하여 구현하는 것을 권장하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 Kotlin 기반의 네이티브 모듈 샘플 코드이다.  Android 네이티브 모듈은 어떻게 구현하고 React Native에 등록되는지 살펴보도록 하자.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;// ZendeskMessagingModule.kt
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod

class ZendeskMessagingModule(private val reactContext: ReactApplicationContext) :
  ReactContextBaseJavaModule(reactContext) {
  private val module: ZendeskNativeModule = ZendeskNativeModule.getInstance()
  private var initialized = false

  override fun getName(): String {
    return &quot;ZendeskMessaging&quot;
  }

  @ReactMethod
  fun reset() {
    ...
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;// ZendeskMessagingPackage.kt
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager

class ZendeskMessagingPackage : ReactPackage {
  override fun createNativeModules(reactContext: ReactApplicationContext): List&amp;lt;NativeModule&amp;gt; {
    return listOf(ZendeskMessagingModule(reactContext))
  }

  override fun createViewManagers(reactContext: ReactApplicationContext): List&amp;lt;ViewManager&amp;lt;*, *&amp;gt;&amp;gt; {
    return emptyList()
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 이야기 했던 것처럼 &lt;code&gt;ReactContextBaseJavaModule&lt;/code&gt; 클래스를 상속하는 클래스를 정의하고 있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 컨텍스트로 특정 메소드를 노출시키고 싶다면, 모듈 메소드에 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactMethod.java&quot;&gt;@ReactMethod&lt;/a&gt; 어노테이션을 추가해주기만 하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈과 메소드를 구현하는 것은 이게 전부다. 생각보다 간단하지 않은가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브에서 기능을 구현하고 React Native에서 제공하는 ReactPackage 형태로 래핑(wrapping)하면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;iOS 모듈&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 의 iOS 네이티브 모듈은 Objective-C 기반으로 구현하게 된다. 물론 기능은 Swift로도 구현이 가능하지만 래핑 할 때에는 Objective-C 만 가능하다. Swift로 기능을 구현하고자 할 경우에는 XCode의 &lt;a href=&quot;https://developer.apple.com/documentation/swift/importing-swift-into-objective-c&quot;&gt;헤더 생성(Header Generation)&lt;/a&gt; 기능을 통해 Objective-C 에서 Swift 모듈을 참조하는 형태로 구현이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본론으로 돌아와서, iOS의 네이티브 모듈 또한 안드로이드와 마찬가지로 직접 하나부터 열까지 구현할 필요는 없고 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/Base/RCTBridgeModule.h&quot;&gt;RCTBridgeModule.h&lt;/a&gt; 에 사전 정의되어있는 매크로를 사용하여 네이티브 모듈 그리고 메소드를 쉽게 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 Objective-C 기반의 네이티브 모듈 샘플 코드이다. 한 번 해당 코드를 기준으로 살펴보도록 하자.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;#import &amp;lt;React/RCTBridgeModule.h&amp;gt;

@interface RCT_EXTERN_MODULE(ZendeskMessaging, NSObject)

RCT_EXTERN_METHOD(reset)

...

@end&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 이름은 &lt;code&gt;ZendeskMessaging&lt;/code&gt;, 그리고 모듈 내에 포함된 메소드는 &lt;code&gt;reset&lt;/code&gt; 하나로 구성되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈과 메소드를 구현하기 위해 사용하는 매크로는 몇 가지가 있는데 이번 글에서는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/Base/RCTBridgeModule.h#L302&quot;&gt;RCT_EXTERN_MODULE&lt;/a&gt; 그리고 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/Base/RCTBridgeModule.h#L319&quot;&gt;RCT_EXTERN_METHOD&lt;/a&gt; 를 기준으로 설명하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈 구현은 인터페이스를 정의하는 것으로 시작하는데, &lt;code&gt;RCT_EXTERN_MODULE&lt;/code&gt; 매크로에 사용하고자 하는 모듈의 이름을 추가하기만 하면 된다. 네이티브 메소드도 모듈과 동일하게 &lt;code&gt;RCT_EXTERN_METHOD&lt;/code&gt; 매크로를 통해 정의하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;RCT_EXTERN_MODULE&lt;/code&gt; 과 &lt;code&gt;RCT_EXTERN_METHOD&lt;/code&gt; 매크로 정의를 살펴보면 내부적으로 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/Base/RCTBridgeModule.h#L307-L313&quot;&gt;RCT_EXTERN_REMAP_MODULE&lt;/a&gt;, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/Base/RCTBridgeModule.h#L331-L336&quot;&gt;_RCT_EXTERN_REMAP_METHOD&lt;/a&gt; 매크로를 사용하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 살펴본 네이티브 모듈 코드는 컴파일 전처리 시점에 아래와 같이 변경된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2462&quot; data-origin-height=&quot;1700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oYFqP/btsueLGxXo9/veGAeuxX1X6MwMvVlCFLe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oYFqP/btsueLGxXo9/veGAeuxX1X6MwMvVlCFLe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oYFqP/btsueLGxXo9/veGAeuxX1X6MwMvVlCFLe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoYFqP%2FbtsueLGxXo9%2FveGAeuxX1X6MwMvVlCFLe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;497&quot; data-origin-width=&quot;2462&quot; data-origin-height=&quot;1700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 살펴볼 부분은 초기화 코드에서 &lt;code&gt;RCTRegisterModule&lt;/code&gt; 를 통해 구현한 모듈 클래스를 등록하는 부분(노란색)이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; font-size: 25.92px;&quot;&gt;Native Module Registry&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; font-size: 25.92px;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; font-size: 16px; letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; font-size: 16px; letter-spacing: 0px;&quot;&gt;구현된 네이티브 모듈을 앱 내에서 사용하기 위해선 등록 과정이 필요하다. 과거에는 직접 Linking 과정을 진행했어야 하는데, 오늘날의 React Native는 Auto Linking을 지원하기 때문에 특별한 경우가 아니라면 추가적인 등록 과정을 진행하지 않아도 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Auto Linking이 어떻게 동작하는지 궁금하다면 &lt;a href=&quot;https://github.com/react-native-community/cli/blob/main/docs/autolinking.md&quot;&gt;문서&lt;/a&gt;를 참조하길 바란다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Android 모듈 등록&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈을 구현한 React Package(이하 패키지)는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java&quot;&gt;ReactNativeHost&lt;/a&gt; 의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/template/android/app/src/main/java/com/helloworld/MainApplication.java#L22-L29&quot;&gt;getPackages&lt;/a&gt; 를 통해 React Native로 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으론 Auto Linking 과정에서 통해 네이티브 모듈이 포함된 대상의 패키지가 자동으로 생성되며, 수동으로 네이티브 모듈 패키지를 추가할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 등록된 패키지들은 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java&quot;&gt;ReactInstanceManager&lt;/a&gt; 인스턴스가 생성될 때 사용되며 &lt;code&gt;ReactNativeHost&lt;/code&gt;의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java#L86-L88&quot;&gt;getPackages&lt;/a&gt; 메소드를 통해 전달된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1694&quot; data-origin-height=&quot;1692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/egWpE8/btst89uIur4/DBRNUoBelDEVpeyxXEqJZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/egWpE8/btst89uIur4/DBRNUoBelDEVpeyxXEqJZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/egWpE8/btst89uIur4/DBRNUoBelDEVpeyxXEqJZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FegWpE8%2Fbtst89uIur4%2FDBRNUoBelDEVpeyxXEqJZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;719&quot; data-origin-width=&quot;1694&quot; data-origin-height=&quot;1692&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;code&gt;ReactInstanceManager&lt;/code&gt; 는 &lt;code&gt;ReactApplicationContext&lt;/code&gt; 를 생성하는 역할을 수행하는데, 이때 앞서 확인했던 패키지들을 초기화한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;2414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvDY7X/btsuejJ6xt9/4qJejmpyK2ZqGcD0UlDYSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvDY7X/btsuejJ6xt9/4qJejmpyK2ZqGcD0UlDYSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvDY7X/btsuejJ6xt9/4qJejmpyK2ZqGcD0UlDYSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvDY7X%2FbtsuejJ6xt9%2F4qJejmpyK2ZqGcD0UlDYSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;1066&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;2414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록했던 패키지들을 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/NativeModuleRegistryBuilder.java&quot;&gt;NativeModuleRegistryBuilder&lt;/a&gt; 로 전달하여 처리하는 코드를 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 노란색으로 강조해 둔 부분을 잘 기억하도록 하고, &lt;code&gt;NativeModuleRegistryBuilder&lt;/code&gt; 내에서 어떤 일들이 일어나는지 이어서 확인해 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1612&quot; data-origin-height=&quot;1806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXEhYb/btsugUJMEKV/55zFlyVmKhoKpRPQS7BOR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXEhYb/btsugUJMEKV/55zFlyVmKhoKpRPQS7BOR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXEhYb/btsugUJMEKV/55zFlyVmKhoKpRPQS7BOR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXEhYb%2FbtsugUJMEKV%2F55zFlyVmKhoKpRPQS7BOR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;807&quot; data-origin-width=&quot;1612&quot; data-origin-height=&quot;1806&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지 인스턴스로부터 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ModuleHolder.java&quot;&gt;ModuleHolder&lt;/a&gt; 를 가져오고 있는데, 이는 모듈을 지연 로딩(Lazy Loading) 가능하도록 돕기 위한 클래스이다. 패키지는 &lt;code&gt;ModuleHolder&lt;/code&gt; 형태가 되어 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeModuleRegistry.java&quot;&gt;NativeModuleRegistry&lt;/a&gt; 인스턴스를 생성할 때 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생성된 &lt;code&gt;NativeModuleRegistry&lt;/code&gt; 인스턴스는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstance.java&quot;&gt;CatalystInstance&lt;/a&gt; 인스턴스를 생성할 때 전달되는데,&lt;br /&gt;&lt;code&gt;CatalystInstance&lt;/code&gt;는 Java에서 자바스크립트 런타임에 접근하거나, 반대로 자바스크립트에서 Java/Kotlin 에 접근할 수 있는 보다 고차원적인 API를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native에서 기본적으로 제공하는 모달(Modal)의 구현체를 확인해 보면, 아래와 같이 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java#L466&quot;&gt;네이티브 모듈을 참조&lt;/a&gt;하고 있는 코드를 확인할 수 있는데, 코드의 흐름을 따라가 보면 &lt;code&gt;ReactModalHostView -&amp;gt; ReactContext.getNativeModule -&amp;gt; CatalystInstance.getNativeModule -&amp;gt; NativeModuleRegistry.getModule&lt;/code&gt; 순서로 호출되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1782&quot; data-origin-height=&quot;1882&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdmEwA/btst7UkaGF9/OPAb3rhFod5CCSF8OFBHT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdmEwA/btst7UkaGF9/OPAb3rhFod5CCSF8OFBHT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdmEwA/btst7UkaGF9/OPAb3rhFod5CCSF8OFBHT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdmEwA%2Fbtst7UkaGF9%2FOPAb3rhFod5CCSF8OFBHT0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;760&quot; data-origin-width=&quot;1782&quot; data-origin-height=&quot;1882&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1934&quot; data-origin-height=&quot;1606&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MBLzj/btsufkhHAex/L5y5jntZLAvEZHmEKQOeqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MBLzj/btsufkhHAex/L5y5jntZLAvEZHmEKQOeqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MBLzj/btsufkhHAex/L5y5jntZLAvEZHmEKQOeqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMBLzj%2FbtsufkhHAex%2FL5y5jntZLAvEZHmEKQOeqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;598&quot; data-origin-width=&quot;1934&quot; data-origin-height=&quot;1606&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;iOS 모듈 등록&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 살펴본 iOS 네이티브 모듈 코드에서 &lt;code&gt;RCTRegisterModule&lt;/code&gt; 를 통해 모듈을 초기화하는 코드를 확인했었다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;__attribute__((constructor)) static void initialize_ZendeskMessaging()
{
  RCTRegisterModule([ZendeskMessaging class]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Native 코드를 들여다보면 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/Base/RCTBridge.m#L59&quot;&gt;RCTRegisterModule&lt;/a&gt; 구현체를 발견할 수 있는데, 초기화된 네이티브 모듈이 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/Base/RCTBridge.m#L26&quot;&gt;RCTModuleClasses&lt;/a&gt; 배열에 모두 등록되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;static NSMutableArray&amp;lt;Class&amp;gt; *RCTModuleClasses;
static dispatch_queue_t RCTModuleClassesSyncQueue;
NSArray&amp;lt;Class&amp;gt; *RCTGetModuleClasses(void)
{
  __block NSArray&amp;lt;Class&amp;gt; *result;
  dispatch_sync(RCTModuleClassesSyncQueue, ^{
    result = [RCTModuleClasses copy];
  });
  return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;RCTRegisterModule&lt;/code&gt; 을 통해 등록된 모듈들은 &lt;code&gt;RCTModuleClasses&lt;/code&gt; 에 저장된다. 이는 브릿지 모듈이 초기화될 때 참조되는데 아래 코드를 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;1886&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZjVjK/btst6M7E3u6/4CQsgly0iKZIzlcHXXRNKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZjVjK/btst6M7E3u6/4CQsgly0iKZIzlcHXXRNKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZjVjK/btst6M7E3u6/4CQsgly0iKZIzlcHXXRNKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZjVjK%2Fbtst6M7E3u6%2F4CQsgly0iKZIzlcHXXRNKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;969&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;1886&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 흐름을 따라가 보면 먼저 초기화될 때 &lt;code&gt;RCTGetModuleClasses&lt;/code&gt; 를 통해 등록된 모듈(&lt;code&gt;RCTModuleClasses&lt;/code&gt;)들을 참조한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록된 모듈들은 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/CxxBridge/RCTCxxBridge.mm#L746-L801&quot;&gt;_registerModulesForClasses&lt;/a&gt; 메소드 내에서 초기화가 이루어진 후 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/CxxBridge/RCTCxxBridge.mm#L869&quot;&gt;_moduleDataByName&lt;/a&gt; 딕셔너리에 저장되는데, 이때 key는 모듈의 이름이고 value는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/Base/RCTModuleData.h&quot;&gt;RCTModuleData&lt;/a&gt; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;RCTModuleData&lt;/code&gt;에는 모듈의 이름, 모듈 내에 포함된 네이티브 메소드 ID 등이 포함되어 있고, 네이티브 모듈을 사용하기 위한 데이터들로 구성되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 등록된 모듈은 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/CxxBridge/RCTCxxBridge.mm#L584-L639&quot;&gt;moduleForName&lt;/a&gt;, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/CxxBridge/RCTCxxBridge.mm#L655-L658&quot;&gt;moduleForClass&lt;/a&gt; 메소드를 통해 참조하게 되는데, 안드로이드에서 확인했던 것과 동일하게 React Native 모달 구현체인 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/React/Views/RCTModalHostViewManager.m#L95&quot;&gt;RCTModalHostViewManager&lt;/a&gt;를 살펴보면 아래와 같이 모듈을 참조하고 있음을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2342&quot; data-origin-height=&quot;1204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cv9YOV/btsueLT7mFH/xSzd7RW4nA4CLZX6rO45Ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cv9YOV/btsueLT7mFH/xSzd7RW4nA4CLZX6rO45Ok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cv9YOV/btsueLT7mFH/xSzd7RW4nA4CLZX6rO45Ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcv9YOV%2FbtsueLT7mFH%2FxSzd7RW4nA4CLZX6rO45Ok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;370&quot; data-origin-width=&quot;2342&quot; data-origin-height=&quot;1204&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Native Module Proxy&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 네이티브 모듈의 등록 과정 그리고 내부적으로 접근하는 과정에 대해 알아보았다. 이어서 자바스크립트 런타임에서 등록된 네이티브 모듈에 접근하는 과정에 대해 살펴보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 글에서 자바스크립트 런타임에 대해 설명하면서 &lt;code&gt;JSIExecutor&lt;/code&gt; 를 소개했다. 런타임 초기화 시 몇 가지 기능들을 자바스크립트 전역 컨텍스트에 노출시킨다고 이야기했는데, 그중 네이티브 모듈에 접근할 수 있도록 하는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp#L87&quot;&gt;nativeModuleProxy&lt;/a&gt; 를 노출시킨다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1714&quot; data-origin-height=&quot;2072&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rH1Hg/btsut12xJPO/mn7QGtxoy7tkGMIatw0nKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rH1Hg/btsut12xJPO/mn7QGtxoy7tkGMIatw0nKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rH1Hg/btsut12xJPO/mn7QGtxoy7tkGMIatw0nKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrH1Hg%2Fbtsut12xJPO%2Fmn7QGtxoy7tkGMIatw0nKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;870&quot; data-origin-width=&quot;1714&quot; data-origin-height=&quot;2072&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;nativeModules_&lt;/code&gt; 는 앞에서 살펴본 모듈 저장소(Module Registry)를 기반으로 생성된 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSINativeModules.h&quot;&gt;JSINativeModules&lt;/a&gt; 인스턴스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 인스턴스를 통해 &lt;code&gt;NativeModuleProxy&lt;/code&gt; 인스턴스를 생성하고 자바스크립트 객체로 변환하여 자바스크립트 전역 속성 &lt;code&gt;nativeModuleProxy&lt;/code&gt; 에 노출시키는 코드라고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;NativeModuleProxy&lt;/code&gt; 구현체를 살펴보면 getter 가 구현되어 있는데, &lt;code&gt;JSINativeModules&lt;/code&gt; 의 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSINativeModules.cpp#L28-L61&quot;&gt;getModule&lt;/a&gt;을 통해 네이티브 모듈을 가져온다. 구현되어 있는 코드를 한 번 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;1914&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/20phD/btsut4kFtfm/2HWzLvolzPeZwaC2Tl3RG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/20phD/btsut4kFtfm/2HWzLvolzPeZwaC2Tl3RG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/20phD/btsut4kFtfm/2HWzLvolzPeZwaC2Tl3RG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F20phD%2Fbtsut4kFtfm%2F2HWzLvolzPeZwaC2Tl3RG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;980&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;1914&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSINativeModules.h#L32&quot;&gt;m_object&lt;/a&gt;에서 모듈 이름에 해당하는 모듈이 존재하면 해당 모듈을 반환하고 존재하지 않다면 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSINativeModules.h#L34-L36&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;createModule&lt;/a&gt; 을 호출하여 모듈을 생성하여 새로 할당한다. &lt;code&gt;createModule&lt;/code&gt; 구현 코드를 한 번 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;1968&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cw9Ep4/btsubJJmV6A/x8GEyZk35MWxX5EcWjcaxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cw9Ep4/btsubJJmV6A/x8GEyZk35MWxX5EcWjcaxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cw9Ep4/btsubJJmV6A/x8GEyZk35MWxX5EcWjcaxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcw9Ep4%2FbtsubJJmV6A%2Fx8GEyZk35MWxX5EcWjcaxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;1008&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;1968&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 자바스크립트 런타임의 전역 객체에서 &lt;code&gt;__fbGenNativeModule&lt;/code&gt; 프로퍼티의 값을 함수로 가져와 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/ReactCommon/jsiexecutor/jsireact/JSINativeModules.h#L30&quot;&gt;m_genNativeModuleJS&lt;/a&gt; 멤버변수에 저장한다. 이렇게 저장된 함수는 모듈을 생성할 때 호출되며, 함수의 인자로는 모듈 생성을 위한 구성 정보와 모듈 ID에 해당하는 숫자 값이 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브에서 접근하는 &lt;code&gt;__fbGenNativeModule&lt;/code&gt; 의 정체는 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 함수의 구현체는 자바스크립트 코드에서 확인 가능하며, &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/BatchedBridge/NativeModules.js#L28-L83&quot;&gt;NativeModules&lt;/a&gt; 모듈에서 확인 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;2302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dwsGGX/btsuQk2geoD/C4XowE5yKURSr9YIaX0HoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dwsGGX/btsuQk2geoD/C4XowE5yKURSr9YIaX0HoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dwsGGX/btsuQk2geoD/C4XowE5yKURSr9YIaX0HoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdwsGGX%2FbtsuQk2geoD%2FC4XowE5yKURSr9YIaX0HoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;1134&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;2302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 아래를 보면 &lt;code&gt;genModule&lt;/code&gt; 을 전역 속성의 값으로 할당하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;genModule&lt;/code&gt; 은 네이티브로부터 전달받은 구성(&lt;code&gt;config&lt;/code&gt;)과 모듈 ID(&lt;code&gt;moduleID&lt;/code&gt;)를 가지고 자바스크립트 객체 형태의 모듈을 생성하는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 생성된 모듈은 아래와 같은 형태가 될 것이다.&lt;/p&gt;
&lt;pre class=&quot;clojure&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;ZendeskMessaging&quot;,
  &quot;reset&quot;: Function(...)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 살펴본 모듈 참조 과정을 한 번 정리해 보도록 하자. 순서대로 나열하면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;nativeModuleProxy -&amp;gt; NativeModuleProxy.get -&amp;gt; JSINativeModules.getModule -&amp;gt; __fbGenNativeModule (최초 1회)&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론은 &lt;code&gt;nativeModuleProxy&lt;/code&gt;를 통해 네이티브 모듈에 접근하게 되며, 최초로 네이티브 모듈에 접근할 경우에는 자바스크립트 런타임의 전역 객체에서 &lt;code&gt;__fbGenNativeModule&lt;/code&gt; 를 참조하여 초기화를 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 여러 과정을 거쳐 자바스크립트 전역 컨텍스트에 노출된 &lt;code&gt;nativeModuleProxy&lt;/code&gt; 는 &lt;code&gt;NativeModule&lt;/code&gt; 모듈에서 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/BatchedBridge/NativeModules.js#L179&quot;&gt;참조&lt;/a&gt;되며, &lt;code&gt;NativeModules&lt;/code&gt; 라는 이름으로 export 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2684&quot; data-origin-height=&quot;1340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dlsI5Z/btsufjiN0Jw/KkNuap4nuaMnUk5TzxbkMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dlsI5Z/btsufjiN0Jw/KkNuap4nuaMnUk5TzxbkMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dlsI5Z/btsufjiN0Jw/KkNuap4nuaMnUk5TzxbkMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdlsI5Z%2FbtsufjiN0Jw%2FKkNuap4nuaMnUk5TzxbkMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;359&quot; data-origin-width=&quot;2684&quot; data-origin-height=&quot;1340&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;export 된 &lt;code&gt;NativeModules&lt;/code&gt; 는 네이티브 모듈을 구현할 때 아래와 같이 참조한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2356&quot; data-origin-height=&quot;2068&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyGVnb/btsuh6JZHx9/QRz9voFHSsvdOuEVIjLnkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyGVnb/btsuh6JZHx9/QRz9voFHSsvdOuEVIjLnkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyGVnb/btsuh6JZHx9/QRz9voFHSsvdOuEVIjLnkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyGVnb%2Fbtsuh6JZHx9%2FQRz9voFHSsvdOuEVIjLnkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;632&quot; data-origin-width=&quot;2356&quot; data-origin-height=&quot;2068&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;NativeModules[moduleName]&lt;/code&gt; 형태로 접근하게 되는데, 내부에 구현된 getter를 통해 등록된 네이티브 모듈에 접근이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈이 초기화될 때 네이티브 메소드도 함께 초기화되기 때문에 지금부터는 메소드에 접근하여 호출할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;genModule&lt;/code&gt; 함수를 살펴보자. 모듈 객체를 생성하는 부분을 자세히 살펴보면 메소드를 생성하는 &lt;a href=&quot;https://github.com/facebook/react-native/blob/v0.72.0/packages/react-native/Libraries/BatchedBridge/NativeModules.js#L98-L162&quot;&gt;genMethod&lt;/a&gt;를 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;1370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biB4Mw/btsudix8wly/3hDizqtkg8swlltIoRWdv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biB4Mw/btsudix8wly/3hDizqtkg8swlltIoRWdv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biB4Mw/btsudix8wly/3hDizqtkg8swlltIoRWdv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiB4Mw%2Fbtsudix8wly%2F3hDizqtkg8swlltIoRWdv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;704&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;1370&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메소드가 속한 모듈의 ID(&lt;code&gt;moduleID&lt;/code&gt;), 메소드 식별을 위한 ID(&lt;code&gt;methodId&lt;/code&gt;) 그리고 메소드의 동작 방식에 따른 유형(&lt;code&gt;methodType&lt;/code&gt;)이 전달된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;2252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EUJSe/btsuk1Po69W/KwFQMelVpWGsk1CfFbKHpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EUJSe/btsuk1Po69W/KwFQMelVpWGsk1CfFbKHpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EUJSe/btsuk1Po69W/KwFQMelVpWGsk1CfFbKHpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEUJSe%2Fbtsuk1Po69W%2FKwFQMelVpWGsk1CfFbKHpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;1293&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;2252&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 메소드 동작 유형(&lt;code&gt;async&lt;/code&gt;, &lt;code&gt;sync&lt;/code&gt;, &lt;code&gt;promise&lt;/code&gt;)에 따라 함수로 래핑(wrapping)하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기로 동작하는 메소드(&lt;code&gt;sync&lt;/code&gt;)는 최종적으로 &lt;code&gt;BatchedBridge.callNativeSyncHook&lt;/code&gt; 을 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기로 동작하는 메소드(&lt;code&gt;async&lt;/code&gt;, &lt;code&gt;promise&lt;/code&gt;)는 콜백(Callback) 혹은 프로미스(Promise)로 래핑 되어있고, 최종적으로는 &lt;code&gt;BatchedBridge.enqueueNativeCall&lt;/code&gt; 을 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈에 구현되어 있는 메소드 호출 처리는 &lt;code&gt;BatchedBridge&lt;/code&gt; 에서 담당하게 된다. &lt;code&gt;BatchedBridge&lt;/code&gt; 는 브릿지 동작에 있어 핵심적인 역할을 수행하는 모듈인데, 이에 대한 자세한 내용은 다음 글에서 이어서 정리하도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리하며&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1806&quot; data-origin-height=&quot;990&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m8bR4/btswcLK3sQn/AfVzF2Dknblg0UkZHQKI0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m8bR4/btswcLK3sQn/AfVzF2Dknblg0UkZHQKI0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m8bR4/btswcLK3sQn/AfVzF2Dknblg0UkZHQKI0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm8bR4%2FbtswcLK3sQn%2FAfVzF2Dknblg0UkZHQKI0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;395&quot; data-origin-width=&quot;1806&quot; data-origin-height=&quot;990&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 살펴본 내용을 정리해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 플랫폼에 맞게 구현된 네이티브 모듈은 앱 초기화 시점에 저장소에 등록된다. 등록된 네이티브 모듈은 프록시 객체를 통해 자바스크립트 런타임에 노출되고, 프록시 객체로부터 네이티브 모듈의 메소드가 호출되면 모듈 ID, 메소드 ID가 브릿지를 통해 네이티브로 전달된다. 최종적으로 저장소에서 ID에 해당하는 모듈을 탐색하고 해당하는 메소드를 호출하여 동작을 수행하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 모듈의 구현체는 상당히 복잡하지만 전반적인 구조를 정리하고 코드를 잘 따라가 보면 어느 매커니즘으로 동작하는지 이해할 수 있을 것이다. 이렇게 감춰져 있는 부분들 덕분에 우리는 쉽게 네이티브 모듈을 구현하고 사용할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 자바스크립트 런타임과 네이티브 환경이 상호작용 할 수 있도록 하는 브릿지(Bridge)에 대해 집중적으로 살펴보도록 하겠다.&lt;/p&gt;</description>
      <category>언어 &amp;amp; 프레임워크/React Native</category>
      <author>이근둥</author>
      <guid isPermaLink="true">https://codevkr.tistory.com/129</guid>
      <comments>https://codevkr.tistory.com/129#entry129comment</comments>
      <pubDate>Mon, 18 Sep 2023 00:13:52 +0900</pubDate>
    </item>
  </channel>
</rss>