React Native
네이티브 모듈을 통해 React Native 앱에 Human Token Journey를 통합하세요.
통합은 iOS에서는 ASWebAuthenticationSession, Android에서는 Custom Tabs에 위임하는 네이티브 모듈을 사용합니다. 여정이 완료되면 네이티브 모듈이 콜백 URL로 Promise를 해결합니다.
사전 요구 사항
- React Native 0.71 이상
- iOS용 Xcode 14+
- Android용 Android Studio
- 앱의 고유한 URL 스킴 (예:
myapp)
단계 1: URL 스킴 정의
고유한 스킴을 선택하세요 (예: myapp). 프로세스 생성 시 callbackUri로 사용됩니다:
callbackUri: myapp://callback
단계 2: TypeScript 브릿지 생성
// src/modules/UnicoModule.ts
import { NativeModules } from 'react-native';
const { UnicoModule } = NativeModules;
if (!UnicoModule) {
throw new Error('UnicoModule not found. Make sure the native module is linked correctly.');
}
export const Unico = {
openSession: (url: string): Promise<string> => UnicoModule.openSession(url),
};
단계 3: iOS 네이티브 모듈 구현
- Objective-C bridge
- Swift implementation
// ios/UnicoModule.m
#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(UnicoModule, NSObject)
RCT_EXTERN_METHOD(openSession:(NSString *)url
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
@end
// ios/UnicoModule.swift
import AuthenticationServices
@objc(UnicoModule)
class UnicoModule: NSObject, ASWebAuthenticationPresentationContextProviding {
private var session: ASWebAuthenticationSession?
@objc func openSession(_ url: String,
resolve: @escaping RCTPromiseResolveBlock,
reject: @escaping RCTPromiseRejectBlock) {
guard let sessionURL = URL(string: url) else {
reject("INVALID_URL", "Invalid URL", nil)
return
}
DispatchQueue.main.async {
let newSession = ASWebAuthenticationSession(
url: sessionURL,
callbackURLScheme: "myapp"
) { callbackURL, error in
self.session = nil
if let callbackURL = callbackURL {
resolve(callbackURL.absoluteString)
} else {
reject("SESSION_ERROR", error?.localizedDescription ?? "Session cancelled", error)
}
}
newSession.presentationContextProvider = self
newSession.prefersEphemeralWebBrowserSession = true
newSession.start()
self.session = newSession
}
}
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first else {
return ASPresentationAnchor()
}
return window
}
@objc static func requiresMainQueueSetup() -> Bool { return true }
}
단계 4: Android 네이티브 모듈 구현
android/app/build.gradle에 의존성 추가:
implementation("androidx.browser:browser:1.5.0")
// android/app/src/main/java/<your.package>/UnicoModule.kt
package com.yourapp
import android.content.Intent
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import com.facebook.react.bridge.*
class UnicoModule(private val reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {
private var pendingPromise: Promise? = null
override fun getName() = "UnicoModule"
@ReactMethod
fun openSession(url: String, promise: Promise) {
pendingPromise = promise
currentActivity?.runOnUiThread {
CustomTabsIntent.Builder().build()
.launchUrl(currentActivity!!, Uri.parse(url))
} ?: promise.reject("NO_ACTIVITY", "No current activity")
}
fun handleRedirectUrl(uri: Uri) {
if (uri.scheme == "myapp") {
pendingPromise?.resolve(uri.toString())
pendingPromise = null
}
}
}
// UnicoPackage.kt
package com.yourapp
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
class UnicoPackage : ReactPackage {
override fun createNativeModules(ctx: ReactApplicationContext): List<NativeModule> =
listOf(UnicoModule(ctx))
override fun createViewManagers(ctx: ReactApplicationContext): List<ViewManager<*, *>> =
emptyList()
}
MainApplication.kt에 등록:
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
add(UnicoPackage())
}
MainActivity.kt에서 리디렉션 처리:
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
intent.data?.let { uri ->
if (uri.scheme == "myapp") {
val module = reactInstanceManager
.currentReactContext
?.getNativeModule(UnicoModule::class.java)
module?.handleRedirectUrl(uri)
}
}
}
단계 5: AndroidManifest.xml 구성
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.CAMERA" />
<activity android:name=".MainActivity" ...>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="callback" />
</intent-filter>
</activity>
단계 6: 여정 시작
import { Unico } from './modules/UnicoModule';
const resultUrl = await Unico.openSession(processLink);
// resultUrl = myapp://callback?id=...
파일 요약
| 파일 | 목적 |
|---|---|
src/modules/UnicoModule.ts | TypeScript 브릿지 |
ios/UnicoModule.m | Objective-C 선언 |
ios/UnicoModule.swift | iOS — ASWebAuthenticationSession |
android/.../UnicoModule.kt | Android — Custom Tabs |
android/.../UnicoPackage.kt | 패키지 등록 |
android/.../MainApplication.kt | 패키지 통합 |
android/.../MainActivity.kt | 리디렉션 URL 처리 |
AndroidManifest.xml | Intent 필터 |