릴리스 빌드와 심볼리케이션

React Native 릴리스 빌드에서 읽을 수 있는 것(Hermes 번들의 JavaScript 스택 트레이스, Android의 R8 매핑, iOS의 dSYM)과 지금 Sophonz가 복원할 수 있는 범위, 피해야 할 Gradle 플러그인 설정을 설명합니다.

React Native 릴리스 빌드에는 복원이 필요한 프레임이 세 종류 있습니다. 압축된 번들 안의 JavaScript 프레임, R8로 줄어든 Java와 Kotlin 프레임, iOS와 C++ 라이브러리의 네이티브 프레임입니다. Sophonz는 업로드한 파일로 R8 프레임과 iOS 네이티브 프레임을 복원합니다. JavaScript 소스 맵은 아직 업로드 경로가 없으므로, 이 문서에서는 JavaScript 프레임을 직접 복원할 수 있도록 무엇을 보관해야 하는지 설명합니다.

한눈에 보기

프레임Sophonz가 복원업로드할 것방법
JavaScript(Hermes 또는 JSC 번들)아니요—소스 맵을 보관하고 로컬에서 복원
Android Java/Kotlin(R8)예mapping.txt@sophonz/cli upload-mapping
iOS 네이티브예dSYM 번들@sophonz/cli upload-dsym 또는 빌드 단계

JavaScript 스택 트레이스

처리되지 않은 예외, 처리한 에러, 경고와 에러 로그에는 JavaScript 스택이 문자열로 붙습니다. 로그에는 spz.stacktrace.rn, iOS의 처리되지 않은 예외에는 spz.ios.react_native_crash.js_exception입니다. 릴리스 빌드에서 이 값은 소스 파일이 아니라 번들 안의 위치입니다. Hermes라면 index.android.bundle:1:284511 같은 바이트코드 오프셋입니다.

Sophonz는 React Native 소스 맵을 저장하지 않습니다. @sophonz/cli upload-sourcemaps는 브라우저 SDK용이며, 맵을 웹 버전과 스크립트 URL 기준으로 저장하는데 React Native 번들에는 둘 다 없습니다. React Native용 업로드 경로가 생기기 전까지는 배포하는 모든 릴리스의 소스 맵을 보관하고, Metro의 symbolicator로 스택 트레이스를 복원하세요.

소스 맵 보관

Android: React Native Gradle 플러그인이 릴리스 빌드 중에 Hermes 합성 소스 맵을 만듭니다.

android/app/build/generated/sourcemaps/react/release/index.android.bundle.map

iOS: Bundle React Native code and images 빌드 단계에서 스크립트가 실행되기 전에 SOURCEMAP_FILE을 지정합니다.

export SOURCEMAP_FILE="$DERIVED_FILE_DIR/main.jsbundle.map"

맵은 앱 버전, 빌드 번호, OTA 업데이트를 한다면 JavaScript 패치까지 이름에 넣어 CI 아티팩트로 빌드와 함께 보관하세요.

트레이스 복원

로그 속성의 스택을 파일로 복사한 뒤 실행합니다.

npx metro-symbolicate index.android.bundle.map < stacktrace.txt

맵은 그 트레이스를 만든 빌드의 것이어야 합니다. iOS 릴리스 빌드에서는 react_native_bundle_id(main.jsbundle의 MD5)로 어느 번들인지 알 수 있습니다. 두 플랫폼 모두 OTA 업데이트를 구분할 수 있도록 initialize에 patch를 지정하세요.

Android: R8 매핑

React Native의 release 빌드 타입은 기본으로 minifyEnabled가 꺼져 있습니다(android/app/build.gradle의 enableProguardInReleaseBuilds = false). 켰다면 빌드마다 mapping.txt를 업로드하세요.

export SOPHONZ_TOKEN=sophonz_pat_...
 
npx @sophonz/cli upload-mapping \
  --file android/app/build/outputs/mapping/release/mapping.txt \
  --version 1.4.0

--version은 빌드의 versionName입니다. 옵션, 업로드 API, 매칭 방식은 Android 매핑 파일 업로드에 있습니다.

WARNING — React Native 앱에서 api_token 설정

sophonz-config.json에 api_token과 app_id를 넣으면 Gradle 플러그인이 매핑을 직접 업로드합니다. React Native 앱에서는 같은 설정이 모든 release variant에 React Native 소스 맵 업로드도 등록하는데, 이 업로드는 Sophonz 서버에 없는 엔드포인트인 /v2/store/sourcemap으로 요청합니다. 플러그인 기본값인 failBuildOnUploadErrors = true에서는 이 업로드 실패로 릴리스 빌드가 실패합니다.

CLI를 쓰고 sophonz-config.json에는 api_token을 넣지 마세요. 플러그인 업로드를 쓴다면 실패한 요청이 빌드를 멈추지 않도록 android/app/build.gradle에 다음을 넣으세요.

sophonz {
  failBuildOnUploadErrors.set(false)
}

이 동작은 1.0.0 Gradle 플러그인 소스를 읽고 정리한 것이며, React Native 릴리스 빌드로 직접 확인하지는 않았습니다.

iOS: dSYM

iOS 크래시 보고서의 네이티브 프레임(Swift와 Objective-C 코드, React Native, Hermes, 그 밖의 pod)은 dSYM 번들로 복원합니다. Release 구성의 Debug Information Format이 DWARF with dSYM File(기본값)이면 아카이브한 뒤 업로드합니다.

npx @sophonz/cli upload-dsym \
  --file "$DWARF_DSYM_FOLDER_PATH" \
  --version 1.4.0 \
  --build-id 120

SophonzIO를 포함해 소스에서 빌드한 pod는 각자 dSYM을 만들며, 폴더를 넘기면 모두 업로드됩니다. Xcode 빌드 단계, GitHub Actions, curl 방식은 iOS dSYM 업로드에 있습니다.

릴리스 체크리스트

  • Android: minSdk와 desugaring이 Gradle 플러그인 확인을 통과하고, 릴리스용 sophonz-config.json에 프로덕션 service_key가 들어 있습니다.
  • iOS: 앱이 쓰는 방식에 따라 SophonzInitializer.swift, sdkConfig.ios, Sophonz-Info.plist 중 하나에 프로덕션 키가 들어 있습니다.
  • OTA 업데이트를 한다면 initialize에 patch를 넘깁니다.
  • 이 빌드의 소스 맵, mapping.txt, dSYM을 보관하거나 업로드했습니다.
  • 내비게이션 트래커는 debug={false}이고, configureSDKErrorLogging은 꺼져 있거나 자체 핸들러로 연결되어 있습니다.