Logo
Bason

Sentry 브라우저 프로파일링 (Browser Profiling)

Sentry 브라우저 프로파일링 (Browser Profiling)

이성훈
이성훈June 17, 2026 · 1 views

Sentry 브라우저 프로파일링 완전 가이드: JS 병목 함수를 정확히 잡는 법

"이 화면이 느리다"는 건 알겠는데, 느린지 모르겠다면? Sentry 브라우저 프로파일링이 콜스택 단위로 원인 함수를 직접 짚어줍니다.


목차

  1. 브라우저 프로파일링이란?
  2. 동작 조건 체크리스트
  3. 현재 설정 코드 해설
  4. 실전 활용법
  5. 기대 효과
  6. 소스맵 업로드와 함수명 복원
  7. 운영 비용 관리

브라우저 프로파일링이란? {#개요}

Sentry 브라우저 프로파일링은 브라우저의 JS Self-Profiling API를 활용해, 성능 트레이스가 수집되는 구간 동안 어떤 JavaScript 함수가 CPU 시간을 얼마나 점유하는지를 함수·콜스택 단위로 기록하는 기능입니다.

기존 트레이싱이 "이 화면 전환이 2.4초 걸렸다"까지 알려준다면, 프로파일링은 그 2.4초 안에서 "그중 1.1초를 buildPricingTable 재귀 계산이 차지했다"처럼 원인 함수까지 정확히 짚어줍니다. 추측 기반의 최적화가 아닌, 측정 기반의 핀포인트 최적화가 비로소 가능해집니다.

구분트레이싱(기존)프로파일링(추가)
답하는 질문"무엇이 느린가" (페이지/요청 단위)"왜 느린가" (함수/콜스택 단위)
데이터 단위Transaction / SpanCPU 샘플 + 콜스택 (Flame Graph)
활용 시점느린 화면·요청 탐지탐지된 병목의 근본 원인 함수 식별

동작 조건 체크리스트 {#prerequisites}

브라우저 프로파일링은 아래 조건이 모두 충족되어야 데이터가 수집됩니다. 하나라도 빠지면 프로파일이 조용히 수집되지 않을 뿐, 앱 동작이나 에러 보고에는 영향이 없습니다.

Document-Policy: js-profiling 응답 헤더

HTML 문서 응답에 이 헤더가 있어야 브라우저가 프로파일러 시작을 허용합니다. AWS Amplify로 배포하는 프로젝트라면 amplify.ymlcustomHeaders(**/*.html)에 설정해야 합니다.

② Chromium 계열 브라우저

JS Self-Profiling API는 현재 Chrome·Edge 등 Chromium 기반 브라우저에서만 동작합니다. Firefox·Safari에서는 에러 없이 자동으로 비활성화됩니다.

③ Secure Context (HTTPS)

Self-Profiling API는 HTTPS 환경(Secure Context)에서만 작동합니다. 로컬 개발 환경(http://localhost)은 예외적으로 허용되는 경우도 있으나, 운영 도메인은 반드시 HTTPS여야 합니다.

browserProfilingIntegration + BrowserTracing 동시 활성화

프로파일은 트레이스에 첨부되는 방식으로 전송됩니다. 따라서 browserProfilingIntegrationBrowserTracing(또는 라우터 연동 변형)이 함께 활성화되어야 합니다.


현재 설정 코드 해설 {#설정}

구현 위치: apps/web/src/shared/util/sentry.ts, amplify.yml

Sentry.init({
    // ...
    integrations: [
        Sentry.tanstackRouterBrowserTracingIntegration(router), // 라우터 연동 트레이싱
        Sentry.browserProfilingIntegration(),                   // 브라우저 프로파일링
    ],
    tracesSampleRate: 0.1,         // 트레이스 10% 샘플링 (비용 정책상 유지)
    profileSessionSampleRate: 1.0, // 모든 세션을 프로파일링 대상으로 (init 시점 1회 결정)
});

샘플링 구조 이해하기

profileSessionSampleRatetracesSampleRate의 관계를 정확히 이해하는 것이 중요합니다.

  • profileSessionSampleRate: 1.0 — 세션 시작 시 "이 세션을 프로파일링할지" 여부를 1회 결정합니다. 1.0은 모든 세션을 대상으로 함을 의미합니다.
  • 실제 프로파일 수집량은 tracesSampleRate에 종속됩니다. 프로파일은 샘플링된 트레이스 구간에만 첨부되므로, profileSessionSampleRate1.0으로 설정해도 최종 프로파일 수집량은 트레이스 볼륨(약 10%)을 초과하지 않습니다. 비용이 자동으로 통제되는 구조입니다.
  • 활성 환경: 운영(main 브랜치 + origindrop 호스트)에서만 초기화됩니다(isOriginDropProduction 게이트). 로컬·테스트 환경에서는 동작하지 않습니다.

실전 활용법 {#활용법}

병목 함수 찾기 (가장 기본적인 워크플로)

  1. Sentry → Performance 또는 Profiling 탭 진입
  2. 느린 Transaction(예: Find products 라우트 로드, Image Editor 진입)을 선택
  3. 해당 트레이스의 Flame Graph(불꽃 그래프) 확인
    • 가로 폭이 넓은 막대 = CPU 시간을 많이 차지한 함수
    • src/... 경로의 우리 코드 중 폭이 넓은 함수가 1차 최적화 후보
  4. 후보 함수를 useMemo / 가상화 / 디바운스 / 알고리즘 개선 등으로 최적화 → 재배포 후 동일 트랜잭션의 Flame Graph로 개선 효과를 수치로 검증

"체감상 느린데 원인을 모를 때"

트레이싱만으로는 "이 화면이 느리다"까지만 보이고, 원인 함수는 추측에 의존하게 됩니다. 프로파일링을 활성화하면 추측 없이 실측 콜스택으로 원인을 좁힐 수 있습니다. 이는 "Measure First(추측 말고 측정)" 원칙과 정확히 맞닿아 있습니다.

회귀 감시 (릴리스 간 비교)

  • 특정 함수(예: 가격 규칙 계산, 캔버스 렌더, 대량 목록 렌더)가 배포 이후 CPU 점유가 늘었는지 릴리스 간 비교가 가능합니다.
  • 무거운 계산이 메인 스레드를 오래 점유하면 입력 지연(끊김)으로 직결됩니다. Flame Graph에서 메인 스레드 장시간 점유 함수를 우선 분리·최적화 대상으로 삼으세요.

우리 서비스에서 기대되는 효과 {#기대효과}

클라이언트 연산이 집중되는 서비스라면 브라우저 프로파일링의 효과가 특히 뚜렷합니다. 다음과 같은 영역에서 즉각적인 인사이트를 기대할 수 있습니다.

기능 영역프로파일링으로 확인 가능한 것
상품 목록·그리드lazy render·가상화가 실제로 효과를 내는지, 카드 렌더 함수가 병목인지 실측 검증
이미지 에디터 (Fabric.js)렌더·변환 연산 중 어떤 호출이 프레임 드랍을 유발하는지 식별
가격 규칙·집계 계산중첩 배열 연산이 메인 스레드를 막는 구간을 콜스택으로 특정

결과적으로 "느리다는 제보 → 추측 수정 → 재배포"의 반복 사이클에서 벗어나, 측정 기반의 핀포인트 최적화로 전환할 수 있습니다.


소스맵 업로드와 함수명 복원 {#소스맵}

운영 번들은 minify되어 함수명이 t, e 같은 한 글자로 압축됩니다. Sentry는 업로드된 소스맵으로 이를 원래 함수명으로 복원합니다. 소스맵이 없으면 Flame Graph와 에러 스택트레이스가 모두 minify된 채로 표시되어 분석이 사실상 불가능합니다.

구현 위치: apps/web/vite.config.ts

// main 브랜치 + 빌드 환경에 SENTRY_AUTH_TOKEN이 있을 때만 동작
const enableSentrySourcemaps = isMainBranch && Boolean(process.env.SENTRY_AUTH_TOKEN);

// build.sourcemap: 'hidden' → 소스맵 생성하되 번들에 참조 주석(sourceMappingURL) 미삽입
sentryVitePlugin({
    org: 'type-b',
    project: 'origin-drop',
    authToken: process.env.SENTRY_AUTH_TOKEN,
    sourcemaps: { filesToDeleteAfterUpload: ['./dist/**/*.map'] },
});

소스코드 노출 방지 (보안 핵심)

소스맵을 그대로 public 배포하면 소스코드가 외부에 노출됩니다. 아래 4단계 구조로 이를 차단합니다.

  1. build.sourcemap: 'hidden' — 소스맵을 생성하되 번들 JS에 //# sourceMappingURL 주석을 삽입하지 않음 → 브라우저가 소스맵을 요청조차 하지 않음
  2. @sentry/vite-plugin — 빌드 중 소스맵을 Sentry 서버로만 업로드 (비공개 처리)
  3. filesToDeleteAfterUploaddist/**/*.map을 빌드 후 즉시 삭제 → 배포 서버에 .map 파일 자체가 남지 않음
  4. debug ID 기반 매칭 — 번들과 소스맵에 자동 주입되는 debug ID로 함수명을 매칭하므로 별도 release 설정이 불필요

주의: SENTRY_AUTH_TOKEN은 빌드 시점에만 사용되는 시크릿으로, 클라이언트 번들에 포함되지 않습니다. 공개 가능한 DSN과 달리, 이 토큰은 외부에 절대 노출되어서는 안 됩니다.

토큰 발급 및 등록 절차

① 토큰 발급

Sentry → Settings → Auth Tokens (Organization Auth Token 권장) → 생성. 소스맵 업로드에 필요한 scope는 project:releases + org:read이며, 조직 토큰은 기본적으로 충분한 권한을 포함합니다.

② 환경 변수 등록

AWS Amplify 콘솔 → 해당 앱 → App settings → Environment variablesSENTRY_AUTH_TOKEN = 발급한 토큰을 추가합니다. 전 브랜치 공유로 설정해도 코드 가드(isMainBranch)로 인해 실제 업로드는 main 브랜치에서만 실행됩니다.

③ 재배포로 활성화

토큰 등록 후 main 브랜치를 재배포하면 해당 빌드부터 소스맵 업로드가 시작됩니다.

안전장치: 토큰이 없거나 잘못되어도 빌드 자체는 실패하지 않습니다. 토큰이 없으면 플러그인을 로드하지 않고(소스맵 미생성), 토큰이 틀려

지원 문의