매핑 파일 업로드

R8/ProGuard mapping.txt를 올려 난독화된 스택 트레이스를 원래 클래스, 메서드, 줄 번호로 복원합니다.

R8로 축소한 릴리스 빌드의 스택 트레이스는 a.b.c(Unknown Source:12)처럼 남습니다. 그 빌드의 mapping.txt를 올려 두면 Sophonz가 R8 retrace로 원래 이름과 줄 번호를 되돌려 보여 줍니다. 매핑 파일은 앱의 앱 버전 아래에 보관되고, 빌드 ID로 정확히 매칭됩니다.

복원되는 대상

데이터속성내용
크래시, 처리된 예외exception.stacktrace예외가 난 스레드의 프레임과 Caused by 체인
크래시 시점의 다른 스레드spz.android.threads스레드 이름, 상태, 프레임

R8이 인라인한 메서드는 원래 호출 순서대로 여러 프레임으로 펼쳐집니다. java., android., androidx., kotlin. 등 플랫폼 패키지 프레임은 난독화되지 않으므로 그대로 표시됩니다.

mapping.txt 위치

isMinifyEnabled = true인 빌드 타입을 빌드하면 R8이 매핑 파일을 만듭니다.

app/build/outputs/mapping/<빌드 배리언트>/mapping.txt

CAUTION — 매핑은 빌드마다 다릅니다

코드가 같아도 다시 빌드하면 난독화 이름이 달라질 수 있습니다. 스토어에 올린 바로 그 빌드의 mapping.txt를 올리세요.

방법 1: Gradle 플러그인 (권장)

Sophonz Gradle 플러그인을 적용했다면 R8 작업이 끝날 때 매핑 파일을 자동으로 올립니다. 플러그인 적용 방법은 설치를 참고하세요.

설정

gradle.properties에 업로드 주소를 지정합니다.

gradle.properties
sophonz.baseUrl=https://app.sophonz.ai

sophonz-config.json에 자격 증명을 넣습니다. 둘 중 하나라도 비어 있으면 업로드 작업이 등록되지 않습니다.

app/src/main/sophonz-config.json
{
  "app_id": "sophz",
  "api_token": "sophonz_pat_..."
}
키값
api_token앱의 액세스 토큰. 설정 > 프로젝트 > 앱 > 액세스 토큰에서 발급합니다
app_id플러그인이 요구하는 5자 문자열. Sophonz 서버는 이 값을 쓰지 않고 토큰으로 앱을 정하므로 임의의 5자면 됩니다

NOTE — 토큰을 커밋하지 마세요

CI에서는 sophonz-config.json에서 api_token을 빼고 환경 변수 SOPHONZ_API_TOKEN으로 넘기세요. app_id도 SOPHONZ_APP_ID로 지정할 수 있습니다.

- name: Build release
  env:
    SOPHONZ_API_TOKEN: ${{ secrets.SOPHONZ_TOKEN }}
    SOPHONZ_APP_ID: sophz
  run: ./gradlew assembleRelease

동작

  • 플러그인은 빌드마다 빌드 ID를 만들어 앱에 넣고, 같은 값으로 매핑 파일을 올립니다. SDK는 이 값을 app.build_id로 보내므로 스택 트레이스와 매핑이 빌드 단위로 정확히 연결됩니다.
  • 업로드 요청에는 버전이 없습니다. 이 빌드가 텔레메트리를 보내면 그 빌드 ID가 속한 앱 버전 아래로 자동으로 옮겨집니다. 그 전까지는 설정의 버전을 아직 모르는 빌드에 표시됩니다.
  • 매핑 파일은 zstd로 압축돼 전송됩니다.
Gradle 속성기본값설명
sophonz.baseUrl—업로드 주소. 비어 있으면 업로드 단계에서 빌드가 실패합니다
sophonz.disableMappingFileUploadfalsetrue면 매핑 업로드를 등록하지 않습니다
sophonz.failBuildOnUploadErrors—업로드 실패 시 빌드를 실패시킬지 여부

방법 2: @sophonz/cli

Gradle 플러그인을 쓰지 않거나 업로드를 별도 단계로 두려면 CLI를 사용합니다. Node.js 20 이상이 필요합니다.

export SOPHONZ_TOKEN=sophonz_pat_...
 
npx @sophonz/cli upload-mapping \
  --file app/build/outputs/mapping/release/mapping.txt \
  --version 1.4.0
옵션설명
-f, --filemapping.txt (필수)
-v, --version앱 버전. SDK가 보내는 service.version(versionName)과 같은 값
-b, --build-id빌드 ID. SDK가 보내는 app.build_id와 같은 값
--typeR8(기본) 또는 PROGUARD
-t, --token액세스 토큰. 기본값은 SOPHONZ_TOKEN

--version과 --build-id 중 하나는 있어야 합니다.

- name: Build release
  run: ./gradlew assembleRelease
 
- name: Upload mapping
  env:
    SOPHONZ_TOKEN: ${{ secrets.SOPHONZ_TOKEN }}
  run: |
    # assembleRelease가 남기는 메타데이터에서 versionName을 읽습니다
    VERSION=$(jq -r '.elements[0].versionName' app/build/outputs/apk/release/output-metadata.json)
    npx @sophonz/cli upload-mapping \
      --file app/build/outputs/mapping/release/mapping.txt \
      --version "$VERSION"

방법 3: curl

curl -X POST https://app.sophonz.ai/api/v1/mapping-files \
  -H "Authorization: Bearer $SOPHONZ_TOKEN" \
  -F type=R8 \
  -F version=1.4.0 \
  -F file=@app/build/outputs/mapping/release/mapping.txt

매칭 방식

스택 트레이스를 열면 다음 순서로 매핑 파일을 찾습니다.

  1. 트레이스의 app.build_id와 빌드 ID가 같은 파일
  2. 없으면 트레이스의 service.version과 버전이 같은 파일 중 가장 최근에 올린 것

CAUTION — 버전만으로 올릴 때

같은 버전으로 여러 번 빌드해 배포했다면 버전 매칭은 마지막에 올린 매핑을 씁니다. 이전 빌드의 트레이스는 잘못된 이름으로 복원될 수 있습니다. 버전을 재사용한다면 빌드 ID와 함께 올리세요. Gradle 플러그인은 항상 빌드 ID로 올립니다.

확인

설정 > 프로젝트 > 앱 > 매핑 파일에 앱의 버전 목록이 있습니다. 목록은 텔레메트리에서 자동으로 채워지고, 버전마다 올라온 매핑 파일과 빌드 ID가 보입니다. 텔레메트리를 보냈는데 매핑이 없는 버전에는 경고가 표시됩니다. 출시 전 버전은 버전 추가로 먼저 만들어 둘 수 있습니다.

앱이 WebView를 쓴다면 같은 화면에 웹 버전 목록도 함께 표시됩니다. WebView의 JavaScript 에러는 소스맵으로 복원됩니다.

크래시나 예외가 들어오면 로그나 트레이스에서 행을 펼칩니다. 스택 트레이스 뷰어에서 예외가 난 스레드가 맨 위에 오고, Caused by와 크래시 시점의 다른 스레드가 스레드 목록에 나옵니다. 앱 패키지의 프레임은 강조되고 연속된 플랫폼 프레임은 접혀서 보입니다. 복원 / 원본 전환으로 기기가 보낸 그대로의 프레임도 볼 수 있습니다.

업로드 API

POST https://app.sophonz.ai/api/v1/mapping-files
Authorization: Bearer sophonz_pat_...
Content-Type: multipart/form-data
필드설명
typeR8 또는 PROGUARD
filemapping.txt. zstd·gzip 압축 파일도 받습니다
version앱 버전
buildId빌드 ID

version과 buildId 중 하나는 필수입니다. Gradle 플러그인은 같은 저장소를 POST /v2/store/proguard로 호출합니다.

상태error원인
401unauthorized토큰이 없거나 잘못됐거나 만료·폐기됨
400fileMissing / typeInvalid / versionRequired필드 누락
413fileTooLarge500MB 초과
422mappingInvalid매핑 파일로 읽을 수 없음
500storageFailed서버가 파일을 저장하지 못함