[Feature] 전역 에러 처리 및 Sentry 연동#1196
Conversation
- 페이지 로드 및 라우팅 성능 모니터링을 위해 browserTracingIntegration 설정 추가
- 동기 런타임, 비동기에러, API에러, 이벤트 핸들러 에러, 타입 에러 테스트
- Sentry.ErrorBoundary로 최상위 컴포넌트 래핑하여 런타임 에러 포착 - GlobalErrorFallback 컴포넌트 연결 - 개발 환경 전용 에러 테스트 라우트(/error-test) 추가
- Sentry.ErrorBoundary와 Suspense를 결합한 GlobalBoundary 컴포넌트 생성 - App 최상위에 적용하여 전역 에러 핸들링 및 코드 스플리팅/Suspense 로딩 처리 일원화 - App.tsx 내의 불필요한 개별 Suspense 래퍼 제거 (GlobalBoundary로 위임)
- App.tsx: 각 페이지 라우트의 Suspense(fallback=null) 래퍼 제거
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | Summary |
|---|---|
Global Error Handling frontend/src/components/common/ErrorBoundary/GlobalBoundary.tsx, frontend/src/components/common/ErrorBoundary/GlobalErrorFallback.tsx, frontend/src/components/common/ErrorBoundary/GlobalErrorFallback.styles.ts, frontend/src/components/common/ErrorBoundary/index.ts |
Sentry ErrorBoundary를 래핑하는 GlobalBoundary 컴포넌트 추가 및 사용자 친화적 에러 폴백 UI 구현. 개발 모드에서는 에러 상세 정보 표시, 프로덕션에서는 숨김 처리. |
Error Testing Page frontend/src/pages/ErrorTestPage/ErrorTestPage.tsx, frontend/src/pages/ErrorTestPage/ErrorTestPage.styles.ts |
동기/비동기 에러, 이벤트 핸들러 에러, React Query 에러 등 다양한 에러 시나리오를 테스트할 수 있는 개발 전용 페이지 추가. |
App Configuration frontend/src/App.tsx, frontend/src/utils/initSDK.ts |
Suspense 래퍼 제거 및 GlobalBoundary로 전체 앱 감싸기. DEV 플래그로 보호된 /error-test 라우트 추가. React Query 설정에서 queries의 throwOnError를 true로 설정. |
Sequence Diagram(s)
sequenceDiagram
participant User
participant App as App Component
participant GlobalBoundary
participant SentryErrorBoundary as Sentry ErrorBoundary
participant Fallback as GlobalErrorFallback
participant Sentry
User->>App: 애플리케이션 사용
App->>GlobalBoundary: 앱 렌더링
GlobalBoundary->>SentryErrorBoundary: children 감싸기
User->>App: 에러 발생 액션
App-->>SentryErrorBoundary: throw Error
SentryErrorBoundary->>Sentry: 에러 캡처/전송
SentryErrorBoundary->>Fallback: error & resetError 전달
Fallback-->>User: 에러 폴백 UI 표시
User->>Fallback: 다시 시도 / 홈으로 이동 선택
Fallback->>SentryErrorBoundary: resetError() 호출
SentryErrorBoundary->>App: 앱 상태 복구
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
- [feature] 사용자 에러 추적을 위한 Sentry 적용 #404: Sentry 초기화 설정 수정과 본 PR의 전역 에러바운더리 구현이 Sentry 통합의 일부로 연관됨
- [release] FE v1.1.6 릴리즈 #842: App.tsx의 라우팅 및 Suspense 사용을 모두 수정하며, 파일 수준에서 직접 영향을 미치는 변경사항
Suggested reviewers
- oesnuj
- lepitaaar
- suhyun113
🚥 Pre-merge checks | ✅ 4 | ❌ 2
❌ Failed checks (2 warnings)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. | |
| Merge Conflict Detection |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | ✅ Passed | PR 제목 '[Feature] 전역 에러 처리 및 Sentry 연동'은 변경 사항의 주요 내용(전역 에러 처리와 Sentry 통합)을 명확하고 간결하게 요약합니다. |
| Linked Issues check | ✅ Passed | PR은 MOA-646의 모든 주요 요구사항을 충족합니다: GlobalBoundary(Sentry ErrorBoundary 포함) 설정, App.tsx 최상위 적용, TanStack Query throwOnError 설정, 에러 폴백 UI 구현. |
| Out of Scope Changes check | ✅ Passed | 모든 변경 사항이 전역 에러 처리 및 Sentry 통합 범위 내에 있으며, ErrorBoundary 컴포넌트, 에러 UI, 테스트 페이지, Sentry 설정은 모두 정의된 목표와 직접 관련됩니다. |
| 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
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
feature/#1193-error-Boundary-sentry-integration-MOA-646
⚔️ Resolve merge conflicts (beta)
- Auto-commit resolved conflicts to branch
feature/#1193-error-Boundary-sentry-integration-MOA-646 - Create stacked PR with resolved conflicts
- Post resolved changes as copyable diffs in a comment
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.
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
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)
frontend/src/utils/initSDK.ts (1)
44-47:⚠️ Potential issue | 🔴 Critical
import.meta.env.NODE_ENV는 Vite에서 기본적으로 제공되지 않습니다.현재 프로젝트의 Vite 설정(
./frontend/config/vite.config.ts)에는 NODE_ENV 정의가 없으며, Vite는 표준으로MODE,DEV,PROD,SSR만 제공합니다.import.meta.env.NODE_ENV는undefined가 되어 조건이 항상 거짓이 되므로, 개발 환경에서도 Sentry가 초기화됩니다. 프로젝트의 다른 파일들(index.tsx:9,ErrorBoundary.tsx:25)에서는 이미import.meta.env.DEV를 올바르게 사용하고 있습니다.제안된 수정
export function initializeSentry() { - if (import.meta.env.NODE_ENV === 'development') { + if (import.meta.env.DEV) { return; }
🤖 Fix all issues with AI agents
In `@frontend/src/pages/ErrorTestPage/ErrorTestPage.tsx`:
- Around line 111-121: The current throwTypeError handler triggers an error in
an event handler (outside React render), so ErrorBoundary won't catch it;
replace that pattern by adding a shouldThrow boolean state (e.g.,
useState(false)) and a toggle function invoked by Styled.TestButton that sets
shouldThrow=true, then in the component render if (shouldThrow) throw new
TypeError(...) so the error occurs during rendering and is catchable by
ErrorBoundary; update the section title text (Styled.SectionTitle) to accurately
state "ErrorBoundary 캐치" remains correct and keep or set the button $variant
appropriately (e.g., keep 'danger' since it will now throw during render) and
remove or repurpose the existing throwTypeError function.
🧹 Nitpick comments (3)
frontend/src/components/common/ErrorBoundary/GlobalBoundary.tsx (1)
12-18:errorData.error as Error타입 단언이 안전하지 않을 수 있습니다.
throw "string"등Error인스턴스가 아닌 값이 던져지면error.message가undefined로 표시될 수 있습니다. 방어적으로 처리하는 것을 권장합니다.제안된 수정
<Sentry.ErrorBoundary fallback={(errorData) => { + const error = + errorData.error instanceof Error + ? errorData.error + : new Error(String(errorData.error)); return ( <GlobalErrorFallback - error={errorData.error as Error} + error={error} resetError={errorData.resetError} /> ); }} >frontend/src/App.tsx (2)
20-20:ErrorTestPage가 정적 import로 프로덕션 번들에 포함됩니다.개발 환경 전용 페이지이지만
lazy가 아닌 정적 import이므로, 프로덕션 빌드에도 해당 코드가 포함됩니다.lazy를 사용하거나 조건부 동적 import로 변경하면 프로덕션 번들 크기를 줄일 수 있습니다.제안된 수정
-import ErrorTestPage from './pages/ErrorTestPage/ErrorTestPage'; +const ErrorTestPage = lazy( + () => import('./pages/ErrorTestPage/ErrorTestPage'), +);
22-34: 전역throwOnError: true설정이 에러 처리에 미치는 영향 검토 필요현재 일부 쿼리 훅(useApplication, usePromotion, useClub 등)과 뮤테이션에서
onError콜백으로 인라인 에러 처리를 하고 있습니다. 다만 이 패턴이 모든 쿼리에 일관되게 적용되지 않으면, 에러 처리가 없는 쿼리의 실패는 ErrorBoundary로 전파됩니다. 다음 중 하나를 고려해 주세요:
- 모든 쿼리가
onError콜백을 통해 인라인 에러 처리를 하도록 통일- 토스트, 재시도 UI 등이 필요한 쿼리는 개별적으로
throwOnError: false설정- 또는
throwOnError: (error) => error.status >= 500같이 특정 에러만 throw하도록 조건부 처리
lepitaaar
left a comment
There was a problem hiding this comment.
오 개굳;;;;;;;;;; 미쳤는데요? 센트리까지 전파 너무좋습니다. 프론트도 에러 로깅되겠군요
- 에러 발생 시 GlobalErrorFallback 대신 콘텐츠 영역에 에러 메시지와 재시도 버튼 표시 - 헤더/네비게이션을 유지하여 사용자가 쉽게 복구 가능
- 각 페이지에서 에러를 직접 처리하여 레이아웃 유지 및 복구 가능성 향상
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@frontend/src/App.tsx`:
- Line 20: The file currently uses a static import of ErrorTestPage which forces
its code into production bundles; replace the static import with a dynamic/lazy
load: remove the top-level "import ErrorTestPage ..." and instead create a
lazy-loaded component (e.g. React.lazy(() =>
import('./pages/ErrorTestPage/ErrorTestPage'))) and render it only inside the
import.meta.env.DEV guard, wrapping the route in Suspense with a fallback so the
lazy component loads correctly; update any references to ErrorTestPage in the
router/route rendering to use the new lazy component name.
🧹 Nitpick comments (1)
frontend/src/pages/MainPage/MainPage.styles.ts (1)
122-148: box-shadow에 하드코딩된 색상값 사용Line 137의
rgba(255, 84, 20, 0.3)는 하드코딩되어 있지만, 배경색은 테마(theme.colors.primary)를 참조하고 있습니다. 테마의 primary 색상이 변경되면 box-shadow 색상이 불일치하게 됩니다. 가능하다면 테마 색상 기반으로 통일하는 것을 고려해 주세요.
oesnuj
left a comment
There was a problem hiding this comment.
에러 바운더리 추가 고생하셨습니다
바운더리 하위 컴포넌트 단에서도 세세하게 처리해서 더 안정적으로 만들어봅시다
Sentry도 잘 활용해보기
| <Suspense fallback={<Spinner />}>{children}</Suspense> | ||
| </Sentry.ErrorBoundary> |
There was a problem hiding this comment.
Suspense가 GlobalBoundary로 이동하면서 로딩과 에러 처리가 한 곳에서 관리되도록 정리된 것 같네요
App.tsx에서 덕지덕지 관리하던 것보다 책임이 분리되어 좋아 보입니다 ㅎㅎ
(물론 Suspense 트리거가 없긴하지만... 안전빵으로 있는 것도 좋은 듯해요)
There was a problem hiding this comment.
useQuery를 쓰고 있는 페이지에선 내부에서 로딩,에러 상태를 관리하니 suspense 대상이 아니지만
UX를 고려해서 useSuspenseQuery가 필요한 곳이라면 Suspense를 쓸 수는 있을 것 같아요.
안전빵이라도 Suspense보단 에러 추적 목적으로 상위 에러바운더리로 전파하는 방식이 좋을 것 같아요.
| <Styled.PrimaryButton onClick={handleReset}> | ||
| 다시 시도 | ||
| </Styled.PrimaryButton> |
There was a problem hiding this comment.
handleReset이 Sentry에서 전달해주는 resetError이라는 함수군요
ErrorBoundary 아래 subtree만 다시 렌더링해야 해서 다시 시도 버튼이 필요한 구조군요.
별도 새로고침이 아니라 이렇게 처리할수 있네요
배워갑니다
There was a problem hiding this comment.
네 Sentry 에러바운더리 내에서 에러 상태를 초기화하는 방식입니다 !
#️⃣연관된 이슈
📝작업 내용
Sentry를 도입했지만 컴포넌트 내부에서 에러를 처리하고 있었기에 상위에서 에러를 잡지 못 하고 있었습니다.
그래서 전역 에러 바운더리를 도입하여 공통된 에러폴백을 보여주도록 했고, Sentry에 에러가 전파되도록 개선했습니다.
1. 전역 에러 바운더리 구현
/error-test경로에서 동기 에러, 비동기 에러, tanstack에러, 이벤트 핸들러 에러, 타입 에러를 각각 테스트할 수 있습니다.개발 환경에서만 테스트할 수 있도록 했습니다.
2. Tanstack Query 에러 전파 설정
throwOnError: true설정으로 Query 에러를 ErrorBoundary로 전파throwOnError: false로 설정Mutation은 수정/삭제 요청이 대부분이며 이것을 에러바운더리로 넘긴다면, 사용자는 요청이 실패하면 에러페이지를 보게됩니다.
이것보다는 토스트로 간단하게 띄워주는 것이 더 낫다고 판단했습니다.
3. Sentry 성능 모니터링 연동
browserTracingIntegration추가로 페이지 로드, 라우팅, API 성능 추적 가능. 이걸로 LCP측정도 가능합니다.4. Suspense 제거
Suspense는 전역적으로 관리되는 Suspense UI가 있어야 하지만 현재는 각 컴포넌트 내에서 로딩, 에러 상태를 관리합니다.
그리고 새로 추가된 전역 에러 바운더리에서 Suspense로 스피너를 띄웁니다. 기존 App.tsx에 있던 Suspense는 이 두 가지 이유로 필요가 없어서 제거했습니다. (사실 원래도 필요가 없었습니다..)
5. 메인페이지 내부 에러처리
메인페이지에서 에러가 뜨면 전역에러 폴백을 보여주는데, 이 부분이 부자연스러웠습니다. api는 카드 부분인데 헤더, 배너, 푸터,
카테고리 버튼 등이 모두 안 보이면 사용자 경험이 더 안 좋다고 판단했습니다.
그래서 error 발생 시 전역으로 던지지 않고 페이지 내부에서 refetch할 수 있도록 디자인을 변경했습니다.
그리고
throwOnError옵션도 일단 제거했습니다. 추후 전역으로 처리할 에러를 정한 뒤 다시 고려해보겠습니다.추후 할 작업
논의하고 싶은 부분(선택)
🫡 참고사항