Sentry: 저 알죠? 근데 왜 모른 척해요?

아, 유저 사이드에서 발생하는 앱 충돌 잡기 좋은 서비스 없나..? 아. 이미 써봤구나. "Sentry" 리마인드 정리

Jun Noh

요즘 주주의 유저가 1350명을 넘어가면서 천천히 꾸준하게 성장하고 있다.

개발자로써는 매우 기분 좋은 소식이고, 주주가 궤도에 올랐다는 것 자체가 나에게는 개인적으로 아주 큰 성과를 이뤘다고 생각한다. (여전히 굉장한… 적자이지만… 뭐…)

아무튼 유저가 늘어난다고 좋은 점만 있는 것은 또 아니다.

1인 개발 특성상, 그리고 주주는 현재 주 1~2회 업데이트라는 굉장히 빠른 패치 주기를 가지고 가고 있기 때문에 항상 부족한 건 QA를 할 시간이다.

따로 테스트 빌드와 테스트 서버를 운영하기엔… 이미 적자라 DB 인스턴스 하나도 아껴야해서 불가능하다.

그래서 유저가 늘어날 수록 내가 잡지 못했던 이슈들이 막 여기 저기서 터져나오고 있는 중이다.

지난주가 정말 심했는데, 지난주에 패치 하나를 올리면서 갑자기 특정 화면에서 시스템 폰트로 키보드가 올라오면 앱이 다운되어 버리는 이슈가 여기 저기서 터져나왔다.

원래 있던 이슈였는데, 유저가 늘어나면서 이제야 발견이 된 것인지… 아니면 애초에 없었는데 지난 번 빌드가 뭔가 잘못 됐던건지.. 알 수는 없지만, 아무튼 그래서 지난주 월, 화요일은 잠을 거의 잘 수가 없었다.

해당 버그가 재현이 안될 뿐더러, 다른 버그도 여러 곳에서 막 터져나왔다.

문제는 QA가 아닌 일반 유저 버그 리포트의 특성상, 내가 어디서 어떻게 이슈가 났는지 알 수가 없었다.

문제 상황을 추측해서 “아, 혹시 이게 문제인가?” 하고 찾아가는 수 밖에 없었다.

그러다보니.. 버그 픽스는 더 느려지고 확실하게 고쳤다! 라고 할 수도 없었다.

아, 맞다.

그러다가 밥플을 만들 때, 적용했던.. 그리고 내가 그 방법을 글로 정리까지 했던 “Sentry”가 생각이 났다.

밥플에 Sentry를 적용하고, 글을 쓴 게.. 1월이니까.. 기억이 안날만도 했다…ㅋㅋ 그래서 리마인드 차원에서 다시 한 번 정리해보려고 한다.

Sentry. 내 앱에 있는 엔트맨.

센트리는 그 이름과는 다르게 앱 빌드 최상단에서 선언해놓으면, 엔트맨마냥 안에서 숨어있다가, 앱이 크래시가 나거나 ANR(Application Not Response)가 나면 바로 알려주는 그런 서비스다.

나름 바로 메일도 오고, 대시보드로 실시간 확인이 가능해서 매우… 매우 유용하다.

로컬에서 개발할 땐 크래시 나면 그냥 콘솔 켜서 스택 보고 아 여기구나 하면 되는데, 유저 폰에서 충돌이 나면… 그건 알 수 있는 방법이 없다.

Play Console 이 native crash 는 잡아주긴 하는데.. React Native 앱한테는 반쪽짜리다.

JS 예외 메시지가 다 소실되고, “어느 화면에서 뭐 하다가 크래시났는지” 도 모른다. 그냥 “어디선가 튕겼음” 만 안다.

App Store Connect 도 마찬가지. 크래시 로그는 있는데 symbolicate 이 자꾸 실패하고, 유저 세션이 안 붙어서 결국 나 혼자 “여기서 났나…?” 하고 추측하게 된다.

Sentry 는 그걸 다 해결해준다.

JS 스택 + native 스택 둘 다 잡고, 유저가 크래시 직전에 어떤 화면을 지나왔는지 breadcrumb 을 자동으로 남긴다.

소스맵도 빌드할 때 자동 업로드해서 스택이 minified 가 아니라 진짜 app/(auth)/splash.tsx:45 로 보인다.

그러니까 크래시 하나 열면 “이 유저가 이 기기에서 이 화면 이 화면 지나서 이 코드 이 줄에서 튕겼음” 이라는 그림이 즉시 나온다.

이거 하나 갖춰두는게 1인 개발자한테는 사실상 QA 팀 붙여놓는 거랑 비슷하다.

유료임?

이게 진짜 개발자 사랑받는 부분인데, 무료 티어가 너무 후하다.

Developer 플랜 (무료) 이 에러 이벤트 월 5,000건 · 데이터 30일 보관 · 소스맵 업로드까지 다 준다.

유료로 넘어가면 $29/월 인데.. 뭐, 이젠 하도 3자리 수 달러를 많이 봐서.. 이젠 저게 빼빼로 하나 사먹는 거 마냥 가성비로 느껴진다ㅋㅋ;;

주주 유저 1,350명 기준, 지난 14일 동안 잡힌 크래시 이벤트가 12개다.

유저가 지금의 100배가 되어도 무료로 커버된다. 유료로 넘어갈 때 되면.. 뭐 그때는 유료 낼 수 있는 매출이 나올거고, 안 나온다면? 그럼 뭐, 주주는 없어져야지..

초기 셋팅 (Expo · React Native 기준)

sentry.io 가입 → Create Project → 플랫폼 React Native 선택. DSN 이라는 URL 하나 발급된다.

이게 우리 앱이 이벤트를 쏘는 주소다.

패키지 설치.

npx expo install @sentry/react-native
app.json 에 config plugin 추가.


{
  "expo": {
    "plugins": [
      ["@sentry/react-native/expo", {
        "organization": "your-org-slug",
        "project": "your-project-slug"
      }]
    ]
  }
}

Auth token 발급

이건 빌드할 때 소스맵을 Sentry 에 올려주는 용도다.

Sentry 설정에서 Account → API → Auth Tokens → Create New.

스코프는 project:releases 만 있으면 된다.

발급된 토큰을 sentry.properties 나 CI 환경변수 SENTRY_AUTH_TOKEN 에 넣는다.

혹은… AI 에이전트한테 이슈까지 싹 읽어서 대처하라고 시킬거면, event:read 까지만 추가하면 될 거 같다.

앱 최상단에서 init 호출.

// lib/sentry.ts
import * as Sentry from '@sentry/react-native';
import Constants from 'expo-constants';

export function initSentry(): void {
    const dsn = process.env.EXPO_PUBLIC_SENTRY_DSN;
    if (!dsn) return;

    Sentry.init({
        dsn,
        enabled: !__DEV__,   // dev 빌드는 이벤트 전송 안 함
        release: `com.zuzoo.mobile@${Constants.expoConfig?.version}`,
        environment: __DEV__ ? 'development' : 'production',
        tracesSampleRate: 0.1,
        enableNativeCrashHandling: true,
        integrations: [Sentry.reactNavigationIntegration()],
    });
}

이걸 _layout.tsx 최상단에서 한 번 호출.

끝이다.

첫 릴리스 빌드 배포하고 며칠 뒤에 대시보드 열면.. 처음 뜨는 이슈 하나가 다 설명해줄 거다.

내가 왜 이걸… 잊고 있었지..? 그냥 머리가 딸려서 고생을 꽤나 하는 타입이다. 항상 보면..

그래서 이슈 좀 잡음? 진짜 쓸만함?

주주에 Sentry 를 붙이고 나니까 대시보드에 슬금슬금 이슈가 쌓이기 시작했다.

지금은 unresolved 상태로 9개. (왜.. 이렇게 많아요?)

첫번째는 앱을 켰을 때 스플래시 화면에서 router.replace() 를 부르는데, “Root Layout 이 마운트 되기 전에 navigate 하지 마세요” 에러가 뜬다는 거였다.

나는 이거 재현이 도저히 안 됐다. 내 폰에서는 백번 켜도 정상. 유저 리포트도 없었다.

근데 Sentry 대시보드에 쓸모 있는 정보가 있었다.

“Galaxy S25 Ultra · Android 16 · production 빌드에서 발생”

이거 설마 울트라라서 개빨라서 그런가?

아 최신 플래그십이 콜드 스타트가 너무 빨라서 useEffect 가 Root Layout 의 이 첫 렌더되기 전에 실행돼버린 거였다.

보통의 폰에서는 걸릴 일이 없고, 유저 입장에서는 앱 다시 켜면 정상 진입되니까 아마 리포트도 안 했을 거다.

그냥 조용히 “이 앱 가끔 튕기네” 하고 넘어갔겠지… 그리고 다음번에 별점 1점을… 받았을지도 모른다.

이건 내일 다룰 이야기긴 하지만.. 별점 1점은 진짜 무슨 송곳 같이 심장에 꽂힌다…ㅋㅋㅋㅋㅋ

아무튼, Sentry 없었으면 이거 절대 못 잡았을 거 같다.

이런 식이다.

어떤 유저는 홈 화면을 여러번 왔다갔다 하다가 종목 상세에 들어가면 OOM 나서 앱이 죽더라. Galaxy S23 Ultra 12기가 램 폰인데..

Android 는 앱 프로세스당 heap 을 268MB 로 제한해서 애니메이션 WebP 를 캐시하다 폭발한 거였다.

Sentry breadcrumb 에 “홈 → 홈 → 홈 → 홈 → 종목상세” 반복 왕복이 딱 남아있었다.

재현 힌트를 유저가 대신 남겨준 셈이다.

또 어떤 이슈는 인형뽑기 화면에서 나오는 Android RenderScript 시스템 세그폴트.

이건 내 코드가 아니라 시스템 deprecated API 문제라 가 고칠 수 있는 문제는 아니지만…

이런 건 Sentry init 의 beforeSend 훅으로 걸러버리면 된다.

Sentry.init({
    ...
    ignoreErrors: [
        /DevLauncherController/,
        /JavaCallback was already settled/,
    ],
    beforeSend(event) {
        const values = event.exception?.values ?? [];
        for (const v of values) {
            const fn = v.stacktrace?.frames?.slice(-1)[0]?.function ?? '';
            if (/^rsd[A-Z]/.test(fn)) return null;   // RenderScript 시스템 크래시 drop
        }
        return event;
    },
});

이러면 진짜 내가 고쳐야 할 이슈만 대시보드에 뜬다.

노이즈 걸러내야 진짜 위험 신호가 눈에 들어온다.

지난 2주 동안 9개 이슈 중에 실제로 코드 고친 건 1개, 무의미한 노이즈로 필터 건 게 4개, 그리고 관찰 상태 4개.

이 중에 유저 리포트로 넘어온 건 0개다. Sentry 없었으면 이 9개 중 상당수는 “가끔 튕기네” 로 별점만 계속 갉아먹고 있었을 거다.

결론

밥플 만들 때 Sentry 를 붙여봤던 과거의 나에게 감사한다.

(얘는 릴리즈도 전에 호들갑 떨면서 붙였었는데… 사용 유저가 한 명도 없을지는 몰랐지…ㅋㅋㅋㅋ)

아무튼, 이제 유저 리포트가 아닌 앱 충돌이 뜨자마자 바로 상세한 로그로 확인해서 훨씬 주도적인 버그 fix가 가능해졌다.

사실 진즉 했어야 했지만, 핑계 아닌 핑계로 진짜 너무 할 일이 많았어서…ㅠㅠ

…

그냥 사실 더 이상 “블로그 글 쓰기” 연차가 남아있질 않아서 좀… 써봤다.

암튼 도움 됐죠…?

마침.

다른 글 보기