로깅
얼마집 프론트엔드는 Datadog(이벤트/RUM)과 Sentry(에러 트래킹) 두 가지 모니터링 도구를 사용합니다.
핵심 원칙
앱에서는
@datadog/browser-rum,@datadog/browser-logs,@sentry/nextjs패키지를 직접 호출하지 말고, 반드시logger패키지를 통해 로깅하세요.
logger 패키지는 두 도구의 초기화·사용자 컨텍스트 설정·호출 방식을 통일된 인터페이스로 추상화합니다.
직접 호출 시 초기화 순서 문제, 사용자 컨텍스트 누락, 서버/클라이언트 환경 혼용 등의 문제가 발생할 수 있습니다.
초기화
Sentry — initSentry
sentry.client.config.ts, sentry.server.config.ts, sentry.edge.config.ts에서 호출합니다.
이 파일들은 Next.js/Sentry가 앱 부트스트랩 전에 자동으로 로드하는 약속된 파일입니다.
// sentry.client.config.ts / sentry.server.config.ts / sentry.edge.config.ts
import {initSentry} from '@howmuchhome-web/logger/sentry';
initSentry();initSentry는 NEXT_PUBLIC_ENV === 'production'일 때만 실제로 초기화합니다.
initSentry는 브라우저 전용 패키지를 포함하지 않도록 서브패스(/sentry)로 분리되어 있습니다. 메인 패키지(@howmuchhome-web/logger)가 아닌 서브패스에서 import해야 합니다.
Datadog RUM + 사용자 컨텍스트 — LoggerProvider
앱 루트에 LoggerProvider를 마운트합니다. 내부적으로 다음을 모두 처리합니다.
- Datadog RUM 초기화 (
datadogRum.init) - Datadog RUM 사용자 컨텍스트 설정 (
datadogRum.setUser) - Sentry 사용자 컨텍스트 설정 (
Sentry.setUser) - OpenTelemetry span 속성 기록 (서버)
// app/layout.tsx (Server Component)
import {LoggerProvider} from '@howmuchhome-web/logger';
export default async function RootLayout({children}) {
const user = await getCurrentUser();
return (
<html>
<body>
<LoggerProvider user={user}>
{children}
</LoggerProvider>
</body>
</html>
);
}user가 null이면 anonymous-{timestamp}-{random} 형식의 익명 ID가 자동으로 생성됩니다.
서비스 이름을 변경해야 하는 경우 service prop을 전달합니다 (기본값: 'howmuchhome-web').
<LoggerProvider user={user} service="howmuchhome-web-admin">
{children}
</LoggerProvider>로그 기록 — Logger
일반 로그 (Datadog)
클라이언트 사이드에서 커스텀 로그를 Datadog Browser Logs로 전송합니다.
import Logger from '@howmuchhome-web/logger';
// 기본 로그 (type 기본값: 'info')
Logger.log('사용자가 매물 상세 페이지를 조회했습니다.');
// 로그 레벨 및 컨텍스트 지정
Logger.log('검색 결과가 없습니다.', {
type: 'warn',
context: { keyword: '강남구', resultCount: 0 },
});type 값 | 설명 |
|---|---|
'ok' | 정상 완료 |
'info' | 일반 정보 (기본값) |
'warn' | 경고 |
'error' | 에러 |
'debug' | 디버그 |
사용자 행동 추적 (Datadog RUM)
Datadog RUM에 커스텀 액션을 기록합니다.
// 버튼 클릭, 폼 제출 등 사용자 인터랙션 추적
Logger.addAction('매물_찜하기', { propertyId: '123', source: 'detail_page' });
Logger.addAction('검색_실행', { keyword: '강남구', filters: { minPrice: 5000 } });뷰 전환 추적 (Datadog RUM)
SPA에서 URL 변경과 무관하게 논리적 뷰 전환을 추적할 때 사용합니다.
Logger.startView('매물_상세');
Logger.startView('청약_신청_퍼널');에러 캡처 (Sentry)
에러를 Sentry로 전송합니다. 서버/클라이언트 양쪽에서 모두 사용할 수 있습니다.
import Logger from '@howmuchhome-web/logger';
import {ErrorLevel} from '@howmuchhome-web/error';
try {
await submitApplication(data);
} catch (error) {
Logger.captureError(error as Error, {
transactionName: '청약_신청',
level: ErrorLevel.ERROR,
tags: { feature: 'application', step: 'submit' },
extra: { applicationId: data.id },
});
}level 옵션 (ErrorLevel)
| 값 | 설명 |
|---|---|
ErrorLevel.FATAL | 치명적 오류 |
ErrorLevel.ERROR | 일반 오류 (기본값) |
ErrorLevel.WARNING | 경고 수준 |
ErrorLevel.INFO | 정보성 이벤트 |
ErrorLevel.IGNORE | Sentry 전송 생략 |
ErrorLevel.IGNORE를 지정하면 Sentry에 전송되지 않습니다. 예상 가능한 에러(사용자 취소 등)에 활용하세요.
fingerprint 옵션
동일한 에러를 하나의 Sentry 이슈로 그룹핑할 때 사용합니다.
Logger.captureError(error, {
fingerprint: ['payment-timeout'],
});API 에러 처리와의 관계
API 요청 에러는 @howmuchhome-web/api 패키지의 fetcher가 자동으로 Sentry에 전송합니다.
@SilentErrorCodeList 데코레이터로 특정 에러 코드를 전송에서 제외할 수 있습니다.
별도로 Logger.captureError()를 호출할 필요가 없습니다.
직접 Logger.captureError()를 사용하는 경우:
- API 이외의 비즈니스 로직 에러
- 외부 SDK 에러
- 클라이언트 사이드에서만 발생하는 예외
패키지 레퍼런스
자세한 API는 @howmuchhome-web/logger를 참고하세요.