FrontEnd/Vue

[Vue 3] 빠르게 정리하기

Grace 2023. 2. 20. 23:44

Vue.js는 MVVM(Model-View-ViewModel) 패턴을 표방하고 있지만 코어 라이브러리는 ViewModel에 집중되어 있습니다.
코드의 재활용성이나 속도에 큰 장점이 있는 단일 페이지 어플리케이션(SPA: Single Page Application)을 개발할 때 Router와 같은 공식 라이브러리를 이용할 수 있으며, 여러 컴포넌트들이 상태를 유지하고 데이터를 공유하기 위해서 Vuex와 같은 라이브러리를 이용할 수 있습니다.
Vue는 가상노드(VNode)를 생성하는 해당 노드에 먼저 모든 DOM을 구성한 후 브라우저에게 최종 DOM 엘리먼트 변경을 통지함으로써 속도 저하를 방지했습니다.

하지만, 개발이 지속되면서 코드의 길이가 길어져 가독성이 떨어지고, 수많은 컴포넌트들이 공유하는 데이터 상태 관리는 Vuex만으로는 점차 감당이 되지 않기 시작했습니다.
이러한 문제를 해결하고자 Vue 3 에서는 다양한 새로운 기능들을 가지고 출시됐습니다.

기존의 Vue2는 Options API를 기반으로 하나의 객체를 하나의 모듈로 만들어 컴포넌트라 칭하는 라이브러리였습니다. 하지만 하나의 변수와 연관된 수많은 기능들이 하나의 SFC(Single FIle Component)에서 이곳 저곳에 산포되기 시작했고, 결국 가독성을 떨어뜨리고 코드 유지 비용이 높아지는 문제점을 보이기 시작했습니다.
컴포지션 API는 컴포넌트를 작성할 때 함수 기반의 방법을 제시합니다. 이로 인해 특정 역할에 따른 함수의 분리 등을 통하여 훨씬 가독성 높고 잘 조직화된 코드를 만들 수 있게 해주며, 은닉화된 코드는 컴포넌트들이 재활용할 수 있습니다.

Suspense는 컴포넌트가 데이터를 받아오기 전까지 기본 컨텐츠를 표시할 수 있는 기능입니다. Skeleton UI를 예시로 들 수 있는데 기존 Vue2에서는 이러한 구현을 위해 v-if, v-show와 같은 디렉티브를 사용했습니다. suspense는 비동기적 컴포넌트의 로딩이 완료될 때까지 컴포넌트를 그리는 방법을 제시하기 때문에 이러한 문제를 매우 손쉽게 해결할 수 있지만, 실험적 요소로 개발중인 상태이기 때문에 어떻게 변할지 모르므로 정확한 정의가 이뤄질 때까지는 사용하지 않기를 권장합니다.

UI와 관련 동작을 은닉화하여 컴포넌트로 만들어 사용할 때, 컴포넌트가 다른 컴포넌트에 중첩되면서 상대적 위치 문제로 UI 렌더 시 스타일에서 에러가 발생할 수 있는데, 이러한 문제를 해결하고자 개발자들은 portal-vue라는 서드파티 플러그인을 이용하곤 했습니다. 하지만 Vue3에서는 어느 컴포넌트든 자신이 렌더링하고 싶은 위치가 있다면 Teleport를 이용해 쉽게 구현할 수 있습니다.

v-model 디렉티브는 양방향 결합 모델입니다. 기존에는 하나의 컴포넌트에 하나의 v-model 디렉티브만을 가지는 것을 허용하였기 때문에 v-bindv-on을 함께 이용했으나, 이제 여러 개의 v-model 디렉티브를 하나의 DOM 엘리먼트에 할당할 수 있습니다.

// 기존의 방식
<MyComponent v-bind:param1 v-on:update="updateParam1" v-bind:param2 v-on:update="updateParam2" />

// 현재의 방식
<MyComponent v-model="param1" v-model="param2" />

기존의 Watch 옵션은 자바스크립트 객체의 속성이 추가되거나 배열의 아이템이 추가되는 것과 같은 객체 변경에 대해서는 getter/setter의 범위를 벗어나기 때문에 반응하지 않았습니다.
Vue3에서는 컴포지션 API를 통해 데이터를 프록시로 변환하여 사용할 수 있는 방법을 제시합니다. 프록시를 사용하면 대상 객체는 프록시 객체 내부로 들어가고 프록시의 getter/setter로 관리가 되면서 모든 데이터의 변화에 반응성을 가지게 됩니다. 이를 위해서 refreactive 함수를 이용하면 됩니다.

const obj = reactive({})
obj.item1 = 2

Vue3에서 fragment는 하나의 컴포넌트가 여러 개의 루트 노드를 가지는 것을 말합니다. 다만 Non-Props 속성의 전달이 필요한 경우 어느 노드가 전달을 받을 것인지 명확하게 해주어야 합니다.

$emit()하나의 컴포넌트가 부모 컴포넌트에게 이벤트를 전달하기 위해 존재하는 함수입니다. 해당 컴포넌트에서 발생하는 이벤트명을 기술하여 컴포넌트에서 사용하는 이벤트들을 한 눈에 볼 수 있는 단순한 기능은 물론, 해당 이벤트의 데이터에 대해서 사전에 검증할 수 있는 기능을 제공합니다. 미리 정의된 이벤트명을 emits 옵션에 선언하지 않을 경우, 같은 이름의 네이티브 이벤트가 존재한다면 네이티브 이벤트를 호출합니다.

runtime-dom과 runtime-core 패키지는 사용자가 렌더링의 동작을 정의할 수 있게 해주는 createRenderer라는 함수를 제공합니다. 이 함수는 Host 환경의 Node와 Element를 제네릭 인자로 받아 해당 노드와 엘리먼트의 렌더링 동작을 변경할 수 있게 해줍니다. 렌더링 동작을 변경할 수 있게 한다는 것은 Renderer가 가지는 CRUD에 관련된 함수를 재정의하는 것을 의미합니다.