이 글은 jQuery 기반의 미디어 플랫폼 모바일 앱에 Vue를 도입한 사례를 정리한 내용입니다.
웹뷰에 Vue를 도입하거나 기존 레거시와 함께 점진적 도입을 고민하고 있으신 분들을 위해 공유합니다.
웹뷰를 좀 더 네이티브 앱에 가깝게 구현하자
기존에 작성했었던 Vue 점진적 도입 경험기(1) 에 이어 MBC 앱을 지속적으로 담당하게 되면서 연말 개편을 맡게 되었다. 지난 버전에서 웹뷰를 도입하며 vue 2.0 버전 cdn을 다운받아 사용해 보는 것을 시작으로, 각 페이지 별로 JS를 로딩하는 고전적 MPA구조로 작성했다. 처음 버전에서는 웹팩의 엔트리를 이렇게 작성했다.
module.exports = {
devtool: 'source-map',
entry: {
main: './src/entry/mainVue.js',
news: './src/entry/newsVue.js',
etc: '~~Vue.js'
},
}
화면별로 번들링을 (bundle_[name].js) 하고 화면이 추가될 때마다 직접 webpack.config.js 를 수정해야 했다. 기존 버전에서는 네이티브의 GNB를 사용해 이 같은 방식이 앱의 UX를 해치지 않았지만, 이번에 전면 web으로 개편하면서 여러 문제점에 직면했다.
1. 모든 페이지 HTML를 처음부터 렌더하며 이는 성능에 영향을 끼친다.
2. HTML과 JS는 모듈화가 가능하지만 CSS는 안된다.
3. 빌드 단계가 없어 ES5으로 제한된다.
4. 컴포넌트 전환 간 블링킹(화면깜빡임) 현상이 심하다.
뿐만 아니라 정적 파일들마다 서버에 요청하며 자원을 받는 기존 형식때문에 높은 Network Cost도 문제가 되었다. 안그래도 OTT 미디어 플랫폼 특성상 영상, 이미지와 같은 무거운 컨텐츠의 초기 렌더 시간 및 서비 비용이 문제가 되었는데, 이는 불필요한 부담으로 다가왔고 개선이 필요했다. 이외에도 번들링을 통해 코드 난독화와 최적화, ES6로 작성시 미지원 브라우저에 대한 ES5 변환 지원은 여러모로 큰 이점이다.
SFC와 NPM 도입
이전 버전의 Vue도입은 레거시와의 호환에 집중하며 '돌아가게'만 했다면, 이번에 도입하며 여러 시도를 자체적으로 해보았다. 사실 한달 남짓되는 기간동안 관리자 툴에 앱까지 완성해야 되는 상황에서 녹록치 않았는데, 그 사이 버전을 두개를 갈아치웠다. 최종적으로 SFC(Single File Component) 개발 환경과 SPA의 형태를 갖춘 앱 아키텍쳐가 완성되었다. CSS를 로컬 컴포넌트 별로 생성해 퍼블리셔와의 협업도 꾀하면서 컴포넌트별로 나누니 올림픽, 선거등의 이벤트 시에도 유지보수가 용이했다.
또한 화면 GNB 클릭시 컴포넌트간 mount시에도 블링킹 현상이 사라졌으며, 성능 또한 1.5초 -> 0.8초로 2배정도의 성능 향상을 이루었다. 뿐만 아니라 입증된 유명 npm 외부 라이브러리 도입으로 비교적 안정적인 서비스 운영에 도움을 받을 수 있었다. 로딩스피너나 무한 스크롤, lazy-loading, debouncing(v1.0에선 직접 구현했던 부분) 등에서 간단하고 직관적으로 접목시킬 수 있었다.
다만 데이터 중심의 화면 개발 지향임에도 불구하고 강력한 jQuery 레거시와 퍼블리셔와의 협업, swiper나 slider와 같은 플러그인으로 인해 Dom의 직접적 제어는 불가피했다. 실제로 호환시킬때 LifeCycle에서 정확한 시점을 잡기도 어려운 경우가 많았고, Dom을 건드릴 수록 원인을 알수 없는 버그가 무수히 쏟아졌다. 이번 버전을 진행하며 불가피하게 Vue에서 제공하는 Dom 제어시 사용하는 refs 속성을 이용해 Dom에 접근하거나, 커스텀 디렉티브를 만들어 써드파티 라이브러리들을 이용하게 되었다.
<template>
<input type="search" placeholder="검색어" v-model='keyword' ref="search"/>
</template>
<script>
data(){
return{
keyword = "",
}
},
..//중략
this.$refs.search.blur();
</script>
IOS의 경우 검색창의 Focus를 remove해야 키패드가 제거되는 등의 이슈가 있기 때문에 이에 따라 DOM을 직접적으로 건드릴 필요가 존재했다. 이를 위해 ref를 지정해줘 직접적으로 focus를 제거했다.
결론적으로 이 둘의 호환은 가능하나, 애초에 지향하는 철학이 다르기 때문에 확실한 것은 당장이 아니더라도 jQuery 레거시는 점진적으로 걷어내야 한다는 것이다. 유지보수 및 개발이 힘들 뿐더러 컴포넌트 사용에도 제약이 생긴다. 개인적으로는 앞으로 Dom-Frist 방식을 벗어나 Data-First 방식의 개발을 계속해서 해 나갈 것이다.
다음은 여러 문제점 해결 및 변화에 대한 내용이다.
코드 스플리팅
SPA(Single Page Application)의 특성상 라우트 변경으로 페이지가 변경되더라도 Network 자체는 아무 변화가 없다. 그렇기 때문에 최초 사이트에 접속할 때 모든 webpack build 결과물을 렌더하게 되고, 이는 앱의 모든 페이지 정보가 app.js 내 존재한다는 것을 의미한다. 이렇게 되면 첫 렌더시 모든 정보를 한번에 로딩해 오게 된다. 첫 렌더가 오래걸리면 걸릴수록 사용자의 이탈율은 증가하게 된다.
결론적으로 이 같은 경우 main 페이지라면 main 페이지만 먼저 들어오고, 나머지 페이지들은 해당 컴포넌트로 이동시 들어오게 하는 코드스플리팅은 필수적이다. 적용한 코드는 다음과 같다.
Vue.use(VueRouter)
export default new VueRouter({
routes: [
{
path: '/' ,
component: () => import('../src/components/main.vue'),
props:{
userAgent: mobileStatus(),
},
},
{
path: '/news',
component: () => import('../src/components/news.vue'),
},
{
path: '*',
component: () => import ('../src/components/error.vue'),
},
]
})
path: '*' 에서와 같이 error 페이지 라우터 처리도 간단히 적용할 수 있었다.
무조건적으로 코드스플리팅의 성능이 좋은 것은 아니니 비교해보고 적용하면 될 것같다.
ES6 사용
Vue SFC 개발환경에서 위의 언급한 문제로 기존 사내 레거시를 100%는 아니어도 어느정도 정리하며 프로젝트를 진행했다. 가장 큰 변화는 ES6 도입에 있다. 이전까지는 ES6 미지원 브라우저에 대한 문제로 ES5에서 반 강제적으로(?) 머물러 있었는데, 이번에 정책이 바뀌면서 ES6를 도입해 보자는 사내 의견이 있었고 처음으로 도입하게 되었다.
아직 완벽한 것은 아니지만 ES6를 도입하며 여러 장점을 얻을 수 있었다.
1. 기존 JS 파일들을 모듈별로 나누고 깔끔해진 코드 용이해진 유지보수
2. const와 let 지원으로 변수나 상수 구분 용이
3. class 문법 지원과 Arrow Function 지원(()=>)
4. Export, Import를 통해 모듈 이용 용이
5. 비동기 프로세싱 Promises 지원
화살표 함수를 받아 기존보다 훨씬 깔끔해진 코드를 얻을 수 있었고, 비동기로 데이터 받아오는 함수에서 기존 var vm = this 와 같이 우회적으로 스코프를 돌려쓰던 형식에서 벗어날 수 있었다. 뿐만아니라 함수를 export해 여러 곳에서 사용할 수 있는 재사용성이 월등히 좋아졌다.
이번 프로젝트에서는 영상에 대한 화면 미리보기 지원에서 영상의 play 여부에 따라서도 여러 효과를 주는데 Promise를 통해 간결하게 표현할 수 있었다.
setTimeout(() => {
var playPromise = that.player.play();
if(playPromise !== undefined) {
playPromise.then(() => {
//영상 없음
}).catch((error) => {
console.log(error);
});
}
}, 300)
하지만 자연스럽게 기존 ES5에 익숙해져있는 습관과 화살표 함수에 대한 거부감으로 아직은 좀 더 익숙해 져야 할 것 같다. 분명한것은 앞선 장점들이나 Destructing과 같이 ES6는 코드 가독성과 간소화에 큰 이점이 있다는 점이다.
PWA
전면 웹뷰로 전환하면서 PWA를 도입하자는 의견이 있었는데 결과적으로 하이브리드앱 형태로 가게 되었다. 다만 웹뷰로써 PWA의 핵심 개념은 가져가기로 했고 그에 따라 짧은 시간 내 많은 버전 업과 아키텍쳐 변동이 있었다. 다음은 구글 개발자 사이트의 PWA 메뉴 정신이다.
1. Reliable - Load instantly and never show the downasaur, even in uncertain network conditions.
1. 신뢰성 : '불안정한 네트워크 조건에서도 페이지 로딩이 즉각적이고 공룡(크롬 에러페이지)을 보여서는 안된다'
2. Fast - Respond quickly to user interactions with silky smooth animations and no janky scrolling.
3. Engaging - Feel like a natural app on the device, with an immersive user experience.
2. 신속성: 사용자 상호작용에 부드러운 애니메이션 효과와 버벅임 없는 스크롤로 즉각적으로 반응해야한다.'
3. 참여성: '몰입형 사용자 경험(immersive user experience)으로 디바이스의 원래 앱처럼 느끼게 한다.'
웹 브라우저 환경과 모바일 환경은 전혀 다른 환경이다. 하드웨어의 성능 부터 기기별 화면 크기, IOS와 AOS간 차이 등 지원하는 형태가 다르고 일정한 포맷이 없다. video를 실행하는 방법부터 select의 작동 방식, 키패드의 작동방식까지 생각지도 못한 이슈가 도사리고 있다. 혼자 개발하는 프로젝트라 기술적 논쟁은 없었지만, 이에 가장 최적화된 라이브러리를 도입하고 커스텀하고, 네이티브 앱과 같은 상호작용과 부드러운 애니메이션 효과를 열악한 하드 성능으로 구동시키는 것이 가장 큰 난제였다. 이에 최적화는 필수적이라고 할 수 있다.
그런 고민의 끝으로 처음의 웹과 같은 모습에서 좀 더 부드러운 애니메이션이 추가되고 빨라진 앱을 만들 수 있었다. 물론 네이티브 앱만큼 최적화가 잘되있다는 것은 아니지만 이 과정에서 한달이라는 데드라인 내 결과적으로 만족할 만한 성과를 거두었다.
결론
프로그레시브한 Vue는 DOM을 직접적으로 건드리는 레거시는 최대한 지양해야 한다. 그렇게 된다면 화면 중심의 개발 방법에서 데이터 중심의 사고로 전환할 수 있다. 이는 개발 시간 단축 및 화면 구성의 복잡한 일련의 과정들을 생략할 수 있는 멋진 방법이다.
SFC의 단점으로 지적된 퍼블리셔와의 협업 불가는 생각보다 할만하다. CSS를 화면별로 분류하는 것과 이미지 번들링, dom 건드리는 부분들만 제외한다면(이는 어느정도 협의가 필요해 보인다).
CLI 형태가 아니더라도 CDN만 스크립트 태그로 받아서 가볍게 사용도 가능하다. 실제로 SPA 프레임워크를 처음 본다면 쉽고 효율적이어서 놀라게 된다.
완벽한 웹뷰 혹은 PWA를 지향한다면 MPA는 지양해야 한다. 본 프로젝트도 MPA로 설계했다가 지나친 화면 깜빡임 및 성능 이슈로 SPA로 대대적 리팩토링을 단행했다 😅
1. 다음 버전은 Vuex를 도입해볼까 하는데, 러닝커브가 짧고 심플해 선택한 Vue가 점점 복잡해지는 과정을 보니, 선배들의 동의가 필요할 것 같다.
2. 진행하는 라디오 쪽 프로젝트에서 React를 선배가 도입한다는데... Vue와 React를 비교할 수 있는 좋은 기회가 될 것같다.
⚡ MBC APP
'Web > Vue' 카테고리의 다른 글
고차 컴포넌트 패턴(HOC)과 믹스인(Mixins) (0) | 2021.12.28 |
---|---|
Vue.js로 점진적 도입 경험 공유 (0) | 2021.07.19 |