Error Handling

Runtime Error Case

  • API ์„œ๋ฒ„๋กœ ์ผ์‹œ์ ์œผ๋กœ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ์Šคํฌ๋ฆฝํŠธ ์—๋Ÿฌ๋กœ ์ธํ•ด ํŽ˜์ด์ง€ ํ‘œ์‹œ๊ฐ€ ์•ˆ๋˜๋Š” ๋ฌธ์ œ ๋ฐœ์ƒ

  • try & catch ๋กœ ๊ฐ์‹ธ๋„ ์•ˆ์ „ํ•˜์ง€ ์•Š๋‹ค.

    • request abort ์‹œ ๋ฐœ์ƒ๋˜๋Š” error๋Š” AbortError , DOMException์ด๋ฏ€๋กœ cause ์†์„ฑ์ด ์—†๋‹ค.

    • DOMException error๋Š” ์ผ๋ฐ˜์ ์ธ ํƒ€์ž… ์—๋Ÿฌ๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์—๋Ÿฌ๊ฐ€ ์ผ์–ด๋‚œ ์†์„ฑ(error.cause)์ด ์—†์–ด ์Šคํฌ๋ฆฝํŠธ ์—๋Ÿฌ๋กœ ์ธํ•ด ํŽ˜์ด์ง€ ํ‘œ์‹œ๊ฐ€ ์•ˆ๋จ

  • JSON.stringify()๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ data๊ฐ€ Serializable ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ (์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์ˆœํ™˜์ฐธ์กฐ๊ฐ€ ๋˜๋Š” ๊ฒฝ์šฐ) ํƒ€์ž…์—๋Ÿฌ ๋ฐœ์ƒ์œผ๋กœ ํŽ˜์ด์ง€ ํ‘œ์‹œ ์•ˆ๋จ

์ œ์–ดํ•  ์ˆ˜ ์—†๋Š” ์—๋Ÿฌ

  • ์—๋Ÿฌ๋Š” ์ƒํ™ฉ์— ๋”ฐ๋ผ ๊ฐ๊ฐ ๋‹ค๋ฅด๊ฒŒ ๋ฐœ์ƒํ•˜๋ฉฐ ๋ชจ๋“  ์ผ€์ด์Šค๋ฅผ ์™„์ „ํžˆ ์ œ์–ดํ•˜๋Š” ๊ฒƒ์€ ์–ด๋ ต๋‹ค.

  • ์ œ์–ดํ• ์ˆ˜ ์—†๋Š” ๋ถ€๋ถ„์—์„œ UI Blocking์„ ๋ฐœ์ƒ์‹œํ‚ค๋Š”๊ฒƒ์ด ๋ฌธ์ œ

  • ์ œ์–ดํ•  ์ˆ˜ ์—†๋Š” ๋ถ€๋ถ„์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ, ํ•ด๋‹น ๋ถ€๋ถ„์„ ๊ฐ์‹ธ ์—๋Ÿฌ๊ฐ€ ์ „ํŒŒ๋˜์–ด ํŽ˜์ด์ง€๊ฐ€ ์ค‘๋‹จ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”

Error Boundary

  • UI ์ผ๋ถ€๋ถ„์˜ ์Šคํฌ๋ฆฝํŠธ ์—๋Ÿฌ๊ฐ€ ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ค‘๋‹จํ•ด์„œ๋Š” ์•ˆ๋œ๋‹ค.

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด๊ฐ€ ์ค‘๋‹จ๋˜๋Š”๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด React v16์—์„œ Error Boundary๋ผ๋Š” ์ƒˆ๋กœ์šด ๊ฐœ๋…์ด ๋„์ž…๋จ

  • React Component tree ํ•˜์œ„ ๋‚ด๋ถ€์— ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ error๋ฅผ catchํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ

  • ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ๋ถ€๋ถ„์— ๋Œ€ํ•ด falback UI๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

ํŠน์ง•

  • ErrorBoundary ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ๋œ ๊ฒฝ์šฐ ๊ฐ€์žฅ ์ธ์ ‘ํ•œ ์ƒ์œ„ ErrorBoundary์— ์ œ๊ณต๋œ Fallback UI๋ฅผ ๋ Œ๋”๋ง

  • ErrorBoundary๋Š” componentDidCatch ๋ผ์ดํ”„์‚ฌ์ดํด ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.

    • ํ•ด๋‹น ๋ฉ”์†Œ๋“œ๋Š” Class Component์—์„œ๋งŒ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ErrorBoundary ์ปดํฌ๋„ŒํŠธ๋Š” Class๋กœ ๋งŒ๋“ค์–ด์ง„๋‹ค.

  • ErrorBoundary๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ ์˜ค๋ฅ˜์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ์‚ฌ์šฉ์ž์—๊ฒŒ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋Š” Static getDerivedStateFromError, componentDidCatch method ์ด ๊ตฌํ˜„๋˜์–ด์•ผ ํ•œ๋‹ค.

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state =  { hasError: false }
  }
  
  // ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ๋œ ๊ฒฝ์šฐ ํ˜ธ์ถœ๋จ
  static getDerivedStateFromError(error) {
    // fallback UI๋ฅผ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด ์—๋Ÿฌ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธ 
    return { hasError: true }
  }
  // ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ๋œ ๊ฒฝ์šฐ ์‹คํ–‰๋˜๋Š” ๋ผ์ดํ”„์‚ฌ์ดํด ๋ฉ”์†Œ๋“œ
  componentDidCatch(error, info) {
    /*
     * Example: componentStack
     *   in ComponentThatThrow (created by App)
     *.  in ErrorBoundray (created by App)
     *.  in div
     *.  in App
    */
     logErrorToMyService(error, info.componentStack);
  }
  
  if (this.state.hasError){
    // render fallback UI 
    return this.props.fallback
  }
  
  return this.props.children
}
  • getDerivedStateFromError()

    • error๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๊ฐฑ์‹ ๋  state๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

    • render step์—์„œ ํ˜ธ์ถœ (VDOM์ด ์ƒ์„ฑ๋˜๊ณ  ์ด์ „ VDOM๊ณผ ๋น„๊ตํ•˜๋Š” ๋‹จ๊ณ„์—์„œ ํ˜ธ์ถœ๋จ)

    • VDOM ๊ตฌ์„ฑ์ด ๊นจ์ง€๋ฉด ์•ˆ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋‚ด๋ถ€์—์„œ ์‚ฌ์ด๋“œ์ดํŽ™ํŠธ๊ฐ€ ๋ฐœ์ƒ ๊ฐ€๋Šฅํ•œ ์ž‘์—…์„ ํ•ด์„  ์•ˆ๋จ

    • fallback UI๋ฅผ ๋ Œ๋”๋งํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ์ž‘์—…์œผ๋กœ์„œ ์—๋Ÿฌ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์ฝ”๋“œ๋งŒ ์ž‘์„ฑ

  • componentDidCatch()

    • commit step์—์„œ ํ˜ธ์ถœ (VDOM ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์‹ค์ œ DOM์— ์ ์šฉํ•˜๋Š” ๋‹จ๊ณ„์—์„œ ํ˜ธ์ถœ๋จ)

    • ์‚ฌ์ด๋“œ์ดํŽ™ํŠธ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ๋ฌด๊ด€

    • ์—๋Ÿฌ ์ •๋ณด๋ฅผ ๋‚จ๊ธธ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. (์—๋Ÿฌ ๋กœ๊น…)

    • error -> ์—๋Ÿฌ์— ๋Œ€ํ•œ ์ •๋ณด

    • info -> ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œ์ผฐ๋Š”์ง€์— ๋Œ€ํ•œ ์ •๋ณด (componentStack)

ErrorBoundary๊ฐ€ ๋‹ค๋ฃจ์ง€ ์•Š๋Š” Error

  • Async ๋™์ž‘๋“ค (Event Handler, setTimeout, rAF)

    • ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ ‰์…˜ ๋˜๋Š” ํŠน์ • ์ด๋ฒคํŠธ๋กœ ๋ Œ๋”๋œ ์ดํ›„์— ๋ฐœ์ƒ๋˜๊ธฐ ๋•Œ๋ฌธ์— ErrorBoundary๊ฐ€ catchํ•˜์ง€ ๋ชปํ•œ๋‹ค.

    • ์ฆ‰, ๋ Œ๋”๋ง ๋„์ค‘์— ์ผ์–ด๋‚œ Error๋งŒ catch ๊ฐ€๋Šฅ

    • unhandledrejection ์ด๋ฒคํŠธ๋ฅผ ํ†ตํ•ด ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์˜ ๋น„๋™๊ธฐ ์—๋Ÿฌ๋ฅผ ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ์žก์•„๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

      • promise.reject๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ด๋ฒคํŠธ ๋ฐœ์ƒ๋จ

  • ServerSide Rendering

  • ErrorBoundary ๋‚ด๋ถ€ ์—๋Ÿฌ

  • try-catch๋กœ ์ฒ˜๋ฆฌ๋œ ์—๋Ÿฌ

unhandledrejection event

  useEffect(() => {
    window.addEventListener('unhandledrejection', callback);
    return () => {
      window.removeEventListener('unhandledrejection', callback);
    }
  }, []);

react-Error-Boundary

์ง์ ‘ ErrorBoundary๋ฅผ ์ œ์ž‘ํ•˜์ง€ ์•Š๊ณ  ์ƒ์šฉํ™”๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉ

"use client"

import { ErrorBoundary } from 'react-error-boundray';

<ErrorBoundray fallback={<div>Error ๋ฐœ์ƒ< /div>}>
  <Component>
</ErrorBoundray>

  • fallbackRender() -> props์œผ๋กœ ์—๋Ÿฌ์— ๋”ฐ๋ฅธ fallback UI ๊ตฌ์„ฑ ๊ฐ€๋Šฅ

const fallbackRender = ({ error, resetErrorBoundary }) => {
  // resetErrorBoundary()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์—๋Ÿฌ๋ฐ”์šด๋”๋ฆฌ ์ƒํƒœ๋ฅผ ์ดˆ๊ธฐํ™”์‹œํ‚ค๊ณ , ๋ฆฌ๋ Œ๋”๋ง
  return (
    <div role='alert'>
      <p>{error.message}</p> 
    </div>
  )
}

<ErrorBoundray 
  fallbackRender={fallbackRender}
  onReset={(details) => {
    // ์—๋Ÿฌ๊ฐ€ ๋‹ค์‹œ ๋ฐœ์ƒ๋˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด ์ƒํƒœ๋ฅผ ์ดˆ๊ธฐํ™”์‹œํ‚ค๋Š” ์ฝ”๋“œ ์ž‘์„ฑ
  }}>
  <Component>
</ErrorBoundray>
  • useErrorBoundary() hook -

    • ๋น„๋™๊ธฐ ์ฝ”๋“œ ์‹คํ–‰ ์ดํ›„์˜ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๋ฅผ ๋Œ€์‘ํ•˜๊ธฐ ์œ„ํ•œ showBoundary() ๋ฅผ ์ œ๊ณต

    • fallback ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐœ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•œ resetBoundary()๋ฅผ ์ œ๊ณต

    • withErrorBoundary() HOC ์ปดํฌ๋„ŒํŠธ ์ œ๊ณต

import { useErrorBoundary } from 'react-error-boundary'
//...
const { showBoundray } = useErrorBoundary();

useEffect(() => {
  somethingFetch().then().catch(error => showBoundary(error));
}, [])
import { useErrorBoundary } from 'react-error-boundary'

const ErrorFallback = ({ error }) => {
  const { resetBoundary } = useErrorBoundary();
  
  return (
    <div role='alert'>
      <p>{error.message}</p> 
      <button type="button" onClick={resetBoundary}>Try Again</button>
    </div>
  )
}
import { withErrorBoundary } from 'react-error-boundary'

const ComponentWithErrorBoundary = withErrorBoundary(Component, {
  fallback: <div>Error occur</div>,
  onError: (error, info) {}
})

Sentry

์ฝ”๋“œ ๊ด€๋ จ ๋ฌธ์ œ๋ฅผ ์‹๋ณ„ํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋Š”๋ฐ ๋„์›€์„ ์ฃผ๋Š” ์†Œํ”„ํŠธ์›จ์–ด ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ

  • ๋Ÿฐํƒ€์ž„ ๋‹จ๊ณ„์˜ ์—๋Ÿฌ๋ฅผ ์ˆ˜์ง‘

  • ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ํ™˜๊ฒฝ์— ๋กœ๊ทธ๋ฅผ ์ง์ ‘ ํ™•์ธํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ณ  ์—๋Ÿฌํ™˜๊ฒฝ์˜ ์žฌํ˜„์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์—๋Ÿฌ๋ฅผ ์ˆ˜์ง‘ํ•˜๋Š”๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค.

  • ํด๋ผ์ด์–ธํŠธ ์—๋Ÿฌ๋ฅผ ์ˆ˜์ง‘ํ•˜์—ฌ ์Šฌ๋ž™์„ ์—ฐ๋™ํ•˜์—ฌ ์•Œ๋ฆผ ์ œ๊ณต

  • ์†Œ์Šค์ฝ”๋“œ ์ปจํ…์ŠคํŠธ์™€ ๋”๋ถˆ์–ด Stack trace ์ œ๊ณต

  • Commit ์ •๋ณด ๋ฐ Stack trace ๊ธฐ๋ฐ˜์œผ๋กœ ์˜์‹ฌ์Šค๋Ÿฌ์šด commit ๋ฐ ํ• ๋‹น์ž ์ œ์•ˆ

Sentry ๊ธฐ๋Šฅ

Stack trace

  • ์ œ๊ณต๋œ SourceMap ๊ธฐ๋ฐ˜ Stack trece๋กœ ์—๋Ÿฌ ํ™˜๊ฒฝ ํŒŒ์•… ๊ฐ€๋Šฅ

  • ์—ฌ๋Ÿฌ ์—๋Ÿฌ๋ฅผ ๊ทธ๋ฃนํ™”ํ•˜์—ฌ ํ•˜๋‚˜์˜ ์ด์Šˆ๋กœ ์ •์˜

  • ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ ์ฝ”๋“œ๊ฐ€ ์–ด๋””์—์„œ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ ์‹œ์ผฐ๋Š”์ง€ ํ™•์ธ ๊ฐ€๋Šฅ

  • Error Event ๋ฐœ์ƒ๊นŒ์ง€์˜ ๊ธฐ๋ก๊ณผ ํƒ€์ž„๋ผ์ธ ์ œ๊ณต

  • HTTP Request, Console.log, Server log, DOM Event ๋“ฑ์ด ํฌํ•จ๋œ๋‹ค.

  • ์–ด๋–ค ๋™์ž‘๋“ค๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ์ด์œ ๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ํŒŒ์•…ํ•˜๊ณ  ํ•ด๋‹น ์ปจํ…์Šค๋ฅผ ์ดํ•ดํ•จ์œผ๋กœ์จ ์—๋Ÿฌ๋ฅผ ์žฌํ˜„ํ•˜๊ธฐ ์šฉ์ดํ•ด์ง„๋‹ค.

Session Replay

  • ์‚ฌ์šฉ์ž ์„ธ์…˜์„ ๋น„๋””์˜ค์ฒ˜๋Ÿผ ์žฌํ˜„

  • ์˜ค๋ฅ˜/์„ฑ๋Šฅ ๋ฌธ์ œ ๋ฐœ์ƒ ์ „/ํ›„ ์ƒํ™ฉ ํŒŒ์•…

  • ๋ฌธ์ œ์˜ ๋Œ€ํ•œ ์‹ฌ์ธต์ ์ธ ๋””๋ฒ„๊น…, ๋ฌธ์ œ๋ฅผ ์žฌํ˜„ํ•˜๊ณ  ๋” ๋น ๋ฅด๊ฒŒ ํ•ด๊ฒฐ ๊ฐ€๋Šฅ

  • UI ์ด์Šˆ์˜ ๊ฒฝ์šฐ ์ œ๊ณต

๋…นํ™” ์‹œ์ž‘ ํŠธ๋ฆฌ๊ฑฐ

  • replay.start() ์ˆ˜๋™ ํ˜ธ์ถœ

  • ์„ธ์…˜์ด replaySessionSampleRate์— ์˜ํ•ด ๋…นํ™”์—ฌ๋ถ€ ๊ฒฐ์ •

๋…นํ™” ์ข…๋ฃŒ ํŠธ๋ฆฌ๊ฑฐ

  • replay.stop() ์ˆ˜๋™ ํ˜ธ์ถœ

  • ์‚ฌ์šฉ์ž ๋น„ํ™œ์„ฑ ์ƒํƒœ, ์ตœ๋Œ€ ์‹œ๊ฐ„์— ๋„๋‹ฌ (๊ธฐ๋ณธ 60๋ถ„)

Alert

  • ์ฝ”๋“œ์˜ ๋ฌธ์ œ/ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์— ๋Œ€ํ•œ ์‹ค์‹œ๊ฐ„์ ์ธ ๊ฐ€์‹œ์„ฑ ์ œ๊ณต

  • ์ง€์ •ํ•œ ๊ทœ์น™์— ์˜ํ•ด ๋ฐœ์ƒ๋˜๋Š” alert

  • alert ๋ชฉ๋ก ํ•„ํ„ฐ๋ง, alert์— ๋Œ€ํ•œ ๋น„ํ™œ์„ฑํ™”/mute ๊ธฐ๋Šฅ, notification ์ œ๊ณต

์„ค์น˜

npx @sentry/wizard@latest -i nextjs

๋ถ€๊ฐ€ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•

Issue ํŒจ๋„

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์˜ค๋ฅ˜, ์„ฑ๋Šฅ๋ฌธ์ œ์— ๋Œ€ํ•œ ์ •๋ณด ํ‘œ์‹œ

  • ํ™˜๊ฒฝ์— ๋”ฐ๋ฅธ ํ•„ํ„ฐ๋ง ๊ฐ€๋Šฅ

  • ํ•˜๋‚˜์˜ ์ด์Šˆ๋Š” ์œ ์‚ฌํ•œ ์›์ธ์œผ๋กœ ๊ทธ๋ฃนํ™”๋œ ์—๋Ÿฌ๋“ค

  • ์ด์Šˆ์˜ ๋ฐœ์ƒ ๋นˆ๋„, ์ถ”์ด, ์‚ฌ์šฉ์ž๋ณ„ ์˜ํ–ฅ๋„ ํŒŒ์•… ๊ฐ€๋Šฅ

์ผ๋ฐ˜์ ์ธ ์•ฑ์€ ๋Œ€๋Ÿ‰์˜ ์ด๋ฒคํŠธ๋“ค์„ ์„ผํŠธ๋ฆฌ์— ์ „์†กํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ์ˆ˜ ๋งŽ์€ ์—๋Ÿฌ๋“ค์ด ์ „์†ก๋˜๋ฉด์„œ ์ด๋Ÿฐ ์—๋Ÿฌ๋“ค์„ ํ•„ํ„ฐ๋ง ๋ฐ ๊ทธ๋ฃนํ™”ํ•ด์„œ ๋ณด์—ฌ์ฃผ์ง€ ์•Š๊ฒŒ๋˜๋ฉด ์ฐพ๊ณ ํ•˜์ž๋Š” ๋ฉ”์ด์ €ํ•œ ์ด์Šˆ ๋ฐ ๋ณด๊ณ  ์žˆ๋˜ ์ด์Šˆ๋“ค์˜ ํŠธ๋ž˜ํ”ฝ, ๋นˆ๋„, ์ƒํ™ฉ ์ถ”์ด ๋“ฑ์˜ ์ •๋ณด๋“ค์„ ํ”ฝํ•ด์„œ ๋ณด๊ธฐ ์‰ฝ์ง€ ์•Š๊ธฐ์— ์„ผํŠธ๋ฆฌ๋Š” ์ด๋Ÿฐ ๋Œ€๋Ÿ‰ ์ด๋ฒคํŠธ๋“ค์„ ๊ทธ๋ฃนํ•‘ํ•ด์„œ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค.

Issue ์ƒํƒœ

  • New: ์ƒ์„ฑํ•œ์ง€ 7์ผ ์ด๋‚ด์ธ ์ด์Šˆ

  • Ongoing: 7์ผ ์ด์ƒ ๋˜์—ˆ๊ฑฐ๋‚˜, ์ˆ˜๋™์œผ๋กœ reviewed ์ฒ˜๋ฆฌ๋œ ์ด์Šˆ

  • Escalating: ์˜ค๋ž˜๋œ ์ด์Šˆ ์ค‘ ๋นˆ๋„๊ฐ€ ๊ธ‰๊ฒฉํžˆ ๋Š˜์–ด๋‚œ ์ด์Šˆ

  • Regressed: resolved ๋˜์—ˆ๋‹ค๊ฐ€ ๋‹ค์‹œ ๋‚˜ํƒ€๋‚œ ์ด์Šˆ

  • Archived: ๋ณด๊ด€์ฒ˜๋ฆฌ๋œ ์ด์Šˆ

  • Resolved: ํ•ด๊ฒฐ ์ฒ˜๋ฆฌ๋œ ์ด์Šˆ

Issue ํƒญ

  • Unresolved: ๊ฒ€ํ† ๊ฐ€ ํ•„์š”ํ•œ, ์•„์ง ํ•ด๊ฒฐ๋˜์ง€ ์•Š์€ ๋ชจ๋“  ์ด์Šˆ ์ด์ง‘ํ•ฉ

  • For Review: ๊ฒ€ํ†  ๋ชฉ๋ก, ๊ฒ€ํ† ๊ฐ€ ํ•„์š”ํ•œ ์ด์Šˆ, ํ•ด๊ฒฐ๋˜์ง€ ์•Š์€ ์ด์Šˆ๋“ค์˜ ํ•˜์œ„ ์ง‘ํ•ฉ

  • Regressed: ํ•ด๊ฒฐ๋œ ์ด์Šˆ, ํ•ด๊ฒฐ๋œ์ด์Šˆ๊ฐ€ ๋‹ค์‹œ ๋ฐœ์ƒํ•œ ์ด์Šˆ

  • Escalating: ์ด์ „์— archived ๋ฌ๋˜ ์ด์Šˆ ์ค‘ ์˜ˆ์ƒ๋˜๋Š” ์ด์Šˆ ๋นˆ๋„๊ฐ€ ์ดˆ๊ณผ๋œ ์ด์Šˆ

  • Archived: ํ•ด๊ฒฐ ์ฒ˜๋ฆฌ๋œ ์ด์Šˆ๋“ค

Issue ํ•„ํ„ฐ

  • By Project

  • By Environment

  • By Time

  • Custom filter

Issue ์ƒ์„ธ ํŒจ๋„

  • ์ด์Šˆ ์ถœ์ฒ˜

  • ์˜ํ–ฅ ๋ฒ”์œ„

  • ๊ทธ๋ฃนํ™”๋œ ์ด์Šˆ์— ๋Œ€ํ•œ ์š”์•ฝ

  • 24์‹œ๊ฐ„/30์ผ๊ฐ„์˜ ๋นˆ๋„

  • ๊ด€๋ จ ํƒœ๊ทธ ์ •๋ณด

  • Stack Trace

  • Breadcrumbs

Session Replay ์„ค์ •

  • Sentry.init() ๋‚ด์— ๊ธฐ๋กํ•˜๋Š” ์„ค์ • ๊ฐ’

  • ์ž๋™ ์„ค์ •์‹œ, sentry.client.config.ts/js ์— ์ •์˜

import * as Sentry from '@sentry/nextjs';

Sentry.init({
  // ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ๋…นํ™”๋˜๋Š” ๋ฆฌํ”Œ๋ ˆ์ด์˜ ์ƒ˜ํ”Œ๋ง ๋น„์œจ
  // ์ด ์œ ํ˜•์˜ ๋ฆฌํ”Œ๋ ˆ์ด๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์ „์˜ ์ด๋ฒคํŠธ๋ฅผ ์ตœ๋Œ€ 1๋ถ„๊นŒ์ง€ ๋…นํ™”
  // or ์„ธ์…˜์ด ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ณ„์† ๋…นํ™”ํ•จ
  // 1.0์€ ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ๋ชจ๋“  ์„ธ์…˜์„ ์บก์ฒ˜ํ•˜๊ณ  0์€ ์•ˆํ•จ (0 ~ 1.0)
  replayOnErrorSampleRate: 1.0,
  // ์‚ฌ์šฉ์ž ์„ธ์…˜ ์ „์ฒด์— ๊ฑธ์ณ ์ง€์†๋˜๋Š” ๋ฆฌํ”Œ๋ ˆ์ด์˜ ์ƒ˜ํ”Œ๋ง ๋น„์œจ
  // 1.0์€ ๋ชจ๋“  ๋ฆฌํ”Œ๋ ˆ์ด๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  0์€ ์•ˆํ•จ (0 ~ 1.0)
  replaySessionSampleRate: 0.1,
  
  intergrations: [
    new Setnry.Replay({
      // ํŽ˜์ด์ง€ ๋กœ๋”ฉ ์ „๋ฐ˜์— ๊ฑธ์ณ ์‚ฌ์šฉ์ž๋ฅผ ์ถ”์ 
      // ํƒญ๋‹น ์„ธ์…˜ ์œ ์ง€, ์—ฌ๋Ÿฌ ํƒญ์„ ์‚ฌ์šฉํ•˜๋Š” ํ•œ ๋ช…์˜ ์‚ฌ์šฉ์ž๋Š” ์—ฌ๋Ÿฌ ์„ธ์…˜์œผ๋กœ ๊ธฐ๋ก
      // ๊ธฐ๋ณธ๊ฐ’: true
      stickySession: true,
      // SDK๊ฐ€ setnry๋กœ ์ „์†ก์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „๊นŒ์ง€์˜ ๋ฆฌํ”Œ๋ ˆ์ด ๊ธธ์ด(ms)
      // max: 15000, default: 5000,
      minReplayDuration: 5000,
      // ๋ชจ๋“  ํ…์ŠคํŠธ ์ปจํ…์ธ ๋ฅผ ๋งˆ์Šคํ‚น์ฒ˜๋ฆฌ
      maskAllText: true,
      // media element blocking
      blockAllMedia: true,
    })
  ]
})

Sampling Rate ์„ค์ •ํ•˜๋Š” ์ด์œ 

  • ๋งค์šฐ ๋น„์Œˆ

  • quota์— ๋Œ€ํ•œ ์ดํ•ด์— ๊ธฐ๋ฐ˜

    • -> ์‚ฌ์šฉ๋Ÿ‰์— ๋”ฐ๋ฅธ ๊ณผ๊ธˆ ๊ตฌ์กฐ

  • ์ผ๋ฐ˜ sentry error ๋ณด๋‹ค 10๋ฐฐ ์ด์ƒ์˜ ๊ฐ€๊ฒฉ

    • ๊ผญ ํ•„์š”ํ•œ ๋ถ€๋ถ„์—์„œ๋งŒ ๋„์ž… or ์ƒ˜ํ”Œ๋ง ๋น„์œจ ์กฐ์ •

Session Replay ํŒจ๋„

  • ํƒ€์ž„๋ผ์ธ

  • ํ”Œ๋ ˆ์ด์–ด

  • Breadcrumbs

  • ๊ธฐํƒ€ ์ปดํฌ๋„ŒํŠธ ํŒจ๋„๋กœ ๊ตฌ๋ถ„

  • ํŽ˜์ด์ง€ ์•ก์…˜

    • ๋ฆฌํ”Œ๋ ˆ์ด ๊ณต์œ 

    • ์‚ญ์ œ

Last updated