[Vue.js + PWA + Prerender] PWA와 Prerender 적용 웹 앱 개발
프로그레시브 웹 앱은 이미 수년전부터 큰 화두였습니다.
오늘은 PWA를 적용하며 느끼고 고민했던 것들 + 약간의 팁(...이라고 말하기 민망한 주의사항)을 공유하고자 합니다.
* 글에 앞서 해당 내용은 주관적인 생각이 20,000% 포함되어 있으며 이 글의 내용이 개발 가이드는 절대 아님을 밝힙니다. 수정 보완해야 할 내용이 있다면 댓글에 공유 부탁드립니다. (_ _)
* 이어서 간단한 소개를 할 것이지만, 혹시나 PWA를 처음 들어보신다면, 아래의 링크들을 참고 해보세요. 많은 도움이 되실 것 같습니다.
PWA는??
처음 PWA를 접하는 분들은 이 용어에 많은 혼란을 느낍니다. 이것은 어떠한 언어 혹은 프레임워크일까요? 정확히 말하자면 PWA는 일반적인 웹 앱을 네이티브 앱 수준에 가깝게 점진적으로 발전시키려는 목표를 가지고 2015년 '알렉스 러셀'이 고안한 '개념'입니다. 그렇기 때문에 PWA는 그 개념에 관한 이해가 상당히 중요하며 어떠한 플러그인 혹은 기술로 PWA가 certificated(인증)되는 것은 아닙니다.
그렇다면 PWA의 개념을 만족시켜주기 위해서는 무엇이 필요할까요?? (여기서는 4가지의 핵심 키워드가 아닌 3가지 키워드로만 설명합니다.)
Progressive Web Apps are user experiences that have the reach of the web, and are:
- Reliable - Load instantly and never show the downasaur, even in uncertain network conditions.
- Fast - Respond quickly to user interactions with silky smooth animations and no janky scrolling.
- Engaging - Feel like a natural app on the device, with an immersive user experience.
This new level of quality allows Progressive Web Apps to earn a place on the user's home screen.
위의 글은 구글 개발자 사이트의 Progressive Web Apps 메뉴의 소개입니다. 바로 이 세가지(혹은 네가지)가 PWA의 핵심 개념이며 이러한 노력과 기술들이 들어있어야 PWA로 인정받을 수 있습니다.
신뢰성
'불안정한 네트워크 조건에서도 페이지 로딩이 즉각적이고 공룡(downasaur: 크롬 에러페이지 주인공입니다.)을 보여서는 안된다'
어떠한 형태로든 네트워크 의존일 수 밖에없는 웹 앱을 네트워크 상황에 맞게 사용자가 사용할 수 있도록 만들어야 함을 뜻합니다.
신속성
'사용자 상호작용에 부드러운 애니메이션 효과와 버벅임 없는 스크롤로 즉각적으로 반응해야한다.'
사용자가 스크롤이나 터치, 클릭 등의 상호작용을 하였을 경우 그에 대한 응답이 빠르게 처리되어야 함을 의미합니다.
참여성
'몰입형 사용자 경험(immersive user experience)으로 디바이스의 원래 앱처럼 느끼게 한다.'
참여성(Engaging)이라는 말이 어렵게 느껴질지 모르지만 현재의 웹 앱이 단순히 웹의 어느 곳에서 서비스되는 것처럼 느끼게 하는 것 보다 마치 실제 데스크탑의 프로그램처럼, 혹은 스마트폰에 직접 설치한 앱처럼 느껴지게 해야함을 표현합니다.
위의 세가지를 지향하고 이를 적극적으로 반영한 웹 앱이 바로 PWA입니다.
Vue.js + PWA + Prerender
이번 프로젝트 역시도 웹뷰에서 서비스 되기 때문에, 조금더 네이티브 앱에 가깝게 구현하기 위한 노력들을 하게 되었고 자연스럽게 PWA를 도입하기로 결정하였습니다. 이와 함께 SEO, 공유기능 등의 이유로 Prerender역시 함께 도입했습니다.
Vue-cli를 사용하여 PWA 플러그인을 추가하였고 Prerender SPA Plugin을 사용하여 해당 프로젝트를 진행하였습니다. PWA를 프로젝트에 추가하면 일반적인 Vue.js 프로젝트에 추가적으로 manifest.json과 registerServiceWorker.js 파일이 설치됩니다.
manifest.json은 일반적인 웹 앱을 확장하여 설치가능한 네이티브 앱처럼 만들게 도와줍니다. 흔히 이 파일에는 앱의 이름과 디스플레이 모드, 방향(orientation), 스플래시 스크린의 설정 값들이 들어가게됩니다. 물론 브라우저에 따라서 제대로 동작되지 않는 것들이 많습니다. 이와 함께 설치된 registerServiceWorker.js는 Service worker를 등록하게 도와주는 파일입니다.
서비스 워커를 모두 소개하기에는 무리가 있지만 간단히 설명하자면 브라우저의 백그라운드에서 작동하는 워커로 오프라인에서 웹 페이지를 보여줄 수 있는 방법을 고민하다 나오게 되었습니다. 기본적인 registerServiceWorker.js 외에도 서비스워커를 등록할 수 있는 방법은 여럿이며 서비스워커는 '캐싱전략'과 아주 밀접한 관계를 가지고 있으므로 이는 직접 찾아보시면서 학습하셔야 할 필요가 있습니다.
추가적으로 전달드리고 싶은 점은 서비스워커를 도입하면 디바이스의 푸시 알람을 보낼 수 있다는 부분과 업데이트가 일반적인 웹 앱과는 다르다는 것입니다. (iOS는 지원하지 않으며 웹뷰에서는, 저희 내부적인 보안상 이유 때문인지는 몰라도, 작동이 안됩니다.) 무작정 PWA를 도입했다가 캐싱과 업데이트 이슈 때문에 빠르게 삭제해 본 경험이 있어서 그런지(이번이 두번째 PWA 도입) 우선은 어떻게 사용자에게 앱에 새로운 변화를 알릴지 (iOS 포함) 그리고 어떻게 해야 엔드유저의 이탈을 막을 수 있을지 여러가지를 다양한 부분에서 신경써야 된다고 말씀드리고 싶습니다.
위의 두가지에 대한 전략과 셋팅이 어느 정도 진행되었다면 이제 PWA 본연의 철학을 위해 내부적으로 진행해야 할 리스트들이 필요할 것입니다. 저 같은 경우에는 혼자서 모든 마크업과 프론트엔드 개발을 진행하기에 바이크쉐딩이 없었지만 어떠한 전략도 완벽한 것은 없으니 가장 적게 잃는 것 (성능, 심미적 아름다움, 사용성 등)을 선택하시길 바랍니다.
저의 최우선 과제는 성능과 앱과 같은 UI/UX였습니다.
처음 디자인을 받았을 때에는 위와 같은 UI가 아니였습니다. 조금 더 모바일 웹 같은 모습이였고 단순한 스크롤 이벤트로 각각의 리스트 아이템들을 살펴볼 수 있었기 때문에 제가 원하던 형태의 앱과 같은 UI/UX는 아니었습니다. 이를 해소하기 위하여 여러 앱을 벤치마킹하였고 디자인팀과 회의를 통해 현재의 Card 형태의 디자인과 스와이프 제스쳐를 사용한 디자인이 완성되었습니다.
디자인이 완료된 후에는 최우선 과제였던 성능적인 부분이 걱정되었습니다. 기존의 스와이퍼 라이브러리들은 제가 원하던 효과를 지원하지 않았고 이를 해결하기 위해서 외부 라이브러리를 수정하기에는 예측과 방어가 어려운 side-effect가 우려되었기 때문에 저희만의 UI/UX를 위한 기능을 직접 제작하기로 결정하였습니다.
마우스 커서를 이용한 시연이기에 샘플로 보여지는 gif에서는 그 느낌이 잘 살진 않지만 제법 앱과 같은 부드러운 터치 제스쳐 느낌을 줄 수 있었고 필요한 기능만 간단하게, 예를 들어 물리적인 느낌을 주기 위해서 자바스크립트로 ease-in 등을 구현하지 않고(사실 삼각함수 바보인 문과 출신인지라...) 최대한 CSS를 활용하여, 작은 사이즈로 원하던 효과를 얻을 수 있었습니다.
이렇게 직접 구현이 필요한 부분과 외부 라이브러리 사용을 검토하는 것은 개발 및 운영상 큰 이점이 있습니다. 커뮤니티가 활발한 라이브러리를 사용한다면 1. 에러가 발생할 경우 집단 지성을 통해 이슈 대응이 수월하고 2. 그러므로 비교적 안정적인 서비스 운영을 할 수 있습니다. 직접 제작할 경우에도 1, 2의 장점을 모두 가질 수 있겠지만 경험상으로는 잘 만들어진 라이브러리를 적절하게 활용하는 것이 개발 생산성을 높이는데 도움이 많이 되었습니다.
직접 구현이 필요하다는 판단은 개발자들 마다 다른 관점의 다양한 이유가 존재하겠지만 저에게는 크게 1. 구현하고자 하는 효과 / 연산 / 퍼포먼스가 기대치 이하일 때, 2. 근사치에 가까운 라이브러리가 존재하지만 수정하는 것이 위험하다고 판단될 때 입니다. 물론 선택과 도입은 개발자 본인의 몫입니다.
프리렌더링은 처음 시도해 보는 것이기도 했고 레퍼런스가 적었기 때문에 개발 초기부터 꾸준히 이터레이션 하면서 배포 과정 중에 확인 및 수정하며 작업을 진행했습니다. 프리렌더링은 미리 정적인 파일을 만들어 deploy하는 것으로 routing 룰과 많은 관계가 있습니다. 예를 들어 /some_dir을 사용하는 path에서 보여지는 페이지를 프리렌더링 하고 싶다면 해당 파일을 번들링 시에 만들어 내도록 vue.config.js에 routes를 등록하고 해당 routes 룰을 직접 수정 및 보완해야 합니다. 여기서 페이지에 어떠한 사용자 상호작용 혹은 일정 시간이 필요하다면 renderer 프로퍼티에 별도의 옵션들을 설정하여 스냅샷을 찍을 때 원본에 가깝도록 만들어줘야 합니다. renderAfterTime 같이 물리적인 시간을 설정하여 기다린 후 파일을 생성하거나 혹은 어떠한 이벤트가 발생한 후 캡쳐하도록 하는 방법들이 Github에 자세히 설명되어있으며 이를 적극 활용하시기 바랍니다.
이렇게 개발 막바지에 들어서고 웹뷰가 아닌 '홈 스크린에 추가'된 웹 앱으로 스플래시 스크린, 아이콘 사이즈 등을 테스트 하다 중요한 사실을 하나 확인할 수 있었습니다. 네이티브 앱과 달리 사용자의 방향(orientation) 전환 이슈가 발생하였고 이를 방지하기 위한 추가적인 조치가 필요하다는 것이었습니다.
UI/UX적으로도 해당 orientation을 대응할 수 없었기에 방향전환 이벤트가 발생하면 정상적인 사용을 유도하기 위한 기능을 넣게 되었습니다. 물론 웹에서 접근하는 경우가 극히 적지만 이를 방어하고 불필요한 에러 혹은 그와 같은 경험을 제거하기 위하여 꼭 필요한 조치였습니다.
아직 오프라인에 대한 대응을 어떻게 해야할지, 그리고 PC 대응 등의 여러 태스크들이 존재하지만 PWA가 지향하는 바와 근접하게 개발하기 위하여 여러가지를 다양하게 고민해 본 프로젝트였습니다.
도입후
확실히, 글에서 언급한 것 외에도, 여러가지 고민을 한 결과 만족할만한 결과물을 얻을 수 있었습니다. 다만 모든 선택이 다 장점만 얻을 수 없듯이 프리렌더링에 대한 아쉬움은 조금 큰 편입니다. 모든 데이터를 자동적으로 반영하여 물리적인 document를 생성하는 것은 불가능하며 이를 통해 검색엔진에서 비교적 정확, 신속한 정보를 얻어내기에는 무리가 있을 것 같습니다. 만약 포스팅한 내용과 비슷하게 서비스를 개발하시려고 한다면 잘 참고하시기 바랍니다.