프로젝트에 PWA 적용하기(2) - React 프로젝트에 PWA를 적용해보자

프로젝트에 PWA 적용하기(2) - React 프로젝트에 PWA를 적용해보자

·

6 min read

🖐
팀원들에게 공유할 목적으로 작성한 글입니다.

이번에는 기존 React 프로젝트에 PWA를 적용한 경험을 공유하겠습니다. 1편에서는 PWA가 무엇인지 그리고 PWA 적용을 위해 필요한 조건들에 대해 알아보았습니다.

1편을 요약하면 PWA는 Progressive Web Apps의 약자로, 앱과 같은 사용자 경험을 제공하는 웹 앱입니다. 완전히 새로운 개념이 아닌 기존에 있던 기술을 조합해 사용하는 방법인데요, PWA 적용을 위해서는 HTTPS, Manifest, Service Worker가 필요합니다.

그럼 이제 작심삼칩 프로젝트에 PWA를 적용해 봅시다!

💪 기존 React 프로젝트에 PWA 적용하기

PWA 프로젝트를 만드는 방법은 두 가지 입니다.

  1. 템플릿을 사용해 처음부터 PWA 프로젝트로 시작하기

  2. 기존 프로젝트에 PWA 도입하기

CRA(Create React App) 공식 문서의 Making a Progressive Web App 파트를 보면 템플릿을 사용해 간단하게 PWA 프로젝트를 생성하는 방법이 나와있습니다. 하지만 지금은 기존 프로젝트를 PWA로 전환해야 하니 2번 방법으로 진행하겠습니다.

CRA로 프로젝트를 만들었다면 PWA 적용 시 필요한 Manifest 파일과 서비스 워커 관련 패키지가 포함되어있습니다. 그래서 패키지를 추가로 설치할 필요 없이 필요한 코드만 작성하면 됩니다. 아래 저장소에서 PWA 템플릿으로 프로젝트를 만들때 함께 생성되는 PWA 세팅 파일을 확인할 수 있습니다. 여기서 필요한 부분만 가져오겠습니다.

1. HTTPS

기존 프로젝트는 HTTPS로 배포되어 있었으니 패스하겠습니다. 만약 적용되어 있지 않다면 보안 및 Service Worker의 정상적인 작동을 위해 적용해야 합니다.

2. Web App Manifest

Web App Manifest는 PWA가 사용자의 디바이스에 어떻게 표시되고 동작해야 하는지 브라우저에게 알려주는 JSON 파일입니다. 웹 앱이 PWA가 되려면 설치 가능해야 하고, 설치가 가능하려면 Manifest가 있어야 합니다. Manifest 파일에는 앱 설치 시 필요한 정보(이름, 아이콘 등)를 정의할 수 있습니다.

CRA 기본 구성에 public/manifest.json 파일이 있는데 프로젝트 초기 세팅 시 필요없어서 삭제 했습니다. 이제는 필요하니 public 폴더에 manifest.json 파일을 다시 생성합니다.

우선 아래와같이 간단하게 작성했습니다.

{
  "name": "작심삼칩",
  "short_name": "작심삼칩", 
  "icons": [
    {
      "src": "chips192.png",
      "type": "image/png",
      "sizes": "192x192",
      "purpose": "any maskable"
    },
    {
      "src": "chips512.png",
      "type": "image/png",
      "sizes": "512x512",
      "purpose": "any maskable"
    }
  ],
  "start_url": ".",
  "display": "standalone",
  "theme_color": "#111111",
  "background_color": "#ffffff"
}
  • name: 사용자에게 표시되는 웹 앱의 이름

  • short_name: name을 표시할 공간이 충분하지 않을 때 표시되는 웹 앱의 이름

  • icons: 아이콘 정의(객체 배열을 지정함)

    • src: 이미지 파일 경로

    • type: 이미지의 미디어 타입

    • sizes: 이미지 사이즈

    • purpose: 이미지 사용 방법(monochrome, maskable, any)

  • start_url: 사용자가 웹 앱을 시작할 때 로드하는 기본 URL

  • display: 웹사이트 표시 모드(fullscreen, standalone, minimal-ui, browser)

  • theme_color: 앱의 기본 테마 색상

  • background_color: 스타일시트가 로드되기 전 표시할 페이지의 배경색

그리고 index.html 파일에 아래 코드를 추가해 Manifest를 연결합니다. 이렇게 하면 Manifest 적용은 끝입니다.

<!-- <head> 요소 내부에 작성 -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

참고 1) 필수 키

크롬 기반 브라우저는 Manifest에 다음 키가 꼭 포함되어야 합니다.

  • name

  • icons

  • start_url

  • display 또는 display override

참고 2) 아이콘

운영 체제 및 디바이스 기능에 따라 서로 다른 아이콘 모양을 가지고 있습니다. 아이콘이 모든 위치에서 올바르게 표시되도록 여러 버전을 만들어야 합니다. 최소한 192x192, 512x512 해상도 아이콘은 있어야 합니다.

아래 사이트에서 운영 체제 별 아이콘 사이즈를 확인할 수 있습니다.

참고 3) 아이콘 마스킹

플랫폼마다 아이콘 모양이 다른데 마스킹을 하지 않으면 아이콘이 잘리거나 너무 작게 표시될 수 있고 혼자 동떨어진 느낌을 줄 수 있습니다. 그래서 purpose키 값에 “maskable”을 추가해 플랫폼 별 아이콘 모양에 맞게 이미지를 조정할 수 있도록 설정했습니다.

참고 4) any maskable

이번 글을 작성하면서 다시 크롬 개발자 도구에서 Manifest 탭을 확인했는데 아래와 같은 경고 문구가 등장했습니다. 경고 문구를 보니 ‘패딩이 너무 많거나 적어서 일부 플랫폼에서는 잘못 보일 수 있기 때문에 이제는 더 이상 “any maskable” 값이 추천되지 않는다’고 합니다.

Declaring an icon with purpose "any maskable" is discouraged. It is likely to look incorrect on some platforms due to too much or too little padding.

이전에 purpose를 지정할 때 하나의 아이콘을 두 가지 용도(일반 용도, 마스킹 용도)로 사용하기 위해 “any maskable” 값을 지정했습니다.

any는 아이콘을 그대로 보여주고 maskable은 아이콘을 마스킹 합니다. 때문에 마스킹용 아이콘을 만들때는 안전 구역(safe zone)을 고려해 만듭니다. 그래서 maskable 아이콘은 any 아이콘보다 사방에 여백이 더 생긴 디자인이 됩니다. maskable 아이콘을 any 용도로 지정하면 아이콘이 그대로 사용되기 때문에 아이콘의 콘텐츠가 더 작게 보일 수 있습니다.

출처:w3c.github.io/manifest/#icon-masks

가장 좋은 방법은 두 가지 용도에 따라 별도의 아이콘 파일을 생성해 각각 용도에 맞게 사용하는 것이라고 합니다. 아래처럼 any 용도로 제작한 아이콘은 purpose를 any로 지정하고, maskable 용도로 제작한 아이콘은 purpose를 maskable로 지정했습니다.

"icons": [
    {
      "src": "any-chips192.png",
      "type": "image/png",
      "sizes": "192x192",
      "purpose": "any"
    },
    {
      "src": "maskable-chips192.png",
      "type": "image/png",
      "sizes": "192x192",
      "purpose": "maskable"
    },
    {
      "src": "any-chips512.png",
      "type": "image/png",
      "sizes": "512x512",
      "purpose": "any"
    },
    {
      "src": "maskable-chips512.png",
      "type": "image/png",
      "sizes": "512x512",
      "purpose": "maskable"
    }
  ],

아이콘을 각각 용도에 맞게 만들고 purpose 값을 따로 설정하니 경고 문구가 사라졌습니다.

3. Service Worker

Manifest 설정을 완료했으면 Service Worker 설정을 해봅시다.

기존 프로젝트는 CRA로 만들었습니다. CRA의 Making a Progressive Web App 문서를 보면 CRA 4부터 프로젝트에 src/service-worker.js 파일을 추가해서 서비스 워커를 컴파일하고 프리캐싱할 URL 목록을 주입하는 Workbox의 InjectManifest 플러그인을 사용할 수 있다고 합니다.

아래 사진처럼 CRA로 기본 리액트 프로젝트를 만들면 node_modules에 workbox가 있는 것을 볼 수 있습니다.

참고로 Workbox는 서비스 워커를 더 쉽게 사용할 수 있도록 해주는 라이브러리 입니다. InjectManifest 플러그인은 서비스 워커 파일에 삽입되는 사전에 캐시할 애셋 목록을 생성하는 역할을 하고 workbox-webpack-plugin 모듈에 클래스로 구현되어 있습니다.

아무튼 추가로 패키지를 설치 할 필요가 없으니 cra-template/pwa 저장소에서 필요한 파일만 가지고 옵시다!

필요한 파일은 service-worker.js, serviceWorkerRegistration.js 입니다. src 폴더에 해당 파일 및 코드를 가져와서 넣습니다.

그리고 index.tsx에 있는 아래 코드를 복사해서 프로젝트의 index.tsx 파일에 붙여넣습니다.

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://cra.link/PWA
serviceWorkerRegistration.unregister();

서비스 워커를 사용하려면 서비스 워커 등록을 해야합니다. unregister()를 register()로 바꾸면 서비스 워커를 등록할 수 있습니다.

서비스 워커 등록이 잘 되었다면 ‘개발자 도구-Application-Service workers 탭’에서 아래와 같은 화면을 확인할 수 있습니다.

여기서 잠시 service-worker.jsserviceWorkerRegistration.js가 어떤 일을 하는지 간단하게 알아봅시다.

  • service-worker.js

    • 애플리케이션의 백그라운드에서 실행되는 스크립트입니다. Service Worker는 리액트 애플리케이션을 오프라인에서 사용할 수 있게 합니다.
  • serviceWorkerRegistration.js

    • 서비스 워커가 성공적으로 등록되었는지 여부를 알려줍니다.

캐싱된 파일은 ‘개발자 도구-Application-Cache storage’에서 확인할 수 있습니다.

💁‍♀️ 유용한 사이트

maskable 로고를 제작할 수 있는 사이트 입니다. 기존 아이콘을 업로드하고 색상, 크키를 조정한 다음 내보내기를 하면 됩니다.

PWA Builder

PWA 앱 제작을 도와주는 사이트 입니다.

Learn PWA

PWA 개발을 배울 수 있는 사이트 입니다.

😆 번외) 새 PWA 프로젝트 시작하기

기존 프로젝트에 PWA를 도입하는 것이 아닌 처음부터 PWA 프로젝트를 시작하려면 아래 템플릿을 사용할 수 있습니다.

CRA

CRA 공식 문서에 나와있는 PWA 프로젝트 템플릿 명령어를 사용해 만들 수 있습니다.

npx create-react-app my-app --template cra-template-pwa

Vite

Vite PWA 플러그인을 사용해 PWA 프로젝트를 만들 수 있습니다. Vite PWA 문서에서 자세한 내용을 확인할 수 있습니다.

npm install -D vite-plugin-pwa

👋 마무리

이상 작심삼칩 프로젝트에 PWA를 적용한 경험을 마무리 짓겠습니다. 지금은 간단하게 다운로드 및 캐싱 정도만 구현했습니다. 아직 푸시 알림, 다운로드 버튼 등 할 게 많지만, 작심삼칩 프로젝트는 더 이상 진행되지 않기에 추후 새 프로젝트에 적용할 예정입니다. 새 프로젝트에서는 사용성을 높일 수 있는 기능 및 화면(푸시 알림, 오프라인 페이지 등)을 추가해 앱의 퀄리티를 더욱 높이면 좋을 것 같습니다.

📌 참고