본문 바로가기

IT 정보

Vue 3의 대표 신규 기능들 소개 (Vue.js 책추천)

*이 포스팅은 <한 권으로 배우는 Vue.js 3>에서 발췌한 내용으로 작성되었습니다.

 

 

Vue는 버전 2까지 발표되면서 이미 웹 애플리케이션을 개발하는데 부족함이 없는 수준에 올라섰다. 하지만 Vue와 함께 웹 애플리케이션의 주축을 이루는 프레임워크들 역시 발전을 거듭하였고, 상호간의 비교를 통해 Vue의 약점들이 드러났다. 특히 웹 애플리케이션의 개발이 대규모 프로젝트가 되면서 코드의 길이가 길어져 가독성이 떨어지고, 다양한 인력들이 개발하는 수많은 컴포넌트들이 공유하는 데이터의 상태 관리는 Vuex만으로는 점차 감당이 되지 않기 시작했다.

 

 

이러한 문제를 해결하고자 Vue 3는 다양한 새로운 기능들을 가지고 출시됐다. 이미 해당 문제점을 극복한 다른 프레임워크들의 장점을 적극적으로 도입하는 한편 Vue의 장점을 더욱 부각시킬 수 있는 변화들이 소개되었는데, 그중 가장 중요하다고 여길 수 있는 몇 가지를 먼저 소개하고 넘어가겠다.

 

 

컴포지션 API, Suspense, Teleport, 여러 개의 v-model 은 <한 권으로 배우는 Vue.js 3>에서 살펴보고, 오늘은 프록시(Proxies)로 진화된 반응성, Fragments, Emits Option, createRenderer에 대해 설명할 것이다. 

 

 

 

프록시(Proxies)로 진화된 반응성

기존의 Watch 옵션은 자바스크립트 객체(Object)의 속성이 추가되거나 배열(Array)의 아이템이 추가되는 것과 같은 객체 변경에 대해서는 반응하지 않았다. Vue는 내부적으로 객체의 속성을 setter와 getter로 변환하여 반응성을 가지도록 했지만, 미리 정의되지 않은 속성의 추가는 getter와 setter의 호출이 아닌 단순한 아이템 추가이기 때문이다. 이해를 돕기 위해 다음과 같은 객체가 있다고 가정하자.

 

 

이제 item2라는 속성을 추가해보자.

 

 

안타깝게도 기존의 Vue는 item2의 추가를 알아채지 못한다. obj 객체의 루트 속성을 추가하는 작업을 진행했기 때문에 getter/setter의 범위를 벗어나기 때문이다.

 

 

객체 속성의 변경에 따른 과정

 

Vue 3는 컴포지션 API를 통해 데이터를 프록시(Proxies)로 변환하여 사용할 수 있는 방법을 제시한다. 프록시는 ES6에서 소개된 객체로, 데이터와 프레임워크 사이에서 데이터의 전달 및 변환, 관리를 담당한다. item2를 추가하는 예제의 경우, Vue 3는 obj 객체를 프록시 안에 담아두기 때문에 obj에 item2가 추가된 것을 즉시 알아차린다. 이런 프록시의 사용은 Vue 3의 반응성을 훨씬 강력하게 해주지만 오래된 인터넷 익스플로러와 같은 브라우저에 대한 지원이 어려워진 것도 사실이다.

 

Vue 3는 인터넷 익스플로러를 위해 기존에 사용하던 Object.defineProperty를 여전히 제공하고 있으나 프록시가 훨씬 가볍고 빠르기 때문에 프록시를 사용하지 않을 이유는 없다. 프록시를 사용하면 대상 객체는 프록시 객체 내부로 들어가고 프록시의 getter/setter로 관리가 되면서 모든 데이터의 변화에 대해 반응성을 가지게 된다.


Vue 3에서 Proxy를 이용하기 위해서는 ref나 reactive 함수를 이용하면 된다.

 

 

 

Fragments

 

Fragment는 하나의 컴포넌트가 여러 개의 루트 노드를 가지는 것을 말한다. 사실 Vue 2에서도 여러 개의 루트 노트를 컴포넌트에 할당할 수 있었으나 Vue는 컴포넌트에 전달되는 Non-Props 속성을 컴포넌트에 정의된 루트 노드에 전달하도록 설계가 되어 있었기 때문에, 여러 개의 루트 노드를 가지면 어느 노드에 속성을 전달해야 할지 애매해서 버그가 나타날 수 있어 이에 대한 경고를 발생했다. Vue 개발자들은 이 문제를 해결하기 위해 Vue의 템플릿을 생성할 때 하나의 <div> 태그를 두고 그 안에 Html을 작성하는 것을 암묵적으로 받아들였다. 이제 Vue 3에서 더이상 해당 사항을 걱정하지 않고 여러 개의 루트 노드를 가질 수 있다. 다만 Non-Props 속성의 전달이 필요할 경우 어느 노드가 전달을 받을 것인지 명확히 해야 한다.

 

 

 

Emits Option

 

$emit( )은 하나의 컴포넌트가 부모 컴포넌트에게 이벤트를 전달하기 위해 존재하는 함수다. 기존 Vue 버전들은 이 함수를 사용하는데 아무런 제약이 없었다.

 

반면, Vue 3에서는 컴포넌트 옵션 emits를 이용해 전송할 이벤트를 정의할 수 있다. 해당 컴포넌트에서 발생하는 이벤트명을 기술하여 컴포넌트에서 사용하는 이벤트들을 한눈에 볼 수 있는 단순한 기능은 물론, 해당 이벤트의 데이터에 대해서 사전에 검증할 수 있는 기능을 제공한다. 이는 많은 사람들과 협업을 하고 event를 발생해야 할 때, 적절한 문서화가 되어 있지 않을 경우 발생되는 혼란을 최소화할 수 있다.

 

만약 미리 정의된 이벤트명을 emits 옵션에 선언하지 않을 경우, 같은 이름의 네이티브 이벤트가 존재한다면 네이티브 이벤트를 호출한다. 예를 들어, 다음과 같이 emits 옵션을 비워두고 click 이벤트를 발생시키면 Vue의 이벤트가 아닌 네이티브 이벤트로 인식된다.

 

 

네이티브 이벤트라 하면 Vue가 재정의한 이벤트가 아닌 브라우저가 정의한 이벤트를 의미한다. 원래 이러한 네이티브 이벤트를 발생시키기 위해서는 @click 이벤트에 native 수식어(Modifier)를 입력해 @click.native 처럼 작성했다. 이 native 수식어는 더 이상 Vue 3에서 사용하지 않으며, 대신 emits 옵션에 명시되지 않은 이벤트는 모두 native로 처리한다. 6장에서 로그인 컴포넌트를 만들 때 해당 기능을 사용해볼 것이다.

 

 

 

createRenderer

 

Vue 3가 제공하는 runtime-dom과 runtime-core 패키지는 사용자가 렌더링의 동작을 정의할 수 있게 해주는 createRenderer라는 함수를 제공한다. 이 함수는 Host 환경의 Node와 Element를 제네릭 인자로 받아 해당 노드와 엘리먼트의 렌더링 동작을 변경할 수 있게 해준다.

 

렌더링 동작을 변경할 수 있게 한다는 것은 Renderer가 가지는 CRUD(Create, Read, Update, Delete)에 관련된 함수들을 재정의한다는 것을 의미한다. createRenderer 함수는 하나의 객체를 인자로 받고, 해당 객체 내에는 개발자가 재정의하고자 하는 함수가 선언되어 있으면 된다.

 

예를 들어 insert라는 함수는 해당 엘리먼트가 삽입될 때 호출되는 함수며, createTextNode는 시작태그와 종료태그 사이에 들어가는 텍스트 노드를 생성할 때 호출되는 함수다. 만약 insert 함수를 재정의하고 싶다면 다음과 같은 방법을 이용할 수 있다.

 

 

 

 

대규모 프로젝트가 아니어도 프로젝트를 진행하면서 Vue의 Options API는 편리하면서도 불편했다. 하나의 짧은 컴포넌트를 작성함에 있어서 Options API는 매우 편리한 API를 제공하였지만, 컴포넌트의 규모가 커지면서 관리가 매우 힘들어지는 단점도 있었다. 아울러 다른 프레임워크에서 잘 쓰는 기능을 사용하기 위해 서 호환 가능한 서드파티 라이브러리를 찾는 일도 꽤나 번거로운 일 중 하나였다. Vue 3는 서드파티에서 사용되던 장점들을 기본 라이브러리로 흡수하고, 타 프레임워크의 장점들을 겸허히 받아들인 겸손하면서도 강력한 프레임워크라 칭하고 싶다. 개발하고자 하는 애플리 케이션의 규모가 작을수록 컴포지션 API의 위력을 느끼기는 힘들다. 이럴 경우에는 이미 익 숙한 Options API를 사용하면 된다. 다만, 프로젝트 규모가 커지고 같이 개발하는 개발자가 늘어난다면 컴포지션 API를 통해서 기능을 나누고 해당 기능의 함수를 API화하여 상호 호 출하여 사용하는 마이크로서비스 형태를 취함으로 개발의 편리성과 안정성을 높일수 있다.

 

《한 권으로 배우는 Vue.js 3》

교보문고 / 예스24 / 알라딘

반응형