
배포를 vercel로 진행하다가 겪은 에러에 대한 문서이다. 이 문제 말고도 여러문제가 있었지만 중요한 순서대로 문서를 작성해 보았다..
프로젝트 구조
형식: Next.js App Router 기반 모노레포
앱 경로: apps/web
특징: Supabase 연동, Sentry, PWA 포함
@sentry/nextjs 패키지를 통해 프로젝트에 통합하고 Replay 기능을 활성화한 뒤, 해당 문제가 발생하기 시작함. 로컬 개발 환경에서는 정상 작동했으나, Vercel에 배포 후 production 환경에서 JS가 작동하지 않음을 확인함.
Uncaught Error: Multiple Sentry Session Replay instances are not supported
at new rq (chunk-sentry.js:2:34885)
at Object.rH (chunk-sentry.js:2:32411)
chunk-app.js:1 Uncaught ReferenceError: window is not defined
b2b66d86-1430f5fa6910f625.js:1 Multiple Sentry Session Replay instances are not supported오류 원인:
instrumentation.ts, instrumentation-client.ts, sentry.config.ts 파일이 모두 존재하고 각각에서 Sentry.init()을 실행하면서, Replay가 클라이언트에서 중복 초기화됨.
Replay 기능은 브라우저에서 단 한 번만 초기화돼야 하는데, 이 중복 초기화로 인해 JavaScript 번들이 완전히 멈추는 치명적인 문제가 발생한 것.
에러 로그 해석:
Multiple Sentry Session Replay instances are not supported: Replay는 싱글톤으로 동작해야 하며, 두 번 이상 초기화될 경우 예외를 던지고 JS 실행을 중단함.ReferenceError: window is not defined: SSR 환경에서 Replay 관련 코드가 조건 없이 실행되면서 서버에서 window 참조 오류 발생.구조적 이해:
@sentry/nextjs는 Next.js App Router 기반 프로젝트에서 클라이언트와 서버 각각 따로 초기화해야 함.NEXT_RUNTIME=nodejs 또는 edge)에서는 절대 실행되면 안 됨.instrumentation.ts와 instrumentation-client.ts가 각각 잘 분리되어야 중복 없이 작동 가능.✅ Replay 기능이란?
Sentry Replay는 사용자의 세션을 동영상처럼 기록해 오류가 발생했을 때 그 전후 UI 상태를 재현할 수 있도록 해준다. 사용자가 어떤 경로를 거쳐 문제를 만났는지 추적할 수 있어 버그 재현과 원인 파악에 매우 효과적이다.
하지만 이 기능은 브라우저에서만 동작하므로 서버 환경에선 절대 활성화되지 않아야 한다.
✅ 개발 환경(dev)에서는 문제가 없었던 이유:
개발 환경에서는 Webpack Dev Server와 Next.js의 유연한 오류 허용 정책 덕분에, Replay가 중복 초기화되더라도 전체 앱이 중단되지 않음.
특히 dev 모드에서는 번들 최적화나 strict error 처리 없이, 대부분의 JS 예외를 브라우저가 로그만 출력하고 실행을 지속하기 때문.
반면 production(Vercel) 환경에서는 JS 번들이 최적화되고, 오류 발생 시 실행이 즉시 중단되어 Replay 인스턴스 충돌이 앱 전체 렌더링 실패로 이어졌음.
Step 1: 중복된 설정 파일 sentry.config.ts 제거
Sentry.init()을 실행하면서 Replay가 두 번 초기화됨.instrumentation.ts, instrumentation-client.ts 두 파일만 유지하여 역할을 명확히 분리함.Step 2: 클라이언트 전용 초기화 파일 작성 – instrumentation-client.ts
import * as Sentry from '@sentry/nextjs';
if (process.env.NODE_ENV === 'production') {
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 1.0,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
integrations:
typeof window !== 'undefined'
? [
Sentry.replayIntegration({
maskAllText: true,
blockAllMedia: true,
}),
]
: [],
});
}
export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;
Step 3: 서버 전용 초기화 파일 작성 – instrumentation.ts
import * as Sentry from '@sentry/nextjs';
export function register() {
if (process.env.NODE_ENV !== 'production') return;
if (process.env.NEXT_RUNTIME === 'nodejs') {
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 1.0,
});
}
if (process.env.NEXT_RUNTIME === 'edge') {
Sentry.init({
dsn: process.env.SENTRY_DSN,
});
}
}
export const onRequestError = Sentry.captureRequestError;
Step 4: App Router layout.tsx 또는 Root 컴포넌트에 Sentry.ErrorBoundary로 래핑
<Sentry.ErrorBoundary fallback={<p>An error has occurred</p>}>
...
{children}
...
</Sentry.ErrorBoundary>
📚 참고 자료
Sentry.init()은 딱 한 번만, 적절한 런타임 조건에서 실행되어야 함.process.env.NEXT_RUNTIME, typeof window 등을 통해 환경 체크 필수window is not defined 오류 발생