Turbopack이 아직 불안정하다는 것을 인지하고 있었지만, 예상보다 이른 단계에서 호환성 문제가 발생해서 당황스러웠습니다. 거의 대부분의 라이브러리와 호환된다고 알고 있었기 때문에 안일하게 생각했던 것 같지만, 오히려 직접 구현해 볼 기회가 생겨서 좋았습니다. 이번 경험을 통해 앞으로는 기술 스택을 선정할 때 최신 기술과 안정성의 균형을 한번 더 고민해봐야겠다는 생각이 들었습니다.
문제 상황
프로젝트 초기 설정 단계에서 빌드 과정에서 next-pwa와 Next.js 15 버전 간 호환성 문제가 발생했습니다.
Failed to compile.
Please check your GenerateSW plugin configuration:
[WebpackGenerateSW] 'experimental' property is not expected to be here. Did you mean property 'exclude'?
> Build failed because of webpack errors
ELIFECYCLE Command failed with exit code 1.
- 해당 라이브러리가 Next.js 13 / 14 버전까지는 안정적으로 동작했지만, Next.js 15부터 빌드 시스템이 Turbopack으로 전환되면서 기존 Webpack 기반 플러그인과 충돌이 발생했습니다.
- 특히 WebpackGenerateSW 플러그인에서 예상치 못한 옵션인 experimental이 들어가면서 Turbopack이 이를 정상적으로 파싱하지 못해 빌드 에러가 발생했습니다.
원인 분석
- next-pwa는 기본적으로 Webpack 기반 플러그인(workbox-webpack-plugin)을 활용합니다.
- 그러나 Next.js 15에서는 빌드 도구가 기본적으로 Turbopack으로 전환되었습니다.
- Webpack과 Turbopack은 내부 구조가 달라, 플러그인 호환성이 보장되지 않습니다.
→ next-pwa가 의존하는 Webpack 플러그인이 Turbopack 빌드 파이프라인에서 동작할 수 없기 때문에 충돌이 발생한 것이었습니다.
해결 방법
제가 찾아본 해결책은 크게 세 가지였습니다.
1. Webpack을 강제로 사용하기
- next.config.js에서 빌드 시 Turbopack 대신 Webpack을 사용하도록 설정하면 안정적으로 동작합니다.
- 하지만 Turbopack의 빠른 빌드 속도와 최신 Next.js 최적화를 포기해야 한다는 단점이 있습니다.
2. next-pwa의 최신 베타 버전(6.x) 사용하기
- 일부 이슈가 해결되었을 가능성이 있으나, 안정성이 검증되지 않아 위험한 방법입니다.
3. next-pwa를 제거하고 Service Worker를 직접 등록하기
- 추가적인 개발 비용은 들지만 Turbopack을 유지할 수 있고, 더 세밀한 커스터마이징이 가능하다는 장점이 있습니다.
→ 따라서 저는 3번 방법을 선택했습니다. 1번 방법인 Webpack 사용도 안전하다는 것을 알지만, dev 모드에서 Turbopack과 Webpack의 서버 속도, 빌드 속도 차이가 심하기 때문에 Turbopack을 유지하는게 더 나은 방법이라고 생각합니다.
구현 과정
1. Service Worker 파일 생성
public/service-worker.js 파일을 만들고, 기본적인 설치 및 활성화 이벤트를 등록했습니다. Service Worker 설치(install)와 활성화(active) 이벤트, 푸시 알림 이벤트를 등록해서 브라우저에 백그라운드 작업을 처리할 기본 스크립트를 만들었습니다.
self.addEventListener('install', (event) => {
console.log('[Service Worker] Installed');
self.skipWaiting();
});
self.addEventListener('activate', (event) => {
console.log('[Service Worker] Activated');
event.waitUntil(self.clients.claim());
});
self.addEventListener('push', (event) => {
console.log('[Service Worker] Push Received');
if (event.data) {
const { title, message } = event.data.json();
const options = {
body: message,
icon: '/icons/512.png',
};
event.waitUntil(self.registration.showNotification(title, options));
}
});
self.addEventListener('notificationclick', function (event) {
event.notification.close();
const url = event.notification.data?.url;
if (url) {
event.waitUntil(clients.openWindow(url));
}
});
- skipWaiting() → 새로운 SW가 설치되면 즉시 활성화
- clients.claim() → 모든 탭에서 해당 SW가 제어권 획득
- showNotification() → 푸시 메시지를 알림으로 노출
2. Service Worker 자동 등록 컴포넌트
src/components/ServiceWorkerRegister.tsx에서 클라이언트 진입 시 Service Worker를 자동 등록하도록 했습니다. 이 컴포넌트는 useEffect를 사용해 클라이언트 사이드 환경에서만 실행되도록 구성했습니다.
'use client';
import { useEffect } from 'react';
export default function ServiceWorkerRegister() {
useEffect(() => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('/firebase-messaging-sw.js')
.then((registration) => {
console.log('✅ Service Worker 등록 성공: ', registration.scope);
})
.catch((error) => {
console.error('❌ Service Worker 등록 실패: ', error);
});
}
}, []);
return null;
}
3. Manifest 파일 작성 및 Metadata 등록
src/app/ src/app/layout.tsx에서 manifest 설정인 앱 이름, 아이콘, 색상 등을 정의했습니다.
또한 Next.js Metadata API를 활용해 manifest와 themeColor를 지정했습니다.
export const metadata: Metadata = {
title: '튜닝',
description: '조직 기반 소셜 매칭 서비스',
manifest: '/manifest.webmanifest',
themeColor: '#ffffff',
viewport:
'width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no, viewport-fit=cover',
icons: {
icon: '/icons/favicon.png',
},
};
5. next.config.mjs 정리
next-pwa 관련 설정을 모두 제거하여 Turbopack 환경을 유지했습니다. 별도의 Webpack 기반 PWA 플러그인(next-pwa)을 사용하지 않고 Turbopack을 그대로 사용하면서도, PWA는 기본 Service Worker + Manifest 조합으로 동작하게 했습니다.
회고
처음에는 단순한 설정 문제라고 생각했지만, 빌드 도구 자체의 호환성 문제라는 걸 깨달았습니다.
이번 경험을 통해 알게된 것은 다음과 같습니다.
- 새로운 빌드 도구 도입 시 호환성 검증을 반드시 선행할 것
- 최신 기술 도입은 안정성과의 균형을 고려해야 함
- 직접 구현하면 더 큰 이해도와 제어권을 얻을 수 있음
다음에도 같은 문제가 생기지 않도록 기술 스택 선정 과정에서 더 고민해봐야겠다고 느꼈습니다.
'👩🏻💻 Develop > TroubleShooting' 카테고리의 다른 글
| [Next.js] 중복 ENUM 값 충돌로 인해 발생한 키워드 매핑 오류 해결하기 (2) | 2025.11.24 |
|---|---|
| [Next.js] Refresh Token 만료로 인한 페이지 무한 새로고침 오류 해결하기 (0) | 2025.09.07 |
| [Next.js] useSearchParams 사용 시 Next.js 15 빌드 실패 이슈 (0) | 2025.09.06 |
| [Next.js] SSE 브라우저 연결 오류 해결하기 (Nginx) (1) | 2025.09.02 |
| [Next.js] App Router에서 조건부 UI 숨김 시 발생한 hydration 오류 (2) | 2025.08.29 |