Skip to content

[FEAT] Sentry 기반 에러 모니터링 환경 구성#439

Open
useon wants to merge 10 commits intodevelopfrom
feat/#435
Open

[FEAT] Sentry 기반 에러 모니터링 환경 구성#439
useon wants to merge 10 commits intodevelopfrom
feat/#435

Conversation

@useon
Copy link
Copy Markdown
Contributor

@useon useon commented Mar 31, 2026

🚩 연관 이슈

closed #435

📝 작업 내용

이번 작업은 프론트에서 발생하는 에러를 실사용자 기준으로 수집하고, 운영에서 바로 확인하고 디버깅할 수 있는 형태로 남기기 위한 Sentry 환경 구성입니다.

이번 PR에서는 범위를 넓게 가져가기보다, 에러가 발생했을 때 누가 어떤 흐름에서 실패했는지 빠르게 파악할 수 있는 상태를 만드는 것에 우선순위를 뒀습니다. 그래서 Error Monitoring, Session Replay, Source Map 업로드를 먼저 적용했고, Logs나 Profiling, component stack 가독성 개선은 이번 범위에서 제외했습니다.

1. Sentry 초기화 및 수집 범위 구성

  • src/instrument.ts를 추가하고, production 환경에서만 Sentry가 초기화되도록 구성했습니다.
  • Session Replay는 전체 세션을 수집하지 않고, 에러가 발생한 세션만 남도록 설정했습니다.
  • tracing은 백엔드가 Datadog를 사용 중인 점을 고려해, 프론트 단독 성능 확인 용도로만 낮은 비율(0.1)로 유지했습니다.

처음부터 모든 기능을 넓게 켜기보다는, 지금 바로 운영에서 필요한 정보만 먼저 수집하도록 구성했습니다.

2. 렌더링 에러와 API 에러 분리 수집

  • ErrorBoundary에서 잡힌 렌더링 에러와 axiosInstance에서 잡는 API 에러를 별도로 수집하도록 구성했습니다.
  • 현재는 401과 정상/리다이렉트 응답만 제외하고, 4xx / 5xx / 네트워크 실패 / 타임아웃을 수집합니다.
  • 5xx는 어떤 화면/행동에서 사용자 장애가 발생했는지 프론트 맥락으로 확인하기 위한 용도로 포함했습니다.

3. 사용자 식별 기준 정리

  • 로그인 시 Sentry.setUser를 연동했습니다.
  • 로그아웃과 세션 만료에는 user context가 제거되도록 처리했습니다.
  • user context에는 내부 사용자 id만 사용하고, email은 넣지 않았습니다.
  • API 에러 전송 시 요청/응답 바디 전체는 개인정보 노출 위험 때문에 제거했습니다.

4. Source Map 업로드 및 검증

운영 환경에서 에러를 수집하더라도, 스택트레이스가 번들된 JS 기준으로만 보이면 실제 원인 추적이 어렵다고 판단했습니다. 그래서 이번 PR에서는 에러를 수집하는 것과 함께, 수집된 에러를 실제로 읽을 수 있게 만드는 것까지 같이 진행했습니다.

  • vite.config.ts에 @sentry/vite-plugin을 추가했습니다.

  • 인증값 없이 배포 산출물에 포함되는 상황을 막기 위해 source map은 production 빌드에서, Sentry 인증값이 설정된 경우에만 생성 및 업로드되도록 정리했습니다.

  • 업로드 후 dist/**/*.map 파일은 자동 삭제되도록 처리했습니다.

  • preview 환경에서 실제로 에러를 발생시켜 symbolication이 정상 동작하는 것까지 확인했습니다.

  • 적용 전

소스맵 적용 전
  • 적용 후
소스맵 적용후

🏞️ 스크린샷 (선택)

image

Sentry에게 인사해주세요 호호호 ^^

🗣️ 리뷰 요구사항 (선택)

궁금하신 점이나 설정 이런 것 추가는 어떤가요? 등등 자유롭게 달아주세요 ~ !!

Summary by CodeRabbit

  • 새로운 기능

    • 애플리케이션 전반에 오류 보고 및 추적 통합(런타임 오류, 렌더 오류, API 오류)
    • 성능/세션 관련 수집 및 선택적 초기화(프로덕션 환경에서만 활성화)
    • 빌드 시 소스맵 업로드 조건부 지원
  • 버그 수정 / 동작 개선

    • 로그아웃 시 사용자 컨텍스트 정리
    • 클라이언트 API 오류에서 불필요한 중복 보고 최소화 및 메타데이터 보강

@useon useon requested review from i-meant-to-be and jaeml06 March 31, 2026 14:07
@useon useon self-assigned this Mar 31, 2026
@useon useon added chore 파일 내부가 아닌 파일 자체에 대한 변경 (디렉토리 이동, 파일 이름 변경, 파일 삭제 등) config 외부 라이브러리 관련 추가 및 설정 labels Mar 31, 2026
@useon useon linked an issue Mar 31, 2026 that may be closed by this pull request
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 31, 2026

🚀 Preview 배포 완료!

환경 URL
Preview 열기
API Dev 환경

PR이 닫히면 자동으로 정리됩니다.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

Walkthrough

프로젝트에 Sentry를 통합하여 빌드/런타임에 SDK 초기화, Vite 플러그인 추가, API 오류 및 렌더/로그아웃 관련 사용자 컨텍스트를 캡처/정리하도록 변경합니다. (약 50단어 이내)

Changes

Cohort / File(s) Summary
의존성
package.json
@sentry/react (^10.46.0) 및 @sentry/vite-plugin (^5.1.1) 추가
초기화/빌드 통합
src/instrument.ts, src/main.tsx, vite.config.ts
환경 기반 Sentry 초기화(instrument.ts) 및 Vite Sentry 플러그인 조건부 적용, 소스맵 설정(숨김/비활성화) 추가; main.tsx에 사이드이펙트 임포트 추가
API 레이어 및 에러 캡처
src/apis/axiosInstance.ts, src/apis/primitives.ts
Axios 인스턴스에 에러 캡처 헬퍼(captureClientApiError) 추가, 요청 타임아웃 상수화(5000ms), 응답 인터셉터에서 Sentry 보고 로직 삽입, APIError에 __sentry_captured__ 플래그 전파
가입/인증 연동
src/apis/apis/member.ts
회원 생성(로그인) 성공 시 Sentry 사용자 컨텍스트 설정(Sentry.setUser) 추가
런타임 에러 처리·로그아웃
src/components/ErrorBoundary/ErrorBoundary.tsx, src/hooks/mutations/useLogout.ts
렌더 단계 에러를 Sentry로 조건부 전송(태그/추가 컨텍스트 포함), 로그아웃 성공 시 Sentry 사용자 컨텍스트 제거(Sentry.setUser(null)) 추가

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant App
  participant Axios as HTTP Client
  participant API
  participant Sentry

  Browser->>App: 사용자 가입/로그인 요청
  App->>Axios: POST /api/member (credentials)
  Axios->>API: HTTP 요청
  API-->>Axios: 200 + auth header, body{id}
  Axios-->>App: 응답 전달 (토큰 저장)
  App->>Sentry: Sentry.setUser(id)
Loading
sequenceDiagram
  participant Browser
  participant App
  participant Axios as HTTP Client
  participant API
  participant Sentry

  Browser->>App: API 요청
  App->>Axios: 요청 실행
  Axios->>API: HTTP 요청
  API-->>Axios: 5xx 또는 에러 응답
  Axios->>App: 에러 리턴 (인터셉터)
  App->>Sentry: captureClientApiError(error)  -- 태그/컨텍스트 포함
  Sentry-->>Sentry: 이벤트 저장/전송
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • i-meant-to-be
  • jaeml06

Poem

"🐇 새벽 코드밭을 달리는 토끼입니다.
Sentry 깃발 들고 버그를 쫓아,
로그인엔 흔적을 남기고, 로그아웃엔 작별을,
렌더 오류엔 소곤소곤 알리며,
밤하늘 아래 에러는 더 이상 숨지 못하네."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 제목은 Sentry 기반 에러 모니터링 환경 구성이라는 주요 변경 사항을 명확하게 요약하고 있으며, PR의 핵심 목표를 정확히 반영합니다.
Linked Issues check ✅ Passed PR의 모든 코드 변경 사항이 #435의 목표인 프론트엔드 에러 수집 및 모니터링을 위한 Sentry 도입 요구 사항을 완전히 충족합니다.
Out of Scope Changes check ✅ Passed 모든 변경 사항이 Sentry 통합 범위 내에 있으며, PR 설명에서 의도적으로 제외한 항목들(로그, 프로파일링, 401 에러 등)은 포함되지 않았습니다.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#435

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request integrates Sentry for error tracking and performance monitoring across the application. Key changes include the addition of Sentry dependencies, initialization logic in a new instrument file, and the capture of user context during authentication flows. Furthermore, an Axios interceptor was implemented to log 4xx API errors, and the global ErrorBoundary was updated to report rendering exceptions with component stacks. The review feedback suggests expanding the scope of captured API errors to include 5xx and network failures for better debugging context, and implementing a flag-based mechanism to prevent duplicate error reporting between the Axios interceptor and the ErrorBoundary.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/apis/axiosInstance.ts (1)

108-116: ⚠️ Potential issue | 🟡 Minor

리프레시 토큰 실패 시 Sentry 사용자 컨텍스트가 정리되지 않습니다.

useLogout.ts에서는 로그아웃 성공 시 Sentry.setUser(null)을 호출하지만, 여기서 리프레시 토큰 실패로 인한 강제 로그아웃 시에는 Sentry 사용자 컨텍스트가 정리되지 않습니다. 이후 발생하는 에러에 이전 사용자 정보가 잘못 연결될 수 있습니다.

🛠️ 제안하는 수정
       } catch (refreshError) {
         console.error('Refresh Token is invalid or expired', refreshError);
         // 재발급도 실패하면 -> 로그인 페이지 이동
         const currentLang = i18n.resolvedLanguage ?? i18n.language;
         const lang = isSupportedLang(currentLang) ? currentLang : DEFAULT_LANG;
         window.location.href = buildLangPath('/home', lang);
         removeAccessToken();
+        Sentry.setUser(null);
         return Promise.reject(refreshError);
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/apis/axiosInstance.ts` around lines 108 - 116, In the refresh token catch
block inside axiosInstance.ts (the catch handling refreshError), clear the
Sentry user context before redirecting and removing tokens: call
Sentry.setUser(null) (ensure Sentry is imported) right before
removeAccessToken() / window.location.href so subsequent errors aren’t
attributed to the old user; keep existing logic that computes lang via
i18n.resolvedLanguage/i18n.language, isSupportedLang, DEFAULT_LANG and uses
buildLangPath('/home', lang) and removeAccessToken().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@vite.config.ts`:
- Around line 30-32: 현재 빌드 설정은 sourcemap: 'hidden'을 isProductionBuild일 때 무조건
적용하고 있어 hasSentryAuth가 false면 .map이 dist에 남아 S3로 업로드됩니다; update the logic so
sourcemap is set to 'hidden' only when both isProductionBuild and hasSentryAuth
are true (otherwise false), and ensure the existing filesToDeleteAfterUpload
behavior is aligned with hasSentryAuth; locate and modify the sourcemap
assignment (sourcemap, isProductionBuild) and the
filesToDeleteAfterUpload/hasSentryAuth checks so they use the same hasSentryAuth
guard.

---

Outside diff comments:
In `@src/apis/axiosInstance.ts`:
- Around line 108-116: In the refresh token catch block inside axiosInstance.ts
(the catch handling refreshError), clear the Sentry user context before
redirecting and removing tokens: call Sentry.setUser(null) (ensure Sentry is
imported) right before removeAccessToken() / window.location.href so subsequent
errors aren’t attributed to the old user; keep existing logic that computes lang
via i18n.resolvedLanguage/i18n.language, isSupportedLang, DEFAULT_LANG and uses
buildLangPath('/home', lang) and removeAccessToken().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: be5b0840-5462-43f5-925e-38a9d16cbc2a

📥 Commits

Reviewing files that changed from the base of the PR and between 8aaff3a and ef0dde5.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • package.json
  • src/apis/apis/member.ts
  • src/apis/axiosInstance.ts
  • src/components/ErrorBoundary/ErrorBoundary.tsx
  • src/hooks/mutations/useLogout.ts
  • src/instrument.ts
  • src/main.tsx
  • vite.config.ts

baseURL: config?.baseURL,
params: config?.params,
requestData: config?.data,
responseData: response?.data,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[이슈 1] 민감 데이터가 Sentry에 그대로 전송될 수 있습니다

requestData: config?.dataresponseData: response?.data는 요청/응답 바디 전체를 Sentry 이벤트에 포함합니다.

지금은 무해할 수 있지만, 향후 API가 확장되면 이름, 이메일, 전화번호 등 개인정보가 포함된 바디가 Sentry 대시보드에 노출될 수 있습니다. GDPR 및 개인정보보호법 관점에서 리스크가 있습니다.

해결 방안

responseData는 제거하고, requestData는 디버깅에 필요한 필드만 선택적으로 포함하도록 변경하는 것을 권장합니다.

extra: {
  url: config?.url,
  method: config?.method,
  baseURL: config?.baseURL,
  params: config?.params,
  // requestData: config?.data,    ← 제거: 요청 바디에 민감 정보 포함 가능
  // responseData: response?.data, ← 제거: 응답 바디에 개인정보 노출 위험
  timeout: config?.timeout ?? requestTimeoutMs,
},

status code와 url만으로도 4xx 에러 원인 파악에 충분한 경우가 많습니다. 만약 요청 정보가 꼭 필요하다면 민감 필드를 제외한 safe subset만 직렬화하는 별도 함수를 두는 것도 방법입니다.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그런데 우리 프로젝트에서는 크게 문제 없을 것 같긴해요. 민감한 정보는 없을 것 같아서...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

꼼꼼하게 봐주셔서 감사합니다! 현재 민감한 정보는 없지만 그렇다고 해서 전송할 이유도 없는 것 같아서 지웠습니다! 추후 필요하다면 민감하지 않은 정도로 선택해서 포함하도록 변경하겠습니다 !!

@@ -79,6 +115,9 @@ axiosInstance.interceptors.response.use(
return Promise.reject(refreshError);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[이슈 2] 토큰 만료로 인한 자동 로그아웃 시 Sentry user context가 정리되지 않습니다

refresh token 재발급 실패 시 removeAccessToken()은 호출되지만 Sentry.setUser(null)은 호출되지 않습니다. 이후 발생하는 에러 이벤트에 이전 사용자의 컨텍스트가 남아 잘못된 사용자에게 에러가 귀속될 수 있습니다.

useLogout.ts에서는 잘 처리하고 있지만, 세션 만료에 의한 강제 로그아웃 경로가 누락되어 있습니다.

해결 방안 (A) — 해당 위치에서 직접 처리

} catch (refreshError) {
  console.error('Refresh Token is invalid or expired', refreshError);
  const currentLang = i18n.resolvedLanguage ?? i18n.language;
  const lang = isSupportedLang(currentLang) ? currentLang : DEFAULT_LANG;
  window.location.href = buildLangPath('/home', lang);
  removeAccessToken();
  Sentry.setUser(null); // ← 추가: 세션 만료 시에도 user context 정리
  return Promise.reject(refreshError);
}

해결 방안 (B) — removeAccessToken 유틸리티에 통합

removeAccessToken() 내부에서 Sentry.setUser(null)을 함께 호출하면, 명시적 로그아웃과 세션 만료 두 경로를 한 번에 처리할 수 있어 이후 실수를 방지할 수 있습니다.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분이 빠졌었네요. 제안해 주신 방안들 중 A안으로 간단하게 작업 완료했습니다!

gemini-code-assist[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as outdated.

@useon useon requested a review from jaeml06 April 1, 2026 20:10
Copy link
Copy Markdown
Contributor

@jaeml06 jaeml06 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확인했습니다.!!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore 파일 내부가 아닌 파일 자체에 대한 변경 (디렉토리 이동, 파일 이름 변경, 파일 삭제 등) config 외부 라이브러리 관련 추가 및 설정

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[FEAT] Sentry 도입

2 participants