| 플랫폼 선택: | iOS+ Android Android NDK 웹 Flutter Unity |
이 가이드에서는 웹 앱에서 Firebase Crashlytics를 시작하는 방법을 설명합니다.
앱에 Firebase Crashlytics SDK를 설정하면 Firebase Console에서 포괄적인 비정상 종료 보고서를 받을 수 있습니다. 웹 앱용 Crashlytics를 사용하면 오류에 대한 보고서를 받을 수 있습니다.
시작하기 전에
Crashlytics를 설정하려면 Firebase Console과 IDE 모두에서 태스크를 수행해야 합니다(예: 앱의 Firebase 구성 및 Crashlytics SDK 추가). 설정을 완료하려면 테스트 비정상 종료를 강제로 적용하여 첫 번째 비정상 종료 보고서를 Firebase로 전송해야 합니다.
웹 앱에서 Crashlytics를 수동으로 설정하려면 다음 기본 요건을 완료해야 합니다.
아직 Firebase 프로젝트와 Firebase 앱이 없으면 만듭니다. 아직 추가하지 않았다면 JavaScript 프로젝트에 Firebase를 추가합니다.
계정에 프로젝트에 대한 소유자 권한이 있는지 확인합니다. 필요한 API 및 IAM 역할을 사용 설정하는 데 필요합니다.
Firebase 프로젝트에서 사용한 만큼만 지불하는 Blaze 요금제를 사용하고 있는지 확인합니다. 웹용 Crashlytics는 원격 분석 스토리지에 Cloud Logging를 사용하고 소스 지도 스토리지에 Cloud Storage를 사용합니다. 이는 무료 등급이 있는 유료 제품입니다. Logging 가격 책정 및 Cloud Storage 가격 책정에 대해 자세히 알아보세요.
1단계: 웹 앱에 Crashlytics 사용 설정
웹용 Crashlytics는 Cloud Logging, Error Reporting, Cloud Storage를 사용하므로 이러한 API를 사용 설정하고 프로젝트에 대한 권한을 구성해야 합니다. Firebase Console 또는 Firebase CLI를 사용하여 웹 앱에 Crashlytics를 사용 설정할 수 있습니다.
Firebase Console 사용
Firebase 콘솔에서 DevOps 및 참여 > Crashlytics 대시보드로 이동합니다.
페이지 상단의 드롭다운 선택기에서 웹 앱을 선택합니다.
Crashlytics 설정을 클릭하고 화면에 표시된 안내를 따릅니다.
Firebase CLI 사용
터미널에서 프로젝트를 구성하거나 AI 코딩 에이전트를 사용하려면 Firebase CLI를 사용하면 됩니다.
Firebase CLI v15.35.0 이상이 설치되어 있고 인증(
firebase login)되었는지 확인합니다. Firebase CLI를 설치하거나 업데이트하는 방법을 알아보세요.다음 명령어를 실행하여 웹 앱에 Crashlytics를 설정합니다.
firebase crashlytics:setup \ --app FIREBASE_APP_ID \ --project PROJECT_ID이 명령어는 다음 설정 단계를 수행합니다.
프로젝트가 사용한 만큼만 지불하는 Blaze 요금제를 사용하고 있는지 확인하고 필요한 경우 업그레이드하라는 메시지를 표시합니다.
필수 Crashlytics 및 원격 분석 API(
firebasetelemetry.googleapis.com,firebasetelemetryadmin.googleapis.com,firebasecrashlytics.googleapis.com,clienttelemetry.googleapis.com)를 사용 설정합니다.웹 앱의 API 키에 API 제한사항이 있는 경우 허용되는 API에
clienttelemetry.googleapis.com를 추가합니다.웹 앱의 Crashlytics 원격 분석 설정을 구성합니다.
대화형으로 실행하면 새 문제 및 회귀 문제에 대한 이메일 알림을 사용 설정하라는 메시지가 표시됩니다.
2단계: 앱에 SDK 추가 및 초기화
Crashlytics SDK를 설치합니다.
npm install firebaseFirebaseApp인스턴스를 초기화합니다.싱글톤 초기화를 사용할 중앙 구성 유틸리티 파일을 만듭니다. Firebase 앱 구성 값을 찾으려면 Firebase Console에서
프로젝트 설정 > 일반 페이지로 이동하고 내 앱 카드로 이동하여 웹 앱을 선택합니다.// src/lib/firebase.ts import { initializeApp, getApps, getApp } from '@firebase/app'; const firebaseConfig = { apiKey: "API_KEY", authDomain: "AUTH_DOMAIN", projectId: "PROJECT_ID", storageBucket: "STORAGE_BUCKET", messagingSenderId: "MESSAGING_SENDER_ID", appId: "FIREBASE_APP_ID" }; // Guarantee singleton initialization across renders or module refreshes export const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApp();
3단계: 프레임워크의 자동 오류 캡처 구성
자동 프레임워크 오류 수신자와 수동 추적기 모두 공유 FirebaseApp 인스턴스를 사용합니다. 프레임워크를 선택하여 자동 오류 캡처를 구성합니다.
Angular
src/app/app.config.ts에서 앱의 제공자에 provideCrashlytics(() => app)를 추가하여 Angular 전역 오류 포착 런타임 및 자동 경로 추적과 통합합니다.
// src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideCrashlytics } from '@firebase/crashlytics/angular';
import { app } from '../lib/firebase'; // Shared initialization script
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideCrashlytics(() => app)
]
};
Next.js
Next.js 15.3 이상(권장): 클라이언트 계측(instrumentation-client.ts)
Next.js 15.3 이상에는 instrumentation-client.ts(또는 instrumentation-client.js)를 통한 네이티브 클라이언트 측 계측이 포함됩니다. Next.js는 React 하이드레이션이 시작되기 전에 브라우저에서 이 파일을 실행합니다.
src/instrumentation-client.ts(또는 src/ 디렉터리를 사용하지 않는 경우 프로젝트 루트의 instrumentation-client.ts)를 만들고 Crashlytics를 초기화합니다.
// src/instrumentation-client.ts
import { getCrashlytics } from '@firebase/crashlytics';
import { app } from './lib/firebase'; // Shared initialization script
getCrashlytics(app);
이 단일 파일은 React 하이드레이션 전에 Crashlytics를 초기화하고 src/app/layout.tsx 또는 React 구성요소를 수정하지 않고도 전역 오류 리스너(error 및 unhandledrejection)를 자동으로 등록합니다.
Next.js < 15.3: 클라이언트 측(앱 라우터 레이아웃)
15.3 이전의 Next.js 버전의 경우 layout.tsx는 기본적으로 서버 구성요소입니다. FirebaseApp는 직렬화할 수 없으므로 'use client' 지시어로 표시된 클라이언트 구성요소(예: src/components/CrashlyticsProvider.tsx)에서 <FirebaseCrashlytics>(@firebase/crashlytics/react에서 Crashlytics를 초기화하고 포착되지 않은 오류를 캡처)를 래핑합니다.
// src/components/CrashlyticsProvider.tsx
'use client';
import React from 'react';
import { FirebaseCrashlytics } from '@firebase/crashlytics/react';
import { app } from '../lib/firebase'; // Shared initialization script
export function CrashlyticsProvider({
children,
}: {
children: React.ReactNode;
}) {
return (
<>
<FirebaseCrashlytics firebaseApp={app} />
{children}
</>
);
}
그런 다음 루트 레이아웃을 <CrashlyticsProvider>로 래핑합니다.
// src/app/layout.tsx
import React from 'react';
import { CrashlyticsProvider } from '../components/CrashlyticsProvider';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<CrashlyticsProvider>
{children}
</CrashlyticsProvider>
</body>
</html>
);
}
React(React Router 없음)
루트 앱 구성요소(일반적으로 src/App.tsx 또는 src/main.tsx)를 <FirebaseCrashlytics> 오류 경계 구성요소로 래핑합니다. 이는 전역 수신자 역할을 하며 브라우저 및 React UI에서 처리되지 않은 거부 및 예외를 처리합니다. 모든 하위 구성요소 계층 구조를 래핑하고 포착되지 않은 예외 또는 라우팅 오류를 자동으로 캡처합니다.
// src/App.tsx
import React from 'react';
import { FirebaseCrashlytics } from '@firebase/crashlytics/react';
import { app } from './lib/firebase'; // Shared initialization script
export default function App({
children,
}: {
children?: React.ReactNode;
}) {
return (
<FirebaseCrashlytics firebaseApp={app}>
{children}
</FirebaseCrashlytics>
);
}
React Router(v6 이상)
React Router v6 이상을 사용하는 경우 React Router의 <Routes> 컨테이너를 <CrashlyticsRoutes>로 바꿉니다. 이렇게 하면 경로 전반에서 오류 경계를 자동으로 매핑하고 /profile/:userId과 같은 템플릿 경로 이름을 사용하여 오류를 추적합니다.
// src/App.tsx
import React from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import { CrashlyticsRoutes } from '@firebase/crashlytics/react-router';
import { app } from './lib/firebase'; // Shared initialization script
import Home from './pages/Home';
import Profile from './pages/Profile';
import Product from './pages/Product';
export default function App() {
return (
<BrowserRouter>
<CrashlyticsRoutes firebaseApp={app}>
<Route path="/" element={<Home />} />
<Route path="/profile/:userId" element={<Profile />} />
<Route path="/product/:productId/details" element={<Product />} />
</CrashlyticsRoutes>
</BrowserRouter>
);
}
TanStack Router
앱에서 TanStack Router를 사용하는 경우 getCrashlytics(app)를 사용하여 Crashlytics를 초기화하고 라우터 인스턴스 구성(defaultOnCatch) 내에 직접 기본 오류 기록을 연결합니다.
import { recordError, getCrashlytics } from '@firebase/crashlytics';
import { app } from './lib/firebase'; // Shared initialization script
import { createRouter } from '@tanstack/react-router';
import { routeTree } from './routeTree.gen';
const crashlytics = getCrashlytics(app);
const router = createRouter({
routeTree,
defaultOnCatch: (error, errorInfo) => {
recordError(crashlytics, error, errorInfo);
},
});
(선택사항) 4단계: 앱 버전 관리 설정
회귀를 특정 배포로 격리할 수 있도록 웹 앱 빌드에 명확한 버전 문자열을 추가하는 것이 좋습니다. 프레임워크의 Crashlytics SDK를 초기화할 때 CrashlyticsOptions에 appVersion를 전달합니다(출시 문자열 또는 빌드 시간 환경 변수(예: process.env.NEXT_PUBLIC_APP_VERSION 또는 import.meta.env.VITE_APP_VERSION) 사용).
Angular
appVersion가 있는 CrashlyticsOptions 객체를 src/app/app.config.ts의 provideCrashlytics에 두 번째 인수로 전달합니다.
// src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideCrashlytics } from '@firebase/crashlytics/angular';
import { app } from '../lib/firebase'; // Shared initialization script
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideCrashlytics(() => app, {
appVersion: 'APP_VERSION'
})
]
};
Next.js
Next.js 15.3 이상(권장): 클라이언트 계측(instrumentation-client.ts)
appVersion가 있는 CrashlyticsOptions 객체를 src/instrumentation-client.ts의 getCrashlytics에 두 번째 인수로 전달합니다.
// src/instrumentation-client.ts
import { getCrashlytics } from '@firebase/crashlytics';
import { app } from './lib/firebase'; // Shared initialization script
getCrashlytics(app, {
appVersion: 'APP_VERSION'
});
Next.js < 15.3: 클라이언트 측(앱 라우터 레이아웃)
src/components/CrashlyticsProvider.tsx에서 appVersion를 사용하여 crashlyticsOptions를 <FirebaseCrashlytics>에 전달합니다.
// src/components/CrashlyticsProvider.tsx
'use client';
import React from 'react';
import { FirebaseCrashlytics } from '@firebase/crashlytics/react';
import { app } from '../lib/firebase'; // Shared initialization script
const crashlyticsOptions = {
appVersion: 'APP_VERSION'
};
export function CrashlyticsProvider({
children,
}: {
children: React.ReactNode;
}) {
return (
<>
<FirebaseCrashlytics
firebaseApp={app}
crashlyticsOptions={crashlyticsOptions}
/>
{children}
</>
);
}
React(React Router 없음)
src/App.tsx에서 appVersion를 사용하여 crashlyticsOptions를 <FirebaseCrashlytics>에 전달합니다.
// src/App.tsx
import React from 'react';
import { FirebaseCrashlytics } from '@firebase/crashlytics/react';
import { app } from './lib/firebase'; // Shared initialization script
const crashlyticsOptions = {
appVersion: 'APP_VERSION'
};
export default function App({
children,
}: {
children?: React.ReactNode;
}) {
return (
<FirebaseCrashlytics
firebaseApp={app}
crashlyticsOptions={crashlyticsOptions}
>
{children}
</FirebaseCrashlytics>
);
}
React Router(v6 이상)
src/App.tsx에서 appVersion를 사용하여 crashlyticsOptions를 <CrashlyticsRoutes>에 전달합니다.
// src/App.tsx
import React from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import { CrashlyticsRoutes } from '@firebase/crashlytics/react-router';
import { app } from './lib/firebase'; // Shared initialization script
import Home from './pages/Home';
import Profile from './pages/Profile';
import Product from './pages/Product';
const crashlyticsOptions = {
appVersion: 'APP_VERSION'
};
export default function App() {
return (
<BrowserRouter>
<CrashlyticsRoutes
firebaseApp={app}
crashlyticsOptions={crashlyticsOptions}
>
<Route path="/" element={<Home />} />
<Route path="/profile/:userId" element={<Profile />} />
<Route path="/product/:productId/details" element={<Product />} />
</CrashlyticsRoutes>
</BrowserRouter>
);
}
TanStack Router
appVersion가 있는 CrashlyticsOptions 객체를 두 번째 인수로 getCrashlytics에 전달합니다.
import { recordError, getCrashlytics } from '@firebase/crashlytics';
import { app } from './lib/firebase'; // Shared initialization script
import { createRouter } from '@tanstack/react-router';
import { routeTree } from './routeTree.gen';
const crashlytics = getCrashlytics(app, {
appVersion: 'APP_VERSION'
});
const router = createRouter({
routeTree,
defaultOnCatch: (error, errorInfo) => {
recordError(crashlytics, error, errorInfo);
},
});
5단계: 테스트 비정상 종료 강제 적용
Crashlytics 설정을 완료하고 Firebase Console의 Crashlytics 대시보드에 초기 데이터를 표시하려면 테스트 비정상 종료를 강제로 적용해야 합니다.
테스트 비정상 종료를 강제로 적용하는 데 사용할 수 있는 코드를 앱에 추가합니다.
클릭 시 런타임 오류를 발생시키는 테스트 버튼 또는 경로 트리거를 React, Next.js 또는 Angular 앱에 추가합니다.
// Trigger an unhandled exception on button click or route load function handleTestCrash() { throw new Error("Crashlytics Web SDK Test Unhandled Error!"); }<FirebaseCrashlytics>(React 또는 Next.js),provideCrashlytics(Angular) 또는<CrashlyticsRoutes>(React Router)를 사용하여 자동 오류 캡처를 구성한 경우 이 오류를 발생시키면 예외가 자동으로 캡처되고 업로드되도록 예약됩니다.앱을 빌드하고 실행한 후 테스트 오류를 트리거하여 앱이 첫 번째 비정상 종료 보고서를 Firebase에 전송하도록 합니다.
Firebase Console에서 DevOps 및 참여 > Crashlytics 대시보드로 이동하여 테스트 비정상 종료 보고서를 확인합니다.
페이지 상단의 드롭다운 선택기에서 웹 앱을 선택합니다.
페이지를 새로고침합니다. 몇 분 이내에 문제 표에 테스트 예외가 표시됩니다.
Console을 새로고침하고 5~10분 후에도 테스트 비정상 종료가 표시되지 않으면 설정 문제 해결을 참고하세요.
이제 Crashlytics에서 웹 앱의 오류를 모니터링합니다. 모든 보고서와 통계를 확인하고 조사하려면 Crashlytics 대시보드로 이동하세요.
다음 단계
- 읽을 수 있는 비정상 종료 보고서 가져오기:
프로덕션 스택 트레이스에 원래 파일 이름과 줄 번호가 표시되도록 자동 소스 맵(
.js.map) 업로드를 설정합니다. - 비정상 종료 보고서 맞춤설정: 처리된 예외를 보고하고, 커스텀 키를 추가하고, 원격 분석 레코드를 강제로 플러시하고, 샘플링을 구성하는 방법을 알아봅니다.