Sentry 브라우저 프로파일링 (Browser Profiling)
Sentry 브라우저 프로파일링 (Browser Profiling)
Sentry 브라우저 프로파일링 완전 가이드: JS 병목 함수를 정확히 잡는 법
"이 화면이 느리다"는 건 알겠는데, 왜 느린지 모르겠다면? Sentry 브라우저 프로파일링이 콜스택 단위로 원인 함수를 직접 짚어줍니다.
목차
브라우저 프로파일링이란? {#개요}
Sentry 브라우저 프로파일링은 브라우저의 JS Self-Profiling API를 활용해, 성능 트레이스가 수집되는 구간 동안 어떤 JavaScript 함수가 CPU 시간을 얼마나 점유하는지를 함수·콜스택 단위로 기록하는 기능입니다.
기존 트레이싱이 "이 화면 전환이 2.4초 걸렸다"까지 알려준다면, 프로파일링은 그 2.4초 안에서 "그중 1.1초를 buildPricingTable 재귀 계산이 차지했다"처럼 원인 함수까지 정확히 짚어줍니다. 추측 기반의 최적화가 아닌, 측정 기반의 핀포인트 최적화가 비로소 가능해집니다.
| 구분 | 트레이싱(기존) | 프로파일링(추가) |
|---|---|---|
| 답하는 질문 | "무엇이 느린가" (페이지/요청 단위) | "왜 느린가" (함수/콜스택 단위) |
| 데이터 단위 | Transaction / Span | CPU 샘플 + 콜스택 (Flame Graph) |
| 활용 시점 | 느린 화면·요청 탐지 | 탐지된 병목의 근본 원인 함수 식별 |
동작 조건 체크리스트 {#prerequisites}
브라우저 프로파일링은 아래 조건이 모두 충족되어야 데이터가 수집됩니다. 하나라도 빠지면 프로파일이 조용히 수집되지 않을 뿐, 앱 동작이나 에러 보고에는 영향이 없습니다.
① Document-Policy: js-profiling 응답 헤더
HTML 문서 응답에 이 헤더가 있어야 브라우저가 프로파일러 시작을 허용합니다. AWS Amplify로 배포하는 프로젝트라면 amplify.yml의 customHeaders(**/*.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 동시 활성화
프로파일은 트레이스에 첨부되는 방식으로 전송됩니다. 따라서 browserProfilingIntegration과 BrowserTracing(또는 라우터 연동 변형)이 함께 활성화되어야 합니다.
현재 설정 코드 해설 {#설정}
구현 위치: 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회 결정)
});
샘플링 구조 이해하기
profileSessionSampleRate와 tracesSampleRate의 관계를 정확히 이해하는 것이 중요합니다.
profileSessionSampleRate: 1.0— 세션 시작 시 "이 세션을 프로파일링할지" 여부를 1회 결정합니다.1.0은 모든 세션을 대상으로 함을 의미합니다.- 실제 프로파일 수집량은
tracesSampleRate에 종속됩니다. 프로파일은 샘플링된 트레이스 구간에만 첨부되므로,profileSessionSampleRate를1.0으로 설정해도 최종 프로파일 수집량은 트레이스 볼륨(약 10%)을 초과하지 않습니다. 비용이 자동으로 통제되는 구조입니다. - 활성 환경: 운영(
main브랜치 +origindrop호스트)에서만 초기화됩니다(isOriginDropProduction게이트). 로컬·테스트 환경에서는 동작하지 않습니다.
실전 활용법 {#활용법}
병목 함수 찾기 (가장 기본적인 워크플로)
- Sentry → Performance 또는 Profiling 탭 진입
- 느린 Transaction(예:
Find products라우트 로드,Image Editor진입)을 선택 - 해당 트레이스의 Flame Graph(불꽃 그래프) 확인
- 가로 폭이 넓은 막대 = CPU 시간을 많이 차지한 함수
src/...경로의 우리 코드 중 폭이 넓은 함수가 1차 최적화 후보
- 후보 함수를
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단계 구조로 이를 차단합니다.
build.sourcemap: 'hidden'— 소스맵을 생성하되 번들 JS에//# sourceMappingURL주석을 삽입하지 않음 → 브라우저가 소스맵을 요청조차 하지 않음@sentry/vite-plugin— 빌드 중 소스맵을 Sentry 서버로만 업로드 (비공개 처리)filesToDeleteAfterUpload—dist/**/*.map을 빌드 후 즉시 삭제 → 배포 서버에.map파일 자체가 남지 않음- 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 variables에 SENTRY_AUTH_TOKEN = 발급한 토큰을 추가합니다. 전 브랜치 공유로 설정해도 코드 가드(isMainBranch)로 인해 실제 업로드는 main 브랜치에서만 실행됩니다.
③ 재배포로 활성화
토큰 등록 후 main 브랜치를 재배포하면 해당 빌드부터 소스맵 업로드가 시작됩니다.
안전장치: 토큰이 없거나 잘못되어도 빌드 자체는 실패하지 않습니다. 토큰이 없으면 플러그인을 로드하지 않고(소스맵 미생성), 토큰이 틀려