Vue, 원피스로 떠나는 여정

안녕하세요. IT연구소 UI개발2팀 안수미입니다.

UI개발팀은 웹 접근성과 함께 웹 표준에 맞추어 사용자가 서비스를 이용하며 접하는 UI를 구현합니다. 빠질 수 없는 HTML, CSS, ECMAScript는 물론이고 Vue.js를 활용하여 프로젝트를 진행합니다.

이번에는 Vue3를 다루게 되어 공유하고자 정리해보았습니다. 기존에 사용하던 Vue2와 무엇이 다른지, 도입하기 전에 고려할 사항으로 어떤 게 존재하는지 나누고자 합니다.

Vue3?


<img src="https://saramin.github.io/img/hhp/hhp_vue3_content_01.png" />
Vue3 원피스

Vue3는 작년, 2020년 9월 18일에 정식으로 릴리즈하였습니다. Vue 창시자인 에반 유는 버전 네이밍으로 원피스라 이름을 지었습니다. 실무에 활용하는 기술이니만큼 반가운 소식에 공식 사이트로 바로 달려갔었습니다. 새롭게 추가된 method와 기능도 다양했지만, 반대로 사라진 문법들이 즐비했습니다. 처음 접하였을 당시에는 프론트 개발자라면 당연히 신경 써야 할 크로스 브라우징과 안정성을 고려할 수밖에 없어 한동안 Vue3 도입을 보류했었습니다.

새로운 버전이 등장한지 1년이 지났으니 언제까지고 Vue2에 머무를 수는 없습니다. 마침 Vue3을 도입할 만한 프로젝트에 참여하게 되어 조금 더 빨리 실무에 적용해보았습니다. 그동안 문서로만 보던 Vue3을 도입하면서 몸소 겪은 변화에 대해 함께 살펴보도록 해요. 보다 수월하게 Vue3을 접근할 수 있도록 개인적인 의견을 첨언하여 공유드립니다.

Vue2 vs Vue3


<img src="https://saramin.github.io/img/hhp/hhp_vue3_content_02.png" />
Vue3 작업된 프로젝트 예시 화면

Vue3를 기반으로 코드를 작성하면서 가장 많이 직면한 문제는 문법의 변화입니다. 새로운 문법이 추가되고 바뀌면서 Vue 성능도 향상되었습니다.

성능

Vue 공식 블로그에서 소개한 설명 중에서 눈에 띄는 내용이 하나 보입니다. “더 빠르고 더 가벼워졌다”라는 문구로 이번 프로젝트에서 Vue3를 사용하게 된 가장 큰 이유입니다.

가상 DOM 병목 현상

그중에서 가장 큰 변화는 오버헤드를 유발했던 불필요한 가상 DOM 트리 탐색이 제거되었다는 사실입니다. 그 덕분에 Vue3는 Vue2에서 고질병이라 여겼던 병목 현상을 극복합니다. Vue2는 전체 DOM을 동적인 템플릿과 정적인 템플릿을 모두 포함하여 전체 노드를 비교하여 업데이트를 판단하였죠? Vue3는 변경되지 않는 정적인 템플릿에 대해 가상 DOM 비교를 제외하면서 업데이트 시 발생하는 오버헤드를 줄였습니다.

트리 쉐이킹 강화

트리 쉐이킹은 모듈을 번들링 하는 과정에서 사용하지 않는 코드를 제거하는 최적화 방안입니다. Vue3은 실제로 사용하는 코드만 남겨 번들 크기를 줄입니다. 실제로 사용되는 기능만 Import한 다음 코드를 생성합니다. 공식 문서도 해당 내용을 뒷받침하기 위하여 번들링에 대한 자료를 제공합니다.

<img src="https://saramin.github.io/img/hhp/hhp_vue3_content_02.png" />
Vue2 vs Vue3 번들 크기 비교(출처: Vue.js 공식블로그)

문법

Vue3에 대한 문법을 함께 살펴볼까요? 크게 다섯 가지로 요약해서 정리해 봤어요. 이해하기 쉽도록 기존 Vue2 문법과 함께 비교하여 설명을 풀어 적었습니다.

Composition API

첫 번째는 코드 재사용성을 높이기 위한 Composition API입니다. Vue3로 올라가면서 Composition API가 core로 적용되었습니다. 기존과 달리 setup 함수 안에서 커스텀 하게 코드를 작성하도록 바뀌었습니다. Vue2는 Vue에서 제공하는 API로 구분 지어 코드를 작성했습니다. 혹여나 Vue2로 Composition API 라이브러리를 사용하셨다면 차이라고 느끼지 못하실지도 모르겠네요.

Vue2

...
export default {
 props: {
  user: {
   type: String,
    required: true
  }
 },
 data() {
  return {
   state: {
    type: 'compositon'
   }
  }
 },
 created() {
  console.log(this.user)
 }
}

Vue3

...
export default {
 props: {
  user: {
   type: String,
   required: true
  }
 },
 setup(props) {
  const state = reactive({
   type: 'compositon'
  })
  console.log(props.user)
  return {
   state
  } 
 }
}

Fragment

두 번째 변화는 Vue 템플릿 최상단을 하나의 태그가 묶지 않아도 됩니다. 실제 DOM 트리에서 렌더링 되지 않는 Fragment 컴포넌트가 추가되어 여러 태그를 같은 선상에 배치할 수 있도록 변경되었습니다. Vue2는 템플릿에 하나의 DOM만 사용이 가능해서 불필요한 태그를 하나 더 삽입하였는데, 더 이상 그럴 필요가 없습니다.

Vue2

<template>
 <div id="wrap">
  <header>...</header>
  <main>...</main>
  <footer>...</footer>
 </div>
</template>
...

Vue3

<template>
 <header>...</header>
 <main>...</main>
 <footer>...</footer>
</template>
...

Teleport

세 번째는 Teleport로 알림 창이나 경고 창, 모달을 그릴 때 렌더링 하는 DOM이 아닌 특정 DOM을 연결하여 호출하도록 바뀌었습니다. Vue2는 모달을 관리하는 컴포넌트를 생성해서 각각 페이지단 뷰에 연결해 줘야만 했습니다. 위치에 따라 하나하나 관리를 해줘야 하는 영역이라 번거로웠습니다. 이제는 특정 DOM으로 연결함으로써 위 문제를 해결할 수 있습니다. Vue2에서 이 문제를 피하려면 portal-vue 라이브러리를 이용해야만 합니다. 리액트를 사용하셨다면 Portal과 같은 기능이라 보면 쉽게 이해되실 거에요.

Vue2

<template>
 <div id="page-wrap">
  <div id="page-teleport">
   <div>modal</div>// 모달 창 마크업 및 스타일링
  </div>
 </div>
</template>
...

Vue3

<!DOCTYPE html>
<html lang="ko">
 <body>
  <div id="app"></div>
  <div id="teleport"></div>
 </body>
</html>
...
<teleport to="#teleport">
 <div>modal</div>// 모달 창 마크업 및 스타일링
</teleport>

Reactivity API

네 번째로 Reactivity API를 이용하여 초기 세팅한 속성 외에 데이터를 추가하고 이를 감지할 수 있습니다. Vue2는 data 메서드에 세팅한 데이터 이외에 속성을 추가할 수가 없어서 Vue. set 메서드를 이용하여 강제 주입했습니다. Vue3에선 Reactivity API를 활용하여 추가적인 제어가 필요 없습니다. 이번 프로젝트를 진행할 때 참조형 데이터는 Reactive를 사용하고 원시형 데이터는 ref로 이용하여 반응형 데이터를 관리했습니다.

Vue2

...
export default {
 data () {
  return { 
   state: { titleList : '' }
  }
 },
 created() {
  this.state.utilList= 2;// 속성 추가 불가
  this.$set( this.state, 'utilList', '' );// 속성 추가
 }
}

Vue3

...
export default {
 setup(props) {
  //참조형 데이터
  const state = reactive({
   titleList: [
    {
     value: "",
    },
   ],
   companyList: [
    {
     value: "",
    },
    {
     value: "",
     color: "grey050",
    },
   ],
   utilList: [
    {
     type: "icon",
     icon: "favorite",
     iconButton: true,
    },
   ]
  })
  //원시형 데이터
  const status = ref('crew');
  state.newState='안녕하세요'; // 속성 추가
  return {
   state,
   status
  } 
 }
}

Suspense

마지막으로 Suspense를 살펴봅시다. Suspense는 비동기 구성요소가 포함되어 하위에 두 개의 슬롯이 존재하고, 그중 하나의 슬롯에 포함된 노드만 노출합니다. 콘텐츠 영역인 경우 데이터가 준비되면 default 슬롯을 노출하고, 문제가 발생하여 정보를 가져올 수 없을 때 fallback 슬롯을 노출하도록 설정이 가능합니다.

하지만 이번 프로젝트에서 사용하지 않았습니다. 공식 문서를 살펴보면 실험적인 API로 언제든지 변경될 수 있다는 내용이 포함된 기능이거든요. 이를 사용 서비스 플랫폼에 사용하기에는 불안정하여 적용하지 않았습니다.

Vue2

<template>
 <div id="page-wrap">
  <div v-if="show">
   <todo-list @show="onShow"/>// default
  </div>
  <div v-else>
   Loading...// fallback
  </div>
 </div>
</template>

<script>
import TodoList from "./TodoList";
export default {
 components: {TodoList},// 비동기 통신 포함된 컴포넌트
 data () {
  return { 
   show: false;
  }
 },
 methods(){
  onShow: function(){
   this.show = true;// 비동기 통신 정상 응답 Flag
  }
 }
}
</script>

Vue3

<template>
 <suspense>
  <template #default>
   <todo-list />
  </template>
  <template #fallback>
   <div>
    Loading...
   </div>
  </template>
 </suspense>
</template>

<script>
import TodoList from "./TodoList";
export default {
 components: {TodoList}// 비동기 통신 포함된 컴포넌트
}
</script>

Vue2와 Vue3 차이점을 한눈에 보기 좋도록 정리해 보았습니다. Vue 버전이 올라가면서 성능뿐만 아니라 개발자를 불편하게 만들었던 영역들이 하나씩 개선되었습니다. Vue2에 널리 사용되었던 라이브러리는 core에 편입되었습니다. Vue 3에 관심이 생겼다면 공식 문서를 자세히 살펴보는 건 어떨까요?

Vue3 Start!


Vue3 대표적인 기능들을 맛보기로 살펴보았습니다. 이 글을 보고 Vue3를 이용하여 프로젝트를 작업하고 싶으신 마음이 드실지도 모르겠습니다. 하지만 실무에 적용하실 계획이라면 잠시 멈춰주세요. Vue 3을 도입하기 전에 고려해야 할 사항을 짚고 넘어가야 해요.

먼저 프로젝트가 지원할 크로스 브라우징 범위를 확인해 주세요. Vue2 익스플로러 대응 정도는 아니더라도 ‘IE11까지 적용하겠지’라는 생각으로 접근하시면 안 됩니다. Browserslist로 환경설정하면서 테스트하다가 지원되지 않아 놀랐었습니다. Vue3는 공식적으로 익스플로러를 지원하지 않아요. 익스플로러에 대응해야 하는 프로젝트라면 부적합하니 Vue2를 사용해야 합니다.

또한 Vue2에서 적용했던 라이브러리 를 그대로 사용하려면 버전 유지를 고려해 주세요. 기존 활용하던 라이브러리들이 아직 Vue3을 지원하지 않는 경우가 꽤 많습니다. 무엇보다 아직 생태계가 자리 잡지 못하여 라이브러리도 한정적이에요. 이와 같은 상황에서 Vue3을 강제로 올리고자 한다면 이슈가 생길 여지가 많습니다. 이번 프로젝트에 적용한 slide 라이브러리는 Swiper.js만 Vue3를 지원하여 다른 라이브러리는 고려 대상이 아니었습니다. 😢

마치며


실무에 Vue3을 적용하면서 새로운 변화에 대해 깊이 살펴보는 계기가 되었습니다. 실제로 적용해보니 아직 B2C, C2C 프로젝트로는 부적합하다는 결론을 내렸습니다. 가장 큰 이유는 한정적인 브라우저 호환성에 대한 이슈입니다. Vue3에서는 IE를 적용하지 않습니다. IE 지원이 요구사항인 프로젝트는 보수적으로 접근하셔야 합니다.

만약 IE 지원이 요구사항으로 포함되어 있지 않다면 성능적인 우위와 함께 코드 편의성이 증대된 Vue3도 좋은 선택으로 다가올 겁니다. B2B 어드민이나 토이 프로젝트에서 한 번 Vue3을 적용해 보는 건 어떨까요? 더 빨라지고 가벼워진 Vue3를 누구보다 먼저 느껴봅시다.

Vue의 무궁무진한 발전을 기대하며 지금 원피스로 떠나봅시다.

참고