[Frontend] Next.js App Router에서 PWA 세팅하기

YuminPark·2026년 4월 18일

Frontend

목록 보기
11/17
post-thumbnail

매일 말로만 듣던 PWA를 이번 큐시즘 밋업 프로젝트 Glit에서 직접 세팅해볼 기회가 생겼습니다.
글릿가벼운 기록으로 무심코 흘려보내는 업무 경험들을 취업 시장의 언어인 ‘직무 역량 데이터’로 치환해 주는 AI 커리어 아카이빙 서비스입니다. 해당 프로젝트는 Next.js 기반인데, 앱 푸시 알림과 앱 레이아웃을 고려했을 때 PWA 도입이 불가피했습니다. 생각보다 간단하니 필요하신 분들은 꼭 참고해보시길 바랍니다!

참고 자료


PWA란?

Progressive Web Application의 약자로, 공식 문서 표현을 빌리자면 "the reach and accessibility of web applications combined with the features and user experience of native mobile apps"입니다. 즉, 웹 기술로 만들었지만 네이티브 앱의 UX를 제공하는 앱이라고 할 수 있습니다.

공식 문서에서 언급하는 PWA의 주요 장점은 세 가지입니다.

  • 앱 스토어 심사 없이 즉시 업데이트 배포 가능
  • 단일 코드베이스로 크로스 플랫폼 지원
  • 홈 화면 설치, 푸시 알림 등 네이티브 앱 수준의 기능 제공

Next.js App Router는 PWA에 필요한 기능들을 내장으로 지원하기 때문에 별도 라이브러리 없이도 세팅이 가능합니다.

1. app/manifest.ts

공식 문서의 Step 1은 Web App Manifest 생성입니다. 브라우저가 이 파일을 읽어서 앱 이름, 아이콘, 화면 모드 등을 결정하고, 이를 통해 사용자가 홈 화면에 PWA를 설치할 수 있게 됩니다(native app-like experience).

Next.js는 app/manifest.ts를 자동으로 감지해서 /manifest.webmanifest 경로로 서빙해줍니다. 공식 문서의 기본 예시는 아래와 같습니다.

import type { MetadataRoute } from 'next'

export default function manifest(): MetadataRoute.Manifest {
  return {
    name: 'Next.js PWA',
    short_name: 'NextPWA',
    description: 'A Progressive Web App built with Next.js',
    start_url: '/',
    display: 'standalone',
    background_color: '#ffffff',
    theme_color: '#000000',
    icons: [
      { src: '/icon-192x192.png', sizes: '192x192', type: 'image/png' },
      { src: '/icon-512x512.png', sizes: '512x512', type: 'image/png' },
    ],
  }
}

공식 문서에서는 이 파일이 "the name, icons, and how it should be displayed as an icon on the user's device"에 대한 정보를 담아야 한다고 설명합니다. 아이콘 파일은 favicon generator 같은 툴로 생성해서 public/ 폴더에 넣으라고 안내하고 있습니다.

아이콘 파일 준비

공식 문서에서는 192x192와 512x512 두 가지 사이즈를 기본으로 안내합니다. 실제 서비스에서는 maskable 아이콘을 별도로 준비하는 것이 권장됩니다. 저는 구형 Android 대응을 위해 각 사이즈별로 모두 등록했습니다.

파일명 사이즈 Purpose 용도
icon-192x192.png 192×192 일반 아이콘 (기본값)
icon-192x192.png 192×192 maskable Android 적응형 아이콘
icon-512x512.png 512×512 스플래시 화면, 고해상도
icon-512x512.png 512×512 maskable Android 적응형 아이콘 (대형)

프로젝트에 적용한 manifest.ts

아래는 실제 사용한 manifest.ts 파일입니다.

import type { MetadataRoute } from "next";

export default function manifest(): MetadataRoute.Manifest {
  return {
    name: "Glit",
    short_name: "Glit",
    description: "KUSITMS 33rd 밋업 프로젝트 Glit",
    start_url: "/",
    display: "standalone",
    background_color: "#111111",  // 스플래시 화면 배경
    theme_color: "#111111",       // 상단 상태바 색상
    icons: [
      { src: "/icon-192x192.png", sizes: "192x192", type: "image/png" },
      { src: "/icon-192x192.png", sizes: "192x192", type: "image/png", purpose: "maskable" },
      { src: "/icon-512x512.png", sizes: "512x512", type: "image/png" },
      { src: "/icon-512x512.png", sizes: "512x512", type: "image/png", purpose: "maskable" },
    ],
  };
}
  • name / short_name : 홈 화면에 표시되는 앱 이름입니다. short_name은 공간이 부족할 때 사용됩니다.
  • display: "standalone" : 브라우저 주소창 없이 앱처럼 실행됩니다. fullscreen, minimal-ui, browser도 선택 가능합니다.
  • background_color : 앱 로드 전 스플래시 화면의 배경색입니다.
  • theme_color : Android Chrome 상단 상태바 색상입니다. iOS 대응은 layout.tsxappleWebApp, viewport 설정으로 별도 처리합니다.
  • purpose: "maskable" : Android 적응형 아이콘 대응입니다. 아이콘을 원형,사각형 등 다양한 모양으로 잘라낼 때 이상하게 보이지 않도록 여백을 포함한 버전으로, 일반 아이콘과 반드시 함께 등록해야 합니다.

2. app/layout.tsx

공식 문서는 manifest.ts 생성까지만 안내하지만, iOS Safari 대응과 viewport 처리를 위해 layout.tsx에도 추가 설정이 필요합니다.

import type { Metadata, Viewport } from "next";

export const metadata: Metadata = {
  title: "Glit",
  description: "KUSITMS 33rd 밋업 프로젝트 Glit",
  manifest: "/manifest.webmanifest",
  openGraph: {
    images: ["/og-image.png"],
  },
  appleWebApp: {                    
    capable: true,
    statusBarStyle: "default",
    title: "Glit",
  },
};

export const viewport: Viewport = {
  themeColor: "#111111",
  viewportFit: "cover",           
};

appleWebApp

iOS에서 홈 화면에 추가했을 때 적용되는 메타 설정입니다.
capable: true를 넣어야 Safari가 아닌 앱처럼 전체화면으로 실행됩니다. statusBarStyle은 상태바 텍스트(시간, 배터리 등) 색상을 제어하며 "default"(검정), "black"(흰색), "black-translucent"(흰색 + 상태바 투명) 중 선택할 수 있습니다. 서비스 배경색에 맞춰 가독성 있는 값으로 설정하면 됩니다.

viewport — themeColor와 viewportFit

Next.js 14부터 themeColormetadata가 아닌 별도 viewport export로 분리되었습니다.
viewportFit: "cover"는 뷰포트를 상태바 영역까지 확장시켜주는 옵션입니다. themeColor는 Android Chrome의 상단 툴바 색상을 지정하며, iOS Safari에서는 브라우저 탭 색상에 적용됩니다.

3. iPhone에서 PWA 설치하기

위 단계대로 PWA 설정을 마쳤다면, 배포된 서비스를 iPhone에 설치하는 방법을 알아보겠습니다.

1) Safari로 사이트에 접속한 뒤, 하단 공유 버튼을 탭합니다.

2) 스크롤을 내려 '홈 화면에 추가'를 탭합니다.

3) 앱 이름을 확인한 뒤 오른쪽 상단 '추가' 버튼을 탭합니다.

4) 홈 화면에 아이콘이 추가된 것을 확인할 수 있습니다.

5) 아이콘을 탭하면 주소창 없이 앱처럼 실행되는 것을 확인할 수 있습니다.


마무리

공식 문서의 예제는 최소한의 뼈대만 보여주기 때문에 실제 서비스에서는 iOS 대응, maskable 아이콘, viewport 처리까지 직접 챙겨야 합니다. 별도 라이브러리 없이 Next.js 내장 API만으로 PWA 세팅이 가능하다는 게 매우 편했습니다.
서비스 워커와 푸시 알림 구현은 다음 글에서 이어서 다루겠습니다!

0개의 댓글