[React] Error Boundary로 Sentry에 에러 보내기

영근·2024년 1월 11일
0

React

목록 보기
1/1
post-thumbnail
post-custom-banner

Sentry & Error Boundary(React)

2023.12.20
GeunYeong Kim

Sentry?

어플리케이션 에러 트래킹, 성능 모니터링을 제공해주는 서비스

  • 실 사용자에게서 에러가 발생했는데 재연할 수 없는 경우, 해결할 방법이 없다
  • PRD 배포 후 뭔가.. 뭔가가 잘못될까봐 불안해서 계속 모니터링 하게 된다

-> 자사 다른 서비스에서 이미 사용중이었기 때문에 현재 서비스에도 도입해보기로 결정했다.


Settings

  • 설치 마법사
npx @sentry/wizard@latest -i nextjs
  • 하라는대로 하면 설치가 완료된다.
    • sentry.client.config.ts 생성
    • sentry.edge.config.ts 생성
    • sentry.server.config.ts 생성
    • _error.tsx 생성
    • next.config.js 수정
  • sdn키가 파일에 그대로 들어가니 얘만 env로 빼준다.
import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_KEY || undefined, // env로

  tracesSampleRate: 1,

  debug: false,

  replaysOnErrorSampleRate: 1.0,

  replaysSessionSampleRate: 0.1,


  integrations: [
    new Sentry.Replay({
      maskAllText: true,
      blockAllMedia: true,
    }),
  ], // replay 관련 세팅. 나는 replay 적극 활용중이라 모두 true
});

Trouble

  • AxiosError는 잘 트래킹 되지만, 다른 자바스크립트 에러는 트래킹되지 못하고 있었다.
    • 기존에 서버 에러만 핸들링하고 있었기 때문이다..🫠!
    • 클라이언트 에러는 React에서 지원하는 Error Boundary를 추가해 사용했다.
    • Configuring: Error Handling

Error Boundary

  • 부분적으로 JS 클라이언트 에러가 나면 다른 어플리케이션 전체가 동작하지 않는 문제가 있어 React16 부터 도입된 특별한 컴포넌트( - Component)
  • 에러 발생시 react는 모든 UI 요소를 지워버리기 때문에, 클라이언트 에러가 발생하면 사용자는 흰 화면만을 보게 된다.

    By default, if your application throws an error during rendering, React will remove its UI from the screen. To prevent this, you can wrap a part of your UI into an error boundary. An error boundary is a special component that lets you display some fallback UI instead of the part that crashed—for example, an error message.

  • 따라서 TypeError 같은 클라이언트 에러를 Sentry에 로깅하고, 에러가 나도 앱의 다른 부분은 사용할 수 있도록 조치하기 위해 도입했다.
  • Next.js에서 Error Boundary를 사용하기 위해서는 class 컴포넌트를 사용하고, _app.tsx 파일에서 Component를 감싸야 한다.

ErrorBoundary.tsx

/* eslint-disable react/destructuring-assignment */
import React, { Component, ErrorInfo, ReactNode } from 'react';

interface Props {
  children?: ReactNode;
}

interface State {
  hasError: boolean;
}

// Next.js에서 사용해야 하기 때문에 class 컴포넌트로 만들었다.
class ErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);

    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error: State) {
    return { hasError: true };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    // 여기서 에러를 catch 한 뒤 Sentry로 보낸다.
    if (process.env.NODE_ENV === 'production') {
      Sentry.withScope((scope) => {
        scope.setExtra('componentStack', errorInfo);
        Sentry.captureException(error);
      });
    }
  }

  render() {
    if (this.state.hasError) {
      return (
             // 에러 시 노출할 UI
      );
    }

    // 에러가 없으면 children을 return 한다
    return this.props.children;
  }
}

export default ErrorBoundary;

  • getDerivedStateFromError, componentDidCatch
    • 렌더링 중 child component에서 에러가 발생하면 call

App.tsx

  • Component를 감싸준다.
...
   <ErrorBoundary>
      <Component {...pageProps} />
      {mount && <SnackbarBasic />}
      {mount && <ModalBasic />}
      <ReactQueryDevtools initialIsOpen={false} />
  </ErrorBoundary>

결과

  • 에러가 발생하면 fallback UI가 노출된다.
  • 에러가 Sentry에 로깅된다.

+ Sentry.ErrorBoundary 사용

React Error Boundary for React
링크를 참조하면 쉽게 구현이 가능하다.


추가) Bundle Size

  • Sentry 사용을 위해 만든 sentry.client.config.ts 파일의 용량이 다른 모듈에 비해 과도하게 컸다.

해결

  • 사용하지 않는 옵션 off
 // Upload a larger set of source maps for prettier stack traces (increases build time)
 widenClientFileUpload: false,

 // Transpiles SDK to be compatible with IE11 (increases bundle size)
 transpileClientSDK: false,

옵션 두 개를 off하니 용량이 많이 줄었다.

이전 267.45KB -> 변경 후 192.52 KB

config.plugins.push(
      new webpack.DefinePlugin({
        __SENTRY_DEBUG__: false, // 디버깅
        __SENTRY_TRACING__: false, // 퍼포먼스 모니터링
        __RRWEB_EXCLUDE_IFRAME__: true, // 리플레이
        __RRWEB_EXCLUDE_SHADOW_DOM__: true, // 리플레이
        __SENTRY_EXCLUDE_REPLAY_WORKER__: true, // 리플레이
      }),
    );

변경 전 192.52KB -> 변경 후 145.61KB


Reference

profile
Undefined JS developer
post-custom-banner

0개의 댓글