Skip to Content
Packages@howmuchhome-web/error

@howmuchhome-web/error

에러 클래스, 에러 바운더리, Sentry 전송 파이프라인을 제공하는 패키지입니다. 실제 로그 전송은 @howmuchhome-web/logger를 경유하므로, 로깅 개요는 로깅 가이드를 함께 참고하세요.

진입점

import 경로대상노출
@howmuchhome-web/errorweb (Next.js)모든 심볼
@howmuchhome-web/error/nativenative (Expo)RN-safe 한 서브셋만

native 진입점은 web 전용 transport(@sentry/nextjs, logger 의 Datadog 브라우저 SDK)를 끌어오지 않기 위해 노출을 좁혔습니다. @howmuchhome-web/ui/native 와 같은 패턴입니다.

/native 배럴이 내보내는 것은 다음뿐입니다.

  • ErrorBoundaryErrorBoundary.native.tsx 구현 (web 과 별도 파일)
  • reportError
  • classes (HowmuchhomeError)
  • types (ErrorLevel)

ErrorFallback, withErrorHandler, sentryBaseConfig, sentryBeforeSend 등 web 전용 심볼은 native 배럴에 없습니다.

native 에서도 reportError 는 web 과 동일하게 Logger.captureError 를 호출합니다. metro resolver 가 logger 내부의 @sentry/nextjs@sentry/react-native 로 redirect 하므로, native 에서는 RN Sentry 로 전송됩니다. 자세한 내용은 native 로깅을 참고하세요.

HowmuchhomeError

프로젝트 전역에서 쓰는 커스텀 에러 클래스입니다. Sentry 로 전송될 때 사용할 메타데이터(레벨, transaction, fingerprint, tags, extra)를 함께 담습니다.

import {HowmuchhomeError, ErrorLevel} from '@howmuchhome-web/error'; throw new HowmuchhomeError('결제 검증 실패', { level: ErrorLevel.WARNING, tags: {feature: 'payment'}, extra: {orderId}, });

시그니처

new HowmuchhomeError( input: string | Error, props?: { cause?: unknown; level?: ErrorLevel; // 기본값: ErrorLevel.ERROR transactionName?: string; fingerprint?: string[]; tags?: Record<string, string>; extra?: Record<string, unknown>; }, )
  • inputError 이면 그 message 를 재사용하고, cause 를 명시하지 않으면 원본 에러를 cause 로 연결합니다.
  • 생성 시 setErrorLevel(level) 이 호출됩니다.

프로퍼티

프로퍼티타입설명
levelErrorLevelSentry severity 로 매핑됨
transactionNamestring?Sentry transaction. 전송 시 normalizeErrorPath 로 정규화됨
fingerprintstring[]?Sentry 그룹핑 지문
tagsRecord<string, string>?Sentry tags 에 병합됨
extraRecord<string, unknown>?Sentry extra 에 병합됨

setErrorLevel

error.setErrorLevel(level: ErrorLevel): this

level 을 갱신합니다. ErrorLevel.IGNORE 이면 에러 객체에 silent 마커를 설정하고, 그 외 레벨이면 마커를 제거합니다. 체이닝을 위해 자신을 반환합니다.

ErrorLevel

export const enum ErrorLevel { FATAL = 'fatal', ERROR = 'error', WARNING = 'warning', INFO = 'info', IGNORE = 'ignore', }

const enum 이므로 컴파일 시 문자열 리터럴로 인라인됩니다. native(Babel/metro)에서도 문제없이 사용됩니다.

  • FATAL / ERROR / WARNING / INFO — Sentry severity 로 그대로 매핑됩니다.
  • IGNORE — Sentry 로 전송하지 않습니다(silent 처리).

reportError

에러를 Sentry 로 전송하는 함수입니다. ErrorBoundaryErrorFallback 이 내부적으로 이 함수를 호출합니다.

import {reportError} from '@howmuchhome-web/error'; reportError(error: Error, errorLevel?: ErrorLevel): string | undefined

동작:

  • isSilentError(error) 이거나 errorLevel === ErrorLevel.IGNORE 이면 아무것도 하지 않고 종료합니다.
  • errorHowmuchhomeError 이면 level(인자 우선), transactionName, fingerprint, tags, extra 를 함께 Logger.captureError 로 넘깁니다.
  • 그 외 일반 Error 는 객체에 level 을 얹은 뒤 Logger.captureError(error) 를 호출합니다.
  • 전송한 경우 Logger.captureError 가 반환한 Sentry eventId 를 반환합니다. silent / IGNORE 로 초기 종료하면 undefined 를 반환합니다. 호출부에서는 반환값의 truthy 여부로 판단하면 됩니다(전역 바운더리도 이 패턴).

ErrorBoundary

react-error-boundaryErrorBoundary 를 감싼 컴포넌트입니다. 에러 발생 시 onError 콜백을 실행하고 reportError 로 자동 전송한 뒤, 반환된 eventIdonGenerateEventId 로 전달합니다.

web 과 native 는 파일이 분리되어 있습니다.

파일대상차이
ErrorBoundary.tsxweb정적 fallback: ReactNode
ErrorBoundary.native.tsxnativefallback 에 더해 fallbackRender(재시도) 지원

web Props

type Props = { errorLevel?: ErrorLevel; children: ReactNode; fallback: ReactNode; onError?: (error: Error, info: {componentStack: string}) => void; onGenerateEventId?: (eventId: string) => void; };

native Props

type Props = { errorLevel?: ErrorLevel; children: ReactNode; fallback?: ReactNode; fallbackRender?: (props: {resetErrorBoundary: () => void}) => ReactNode; onError?: (error: Error, info: {componentStack: string}) => void; onGenerateEventId?: (eventId: string) => void; };
  • fallbackRender 가 있으면 fallback 은 무시됩니다.
  • native 에서 재시도가 필요한 화면은 보통 fallbackRender 를 씁니다. resetErrorBoundary 가 자식 트리를 재마운트하므로, web 의 window.location.reload 를 대신합니다. (native 는 expo-updates 를 Hermes release 에서 리로드에 쓸 수 없어 트리 재마운트로 대체합니다.)

앱의 전역 바운더리는 이 컴포넌트를 감싼 HowmuchhomeErrorBoundary(web: apps/app, native: apps/native-app)에서 401 처리, chunk-load 재시도(web), 에러 화면 UI 를 붙여 사용합니다.

ErrorFallback

컴포넌트가 아닌 에러 화면(예: Next.js error.tsx)에서 에러를 신고할 때 쓰는 얇은 컴포넌트입니다. 'use client' 이며, 마운트 시 useEffectreportError 를 한 번 호출하고 children 을 그대로 렌더합니다.

import {ErrorFallback} from '@howmuchhome-web/error'; // app/error.tsx 등 <ErrorFallback error={error} errorLevel={ErrorLevel.ERROR}> {/* 에러 UI */} </ErrorFallback>
프로퍼티타입설명
errorError신고할 에러
errorLevelErrorLevel?reportError 에 전달
childrenReactNode그대로 렌더링됨

native 배럴에는 없습니다.

withErrorHandler

콜백을 감싸, 실행 중 던져진 Error 를 지정 레벨의 HowmuchhomeError 로 다시 던지도록 만드는 헬퍼입니다. 동기/비동기(Promise) 콜백을 모두 지원합니다.

import {withErrorHandler, ErrorLevel} from '@howmuchhome-web/error'; const run = withErrorHandler(async () => { await doSomething(); }, {errorLevel: ErrorLevel.WARNING});
  • 반환값이 thenable 이면 .catch 에서, 동기면 try/catch 에서 래핑합니다.
  • Error 인스턴스만 래핑합니다. 그 외 값(문자열 등)은 원본 그대로 재던집니다.
  • options 기본값은 {errorLevel: ErrorLevel.ERROR} 입니다.

native 배럴에는 없습니다.

Sentry 설정 (web 전용)

Sentry beforeSend 파이프라인과 초기화용 기본 설정을 제공합니다. native 에서는 사용하지 않습니다.

sentryBaseConfig

Sentry.init 에 넘길 기본 옵션 객체입니다. dsn(SENTRY_DSN / NEXT_PUBLIC_SENTRY_DSN), environment(VERCEL_ENV), 그리고 beforeSend 가 설정되어 있습니다. beforeSendsentryBeforeSend 를 호출하면서, 특정 메시지('Store is not initialized.', 'Request has been canceled', 'NEXT_HTTP_ERROR_FALLBACK')를 포함하는 에러를 drop 하는 추가 콜백을 넘깁니다.

logger 의 initSentry(/packages/logger)가 이 설정을 사용합니다.

// packages/logger/sentry.ts (참고) import {sentryBaseConfig} from '@howmuchhome-web/error'; import * as Sentry from '@sentry/nextjs'; export function initSentry() { if (process.env.NEXT_PUBLIC_ENV === 'production') { Sentry.init(sentryBaseConfig); } }

sentryBeforeSend

Sentry 이벤트를 전송 직전에 가공하는 beforeSend 구현입니다.

sentryBeforeSend( event: Sentry.ErrorEvent, hint: Sentry.EventHint, additionalCallback?: (event, hint) => Sentry.ErrorEvent | null | void, ): Sentry.ErrorEvent | null
  • silent 에러(isSilentError)이면 null 을 반환해 전송을 막습니다.
  • HowmuchhomeError 이면 levelevent.level, transactionName → 정규화된 event.transaction, message, fingerprint, 병합된 tags / extra 를 이벤트에 반영합니다.
  • 그 외 에러는 SendbirdErrorWARNING 으로 낮추고, 에러에 level 필드가 있으면 반영하며, event.transaction 을 정규화합니다.
  • additionalCallbackundefined 가 아닌 값을 반환하면 그 값을 최종 결과로 사용합니다(예: null 로 drop).

유틸리티

normalizeErrorPath

경로 문자열에서 쿼리스트링을 제거하고, 숫자/UUID/32자리 hex 세그먼트를 {id} 로 치환합니다. Sentry transaction 그룹핑 노이즈를 줄이기 위해 사용됩니다.

import {normalizeErrorPath} from '@howmuchhome-web/error'; normalizeErrorPath('/users/12345/posts?page=2'); // '/users/{id}/posts'

isSilentError

에러 객체에 silent 마커가 붙어 있는지 판단합니다(내부적으로 __silentError__ own-property 로 표시). ErrorLevel.IGNORE 로 만든 에러가 여기에 해당합니다.

import {isSilentError} from '@howmuchhome-web/error';

toSentryLevel

임의의 값을 Sentry SeverityLevel 로 변환합니다. 매핑 불가하면 undefined, 'warn''warning' 으로 정규화합니다.

@howmuchhome-web/api 와의 관계

@howmuchhome-web/apiApiServerError 는 이 패키지의 HowmuchhomeError상속합니다.

// packages/api/error.ts (참고) import {ErrorLevel, HowmuchhomeError, normalizeErrorPath} from '@howmuchhome-web/error'; export class ApiServerError<...> extends HowmuchhomeError { ... }

즉 API 에러도 HowmuchhomeError 이므로, reportError / sentryBeforeSendHowmuchhomeError 경로를 그대로 탑니다. ApiServerError 는 생성 시 transactionName, fingerprint(경로·메서드·상태코드·에러코드 조합, normalizeErrorPath 적용), extra(detail·requestBody)를 채웁니다.

전역 바운더리에서 401 을 걸러 로그아웃 처리하는 코드가 이 상속 관계를 이용합니다.

if (ApiServerError.isApiServerError(e) && e.statusCode === 401) { // 세션 정리 }

서버 액션 경계에서 ApiServerError 는 클라이언트로 그대로 전달되지 않으므로, serializeForServerAction() / deserializeForServerAction() 를 사용해야 합니다. 자세한 내용은 @howmuchhome-web/api를 참고하세요.

무엇을 언제 쓰나

상황사용
컴포넌트 트리 에러 격리 + 폴백 UIErrorBoundary (web) / ErrorBoundary from /native
Next.js error.tsx 등에서 에러 신고ErrorFallback
도메인 에러를 메타데이터와 함께 던지기new HowmuchhomeError(...)
임의 콜백의 에러를 HowmuchhomeError 로 래핑withErrorHandler
명시적으로 Sentry 전송reportError (또는 직접 Logger.captureError)
Sentry 전송에서 제외ErrorLevel.IGNORE / silent 처리

에러 로깅의 큰 그림과 Datadog/Sentry 사용 규칙은 로깅 가이드, 로그 API 는 @howmuchhome-web/logger를 참고하세요.

Last updated on