👩🏻‍💻 Develop/TroubleShooting

[Next.js] next-pwa와 Turbopack 호환성 문제 해결하기

dev.daisy 2025. 9. 5. 23:24
Turbopack이 아직 불안정하다는 것을 인지하고 있었지만, 예상보다 이른 단계에서 호환성 문제가 발생해서 당황스러웠습니다. 거의 대부분의 라이브러리와 호환된다고 알고 있었기 때문에 안일하게 생각했던 것 같지만, 오히려 직접 구현해 볼 기회가 생겨서 좋았습니다. 이번 경험을 통해 앞으로는 기술 스택을 선정할 때 최신 기술안정성의 균형을 한번 더 고민해봐야겠다는 생각이 들었습니다.

 

문제 상황

프로젝트 초기 설정 단계에서 빌드 과정에서 next-pwaNext.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 조합으로 동작하게 했습니다.

 


회고

처음에는 단순한 설정 문제라고 생각했지만, 빌드 도구 자체의 호환성 문제라는 걸 깨달았습니다.

이번 경험을 통해 알게된 것은 다음과 같습니다.

  1. 새로운 빌드 도구 도입 시 호환성 검증을 반드시 선행할 것
  2. 최신 기술 도입은 안정성과의 균형을 고려해야 함
  3. 직접 구현하면 더 큰 이해도와 제어권을 얻을 수 있음

다음에도 같은 문제가 생기지 않도록 기술 스택 선정 과정에서 더 고민해봐야겠다고 느꼈습니다.