설치

React Native 패키지를 추가하고, 비공개 네이티브 SDK 접근 권한을 준비하고, Expo 설정 플러그인이나 설치 마법사 또는 직접 설정으로 네이티브 SDK를 붙인 뒤 SDK를 시작하는 과정을 설명합니다.

설치는 JavaScript 부분과 네이티브 부분으로 나뉩니다. JavaScript 쪽은 패키지 하나와 initialize 호출 한 번이면 끝납니다. 네이티브 쪽, 즉 Android의 Gradle 플러그인과 설정 파일, iOS의 pod와 initializer는 Expo 설정 플러그인이나 설치 마법사에 맡길 수도 있고 직접 설정할 수도 있습니다.

빠른 시작

아래에서 수집기 URL, 프로젝트, 앱, 앱 키를 입력하고 탭(JS init · Android config · iOS plist)을 선택하면 그 값으로 설정 코드가 생성됩니다. JS init 탭은 JavaScript initialize 호출과 함께, JS가 다루지 못하는 Android 설정 파일도 함께 보여줍니다. 이 섹션은 값을 채워 설정 코드를 만드는 용도이며, 설치 방법 자체는 아래 설치 방법 고르기에서 고릅니다. 고급 옵션은 "고급 옵션" 토글 뒤에 있습니다.

// App.tsx — 렌더링 전에, 예를 들어 index.js 최상단에서 호출하세요
import { initialize } from '@sophonz/react-native';

await initialize({
  sdkConfig: {
    ios: {
      collectorUrl: 'https://in.sophonz.ai',
      appKey: 'sk_tYtJUa4WweXXedj6lxjSqoPW5O0DnbXz',
      appName: 'next-sample',
      project: 'sophonz',
    },
  },
});

// android/app/src/main/sophonz-config.json — Android는 JS에서 아무것도 읽지 않습니다.
// 이 파일이 Android 아이덴티티를 설정하는 유일한 방법입니다.
{
  "sdk_config": {
    "app_framework": "react_native",
    "ingest": {
      "collector_url": "https://in.sophonz.ai",
      "service_key": "sk_tYtJUa4WweXXedj6lxjSqoPW5O0DnbXz",
      "service_namespace": "sophonz"
    }
  }
}

요구 사항

항목요구 사항
React Nativepeer dependency로는 react-native 0.56 이상을 받지만, 빌드와 테스트는 0.85와 0.86 기준입니다
Expo (선택)expo 47 이상을 받으며, 설정 플러그인은 Expo SDK 57 기준으로 테스트했습니다. Expo Go는 지원하지 않습니다
아키텍처새 아키텍처와 기존 아키텍처 모두. 설정할 것은 없고, 앱이 쓰는 쪽으로 모듈이 컴파일됩니다
Android minSdk21 이상. 26 미만이면 core library desugaring이 필요합니다. 아래를 참고하세요
Android 빌드compileSdk 34 이상, AGP 8.0.2 이상(minSdk가 26 미만이면 8.3.0 이상), Kotlin 2.0.21
iOS13.0 이상, Swift 5.9, Xcode 26 이상(Apple SDK를 소스에서 빌드합니다)
iOS에서 SPM 사용 시 (선택)React Native 0.75 이상, 동적 프레임워크
Node패키지에는 engine 제한이 없습니다. 설치 마법사는 Node 18 이상이 필요합니다

Android와 iOS 항목은 네이티브 SDK 자체의 요구 사항입니다. 자세한 배경은 Android 설치와 iOS 설치를 참고하세요.

Android minSdk와 desugaring

Android SDK 자체는 minSdk 21을 지원합니다. React Native 모듈은 루트 android/build.gradle의 ext에 지정한 minSdkVersion으로 컴파일되고, 값이 없으면 24를 씁니다. React Native 기본 템플릿은 24로 설정합니다.

앱의 minSdk가 26 미만이면 Sophonz Gradle 플러그인이 설정 단계에서 세 가지를 확인하고, 하나라도 빠지면 빌드를 실패시킵니다.

  • 앱 모듈에서 core library desugaring이 켜져 있을 것
  • AGP가 8.3.0 이상일 것
  • android/gradle.properties에 android.useFullClasspathForDexingTransform=true가 있을 것

그래서 기본 React Native 앱(minSdk 24)에는 android/app/build.gradle에 다음이 필요합니다.

android/app/build.gradle
android {
  compileOptions {
    coreLibraryDesugaringEnabled true
  }
}
 
dependencies {
  coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.3"
}
android/gradle.properties
android.useFullClasspathForDexingTransform=true

아니면 minSdk를 26으로 올리면 세 가지 확인이 모두 사라집니다. Expo 설정 플러그인과 설치 마법사는 이 부분을 대신 바꿔 주지 않습니다.

설치 방법 고르기

방법네이티브 설정 담당SDK 시작 위치
Exponpx expo prebuild 중 설정 플러그인네이티브(MainApplication, AppDelegate)
일반 React Native, 설치 마법사한 번 실행하는 스크립트 두 개네이티브(MainApplication, AppDelegate)
일반 React Native, 직접 설정직접네이티브 또는 JavaScript 중 선택

네이티브에서 시작하면 JavaScript가 로드되기 전의 크래시와 앱 시작 과정까지 수집됩니다. JavaScript에서 시작하면 initialize로 iOS 옵션과 OTLP 익스포터를 넘길 수 있습니다. 한 번의 실행에서 둘을 함께 쓸 수는 없습니다. 네이티브 시작과 JavaScript 시작을 참고하세요.

1. 패키지 추가

패키지는 npm의 @sophonz 스코프로 배포됩니다. 현재 릴리스는 0.1.0이며 다섯 패키지 모두 같은 버전을 씁니다.

npm install @sophonz/react-native@0.1.0
# 또는
yarn add @sophonz/react-native@0.1.0
# 또는 Expo 앱에서
npx expo install @sophonz/react-native@0.1.0

선택 패키지도 같은 버전으로 같은 방식으로 추가합니다.

npm install @sophonz/react-native-tracer-provider@0.1.0 \
  @sophonz/react-native-navigation@0.1.0 \
  @sophonz/react-native-redux@0.1.0 \
  @sophonz/react-native-otlp@0.1.0
패키지용도네이티브 코드
@sophonz/react-native코어 바인딩, 에러 핸들러, 로그, 세션, Expo 설정 플러그인있음
@sophonz/react-native-tracer-provider네이티브 SDK 기반 OpenTelemetry TracerProvider있음
@sophonz/react-native-navigation화면 스팬없음
@sophonz/react-native-redux디스패치된 Redux 액션 스팬없음
@sophonz/react-native-otlp컬렉터와 함께 쓰는 추가 OTLP/HTTP 익스포터있음

네이티브 코드가 있는 패키지를 추가한 뒤에는 앱을 다시 빌드하고, iOS에서는 pod install도 실행해야 합니다.

2. 네이티브 SDK 접근 권한

0.1.0은 Android SDK 1.0.0과 Apple SDK 1.0.0에 연결됩니다. 둘 다 공개 레지스트리에 없으므로 앱을 빌드하는 모든 머신(개발 PC, CI, EAS)에 두 SDK의 인증 정보가 있어야 합니다.

Android: GitHub Packages

Android SDK, 내부 API 아티팩트, Gradle 플러그인은 https://maven.pkg.github.com/sophonz-labs/sophonz-android-sdk에서 받으며, 읽기만 하는 데도 토큰이 필요합니다. sophonz-labs를 볼 수 있는 계정에서 read:packages 권한이 있는 classic 토큰을 만드세요.

~/.gradle/gradle.properties
gpr.user=<GitHub 사용자 이름>
gpr.key=<read:packages 권한이 있는 토큰>

이 속성이 없으면 빌드는 대신 GITHUB_ACTOR와 GITHUB_TOKEN 환경 변수를 읽습니다. CI에서는 이쪽을 쓰면 됩니다.

CAUTION — 404는 대개 인증 문제입니다

GitHub Packages는 볼 권한이 없는 패키지에 403이 아닌 404를 돌려줍니다. 그래서 토큰이 없을 때 Gradle은 "could not resolve io.sophonz:sophonz-android-sdk"라고만 보고합니다. 버전을 의심하기 전에 토큰부터 확인하세요.

iOS: Apple SDK git 저장소

SophonzIO는 CocoaPods나 SPM이 https://github.com/sophonz-labs/sophonz-apple-sdk.git에서 git으로 받아옵니다. pod install을 실행하는 머신에는 git credential helper나 ~/.netrc를 통해 이 저장소에 접근할 권한이 있어야 합니다.

~/.netrc
machine github.com
login <GitHub 사용자 이름>
password <저장소 읽기 권한이 있는 토큰>

Expo

플러그인 설정

app.json(또는 app.config.js)에 설정 플러그인을 추가합니다.

app.json
{
  "expo": {
    "plugins": [
      [
        "@sophonz/react-native",
        {
          "androidAppKey": "sk_android_replace_me",
          "iOSAppKey": "sk_ios_replace_me",
          "iOSAppName": "my-ios-app",
          "project": "my-project",
          "deploymentEnvironment": "production"
        }
      ]
    ]
  }
}

androidAppKey와 iOSAppKey는 필수입니다. 포털에서 두 플랫폼은 각각 별도의 앱이고 키도 따로 있습니다. 플러그인은 패키지 이름으로 찾으며, @sophonz/react-native/lib/app.plugin.js 경로도 그대로 동작합니다. 모든 속성은 설정에 있습니다.

앱의 Android minSdk가 26 미만이면(Expo 기본값은 24) 플러그인이 desugaring을 추가하지 않으므로 expo-build-properties로 minSdk를 올리세요.

app.json
{
  "expo": {
    "plugins": [
      ["expo-build-properties", {"android": {"minSdkVersion": 26}}],
      ["@sophonz/react-native", {"androidAppKey": "sk_android_replace_me", "iOSAppKey": "sk_ios_replace_me"}]
    ]
  }
}

Prebuild

npx expo prebuild

prebuild는 다음을 바꿉니다.

플랫폼변경 내용
Androidsdk_config.ingest와 app_framework: "react_native"를 담은 android/app/src/main/sophonz-config.json 생성
Androidandroid/build.gradle에 GitHub Packages 저장소와 io.sophonz:sophonz-gradle-plugin classpath 추가
Androidandroid/app/build.gradle에 io.sophonz.gradle 적용
AndroidMainApplication(Kotlin 또는 Java)의 super.onCreate() 바로 뒤에 Sophonz.start(this) 호출 추가
iOS키로 Apple SDK를 시작하는 SophonzInitializer.swift를 만들고 Xcode 프로젝트에 추가
iOSAppDelegate에서 SophonzInitializer.start() 호출. AppDelegate가 Objective-C면 브리징 헤더도 추가
iOSPodfile에 v1.0.0으로 고정한 SophonzIO pod와 sophonz_post_install 훅 추가

그다음 평소처럼 개발 빌드를 만듭니다(npx expo run:android, npx expo run:ios, 또는 EAS).

CAUTION — 플러그인 실패는 경고로만 나옵니다

플러그인이 수정할 줄을 찾지 못하면(형태가 다른 MainApplication, post_install이 없는 Podfile 등) prebuild는 성공한 채로 @sophonz/react-native의 경고만 출력합니다. 그러면 앱은 SDK 없이 빌드됩니다. 플러그인을 추가한 뒤에는 prebuild 출력을 확인하세요.

NOTE — 생성된 파일은 갱신되지 않습니다

sophonz-config.json과 SophonzInitializer.swift는 파일이 없을 때만 만들어집니다. 속성을 바꾼 뒤에는 npx expo prebuild --clean을 실행하세요.

플러그인이 두 플랫폼 모두에서 SDK를 네이티브로 시작하므로, 플러그인을 쓰는 Expo 앱에서 initialize에 넘긴 sdkConfig는 무시됩니다. 옵션은 플러그인 속성에 넣으세요.

EAS Build

EAS 워커에도 로컬 머신과 같은 접근 권한이 필요합니다.

  1. 빌드하는 모든 environment에 GITHUB_ACTOR와 GITHUB_TOKEN을 secret 공개 범위의 EAS 환경 변수로 만드세요. Gradle은 이 값을 바로 읽습니다.
  2. iOS에서는 의존성 설치 전에 이 값을 ~/.netrc에 써서 pod install이 Apple SDK를 clone할 수 있게 합니다. EAS는 그 시점에 package.json의 eas-build-pre-install 스크립트를 실행합니다.
package.json
{
  "scripts": {
    "eas-build-pre-install": "bash eas-hooks/pre-install.sh"
  }
}
eas-hooks/pre-install.sh
#!/usr/bin/env bash
set -euo pipefail
 
if [ -n "${GITHUB_TOKEN:-}" ]; then
  printf 'machine github.com\nlogin %s\npassword %s\n' "$GITHUB_ACTOR" "$GITHUB_TOKEN" > ~/.netrc
  chmod 600 ~/.netrc
fi

토큰을 app.json에 넣거나 .netrc를 커밋하지 마세요.

일반 React Native: 설치 마법사

package.json이 있는 앱 루트에서 실행합니다.

node node_modules/@sophonz/react-native/lib/scripts/setup/installAndroid.js
node node_modules/@sophonz/react-native/lib/scripts/setup/installIos.js
cd ios && pod install

각 스크립트는 컬렉터 URL(비워 두면 https://in.sophonz.ai)과 해당 플랫폼의 앱 키를 묻습니다. iOS 스크립트는 iOS 프로젝트 폴더 이름이 package.json과 다르면 그 이름도 묻습니다. 그다음 다음을 수행합니다.

스크립트단계변경 내용
installAndroid.jspatchBuildGradleandroid/build.gradle에 Gradle 플러그인 classpath와 GitHub Packages 저장소
patchAppBuildGradleandroid/app/build.gradle에 apply plugin: "io.sophonz.gradle"
createSophonzJSONsdk_config.ingest를 담은 android/app/src/main/sophonz-config.json
patchMainApplicationMainApplication에 Sophonz import와 시작 코드
installIos.jsaddSophonzInitializerSwiftSophonzInitializer.swift를 만들고 Xcode 프로젝트에 추가
iosInitializeSophonzAppDelegate(Swift 또는 Objective-C)에서 SophonzInitializer.start() 호출
patchPodfilegit 태그로 고정한 SophonzIO pod와 sophonz_post_install 훅

두 플랫폼의 변경을 되돌리려면 다음을 실행합니다.

node node_modules/@sophonz/react-native/lib/scripts/setup/uninstall.js

직접 추가한 Sophonz-Info.plist는 남겨 둡니다.

일반 React Native: 직접 설정

Android

프로젝트 빌드 파일에 저장소와 Gradle 플러그인을 추가합니다. 저장소는 플러그인용으로 buildscript에, SDK 아티팩트용으로 allprojects에 모두 넣습니다.

android/build.gradle
buildscript {
  repositories {
    google()
    mavenCentral()
    maven {
      url "https://maven.pkg.github.com/sophonz-labs/sophonz-android-sdk"
      credentials {
        username = findProperty("gpr.user") ?: System.getenv("GITHUB_ACTOR")
        password = findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN")
      }
    }
  }
  dependencies {
    classpath("com.android.tools.build:gradle")
    classpath("io.sophonz:sophonz-gradle-plugin:1.0.0")
  }
}
 
allprojects {
  repositories {
    maven {
      url "https://maven.pkg.github.com/sophonz-labs/sophonz-android-sdk"
      credentials {
        username = findProperty("gpr.user") ?: System.getenv("GITHUB_ACTOR")
        password = findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN")
      }
    }
  }
}

앱 모듈에서 Android application 플러그인 옆에 적용합니다.

android/app/build.gradle
apply plugin: "com.android.application"
apply plugin: "io.sophonz.gradle"

plugins 블록을 쓴다면 id("io.sophonz.gradle")로 적습니다. minSdk가 26 미만이면 desugaring도 추가하세요.

설정 파일을 만듭니다. Gradle 플러그인이 이 파일을 앱에 컴파일해 넣으므로 반드시 이 위치에 있어야 합니다.

android/app/src/main/sophonz-config.json
{
  "sdk_config": {
    "app_framework": "react_native",
    "ingest": {
      "collector_url": "https://in.sophonz.ai",
      "service_key": "sk_android_replace_me",
      "service_namespace": "my-project"
    }
  }
}

원한다면 MainApplication의 super.onCreate() 바로 뒤에서 SDK를 시작합니다.

MainApplication.kt
import io.sophonz.android.sophonzsdk.Sophonz
 
override fun onCreate() {
  super.onCreate()
  Sophonz.start(this)
  // ... 나머지 React Native 설정
}
MainApplication.java
import io.sophonz.android.sophonzsdk.Sophonz;
 
@Override
public void onCreate() {
  super.onCreate();
  Sophonz.INSTANCE.start(this);
  // ...
}

이 줄을 생략하면 JavaScript가 실행될 때 initialize가 SDK를 시작합니다. Android에서는 설정이 항상 sophonz-config.json에서 오므로 설정에는 차이가 없고, 시작 시점만 늦어집니다.

iOS

SophonzIO는 CocoaPods trunk에 없으므로 Podfile에 출처를 적어야 합니다. 앱 타깃 안에 넣으세요.

ios/Podfile
target 'MyApp' do
  pod 'SophonzIO', :git => 'https://github.com/sophonz-labs/sophonz-apple-sdk.git', :tag => 'v1.0.0'
  # ... use_react_native! 등 나머지
end
cd ios && pod install

React Native 모듈 pod(RNSophonzCore, 설치했다면 RNSophonzTracerProvider나 RNSophonzOTLP)는 autolinking이 추가합니다. 설치 마법사가 넣는 sophonz_post_install 훅은 Swift Package Manager를 쓸 때만 필요합니다.

그다음 sdkConfig.ios나 Sophonz-Info.plist로 JavaScript에서 SDK를 시작하거나(설정), JavaScript 로드 전 상황까지 수집하도록 네이티브에서 시작합니다.

ios/MyApp/SophonzInitializer.swift
import Foundation
import SophonzIO
 
@objcMembers class SophonzInitializer: NSObject {
  static func start() {
    do {
      try Sophonz.start(
        options: .withCollector(
          url: "https://in.sophonz.ai",
          appKey: "sk_ios_replace_me",
          appName: "my-ios-app",
          project: "my-project",
          deploymentEnvironment: "production",
          platform: .reactNative
        )
      )
    } catch {
      print("[sophonz] failed to start: \(error)")
    }
  }
}

application(_:didFinishLaunchingWithOptions:)의 맨 처음에서 호출합니다.

ios/MyApp/AppDelegate.swift
func application(
  _ application: UIApplication,
  didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
  SophonzInitializer.start()
  // ... 나머지 React Native 설정
}

Objective-C AppDelegate.mm에서는 생성된 Swift 헤더를 import하고 같은 방식으로 호출합니다. 헤더 이름은 product module 이름을 따르며, Swift가 타깃에 컴파일되려면 프로젝트에 브리징 헤더가 있어야 합니다.

ios/MyApp/AppDelegate.mm
#import "AppDelegate.h"
#import "MyApp-Swift.h"
 
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [SophonzInitializer start];
  // ...
}

NOTE — 메인 스레드와 .reactNative

Sophonz.start는 메인 스레드가 아니면 invalidThread를 던집니다. platform: .reactNative는 앱의 프레임워크 리소스로 기록되며, 빼면 앱이 네이티브 앱으로 보고됩니다.

새 아키텍처와 기존 아키텍처

전환할 것이 없습니다. Android 모듈은 TurboModule과 기존 브리지 모듈을 모두 담고 있고, Gradle이 앱의 newArchEnabled 속성에 따라 하나를 고릅니다. iOS 모듈은 RCT_NEW_ARCH_ENABLED에 맞춰 컴파일됩니다. JavaScript에서는 TurboModuleRegistry를 먼저, NativeModules를 다음으로 찾으므로 같은 코드가 양쪽에서 동작합니다.

모듈이 아예 링크되지 않았다면(설치 후 재빌드 안 함, pod install 안 함, Expo Go) initialize는 경고를 남기고 false로 resolve되며, 나머지 호출은 모두 기본값으로 resolve됩니다.

JavaScript에서 initialize 호출

initialize는 가능한 한 일찍 한 번 호출하세요. SDK를 이미 네이티브에서 시작했더라도 호출해야 합니다. 전역 에러 핸들러를 설치하고, React Native와 SDK 버전을 기록하고, 처리되지 않은 Promise 거부 추적을 켜는 일이 여기서 이루어집니다.

App.tsx
import {useEffect} from "react";
import {initialize} from "@sophonz/react-native";
 
export default function App() {
  useEffect(() => {
    initialize({
      sdkConfig: {
        ios: {
          collectorUrl: "https://in.sophonz.ai",
          appKey: "sk_ios_replace_me",
        },
        trackUnhandledRejections: true,
      },
    });
  }, []);
 
  // ...
}

완료 시점까지 알려 주는 훅을 써도 됩니다.

import {useSophonz} from "@sophonz/react-native";
 
const SOPHONZ_CONFIG = {
  ios: {collectorUrl: "https://in.sophonz.ai", appKey: "sk_ios_replace_me"},
  trackUnhandledRejections: true,
};
 
export default function App() {
  const {isPending, isStarted} = useSophonz(SOPHONZ_CONFIG);
  // ...
}

설정 객체는 컴포넌트 밖에 정의하세요. 객체의 참조가 바뀌면 훅이 다시 실행됩니다.

네이티브 시작과 JavaScript 시작

initialize는 먼저 네이티브 SDK가 실행 중인지 묻습니다.

네이티브에서 시작initialize가 시작
JS 로드 전의 크래시와 앱 시작 수집예아니요
sdkConfig.ios 적용아니요예
sdkConfig.exporters 적용아니요예
trackUnhandledRejections, patch 적용예예
전역 JS 에러 핸들러 설치예예
Android 설정sophonz-config.jsonsophonz-config.json

CAUTION — 네이티브 시작이 조용히 우선합니다

MainApplication이나 SophonzInitializer에서 SDK를 이미 시작했다면 initialize는 실행 중인 SDK를 보고 자체 시작을 건너뜁니다. 그래서 넘긴 iOS 옵션과 exporters는 경고 없이 버려집니다. 네이티브에서 시작한 SDK는 네이티브에서 설정하세요. iOS는 SophonzInitializer.swift나 Sophonz-Info.plist, 익스포터는 OpenTelemetry와 OTLP에 설명한 네이티브 방식입니다.

확인

  • initialize가 true로 resolve되고 콘솔에 [Sophonz] native SDK was started가 보입니다(JavaScript가 시작한 경우에만).
  • 버튼에서 에러를 던지거나 logError("install check")를 호출한 뒤 앱을 백그라운드로 보냅니다. iOS는 타이머가 아니라 세션 파트가 끝날 때 업로드하므로, 포그라운드에 있는 시뮬레이터는 아무것도 보내지 않습니다.
  • 세션이 보이지 않으면 앱 키부터 확인하세요. 컬렉터는 테넌트를 찾을 수 없는 키의 텔레메트리를 오류 응답 없이 버리므로, 키가 틀린 설치도 정상 설치와 똑같아 보입니다.
  • Android에서는 설치한 빌드의 android/app/src/main/sophonz-config.json에 키가 들어 있는지 확인하세요. 값은 빌드에 컴파일됩니다.

다음 단계

  • 설정 — 두 플랫폼의 모든 옵션
  • 계측 — 설치만으로 수집되는 것