Vue 3.0과 Composition API

호박고구마·2022년 7월 19일
0

지금까지 회사에서 주로 사용해본 Vue.js 버전은 2.0 기준이었다.
2020년 9월 Vue 3.0이 등장했고 드디어 2022년 2월 Vue.js의 기본 버전이 3버전으로 설정되었다.
이번에 회사에서 Vue 3.0를 주제로 발표 준비를 하면서 짧게 학습한 Vue 3.0의 컨셉과 API 등을 정리해보려 한다.


Vue 3.0의 특징

  1. 가상 DOM 최적화
  2. 트리 쉐이킹 강화
  3. Composition API의 공식화
  4. Typescript 지원 강화
  5. Teleport
  6. Suspense
  7. 그 외 템플릿 표현식 수정 및 추가 제공, class 및 style 바인딩 변경 등등

Vue 공식 블로그에 따르면 이런 특징들로 인해 Vue 2.0과 비교했을 때 번들 사이즈가 41% 줄어들었고 렌더링이 55%가 빨라졌고 메모리 사용이 54% 줄어 들었다고 한다.

결론적으로 이전보다 업그레이드 되어서 퍼포먼스가 향상되었다는 것인데, 사실상 핵심은 React.js의 변화에 발맞춰 변경된 것이라고 볼 수 있다.

React의 변화

그렇다면 React.js가 어떻게 변화했길래 Vue.js가 이에 발맞춰서 버전까지 바꾼걸까?
React.js 는 이미 2019년 6월에 공식적으로 Hook을 도입했고 함수형 컴포넌트로 개발하기를 권장하고 있다.
이러한 흐름은 결국 기존의 클래스 컴포넌트가 가진 단점(재사용성이 좋지 않고 가독성이 좋지 않으며 코드 길이가 길다 등등)을 극복하고 Hook을 통해 로직의 재사용성을 높이고 유지 보수가 용이하게 만들기 위한 것이다.
이런 맥락에서 Vue 3.0은 기존의 React Hook의 장점은 가져가되 단점은 보완하는 방식으로 만들어진 것 같다.
결국 Composition API를 기반으로 한 함수형 컴포넌트가 Vue 3.0의 핵심인 것 같다.

Composition API

React의 Hook의 역할을 대신하는 API로, Reactivity(반응성)과 컴포넌트의 라이프 사이클을 관리하고 의존성을 주입하는 등의 역할을 한다.
Composition API를 통해 기존의 Options API에서 관리하던 data, props, computed, watch, methods, 라이프 사이클 등을 setup 함수 내에서 한번에 관리할 수 있다. 또한 관심사와 로직을 분리해서 작성할 수 있기 때문에 공통되는 로직의 경우 하나의 파일에서 import 해서 재사용할 수도 있다.

공식 문서에 따르면 위의 코드 화면 처럼 Composition API를 사용하면 Options API 보다 코드 양도 줄고 가독성도 높다.

Composition API의 이점

  1. 데이터, 로직 목적별로 관심사를 분리할 수 있음
  2. 불필요한 코드양을 줄여 가독성을 높임
  3. 반복되는 코드들을 한 곳에서 관리해 재사용성이 높아지고 유지 보수 용이
  4. Typescript와 함께 사용하면 이점 극대화
  5. React Hook의 단점 보완
    (컴포넌트가 변경될 때 마다 함수를 재실행하는 React와 달리 Composition API는 setup 함수가 실행될 때, 즉 컴포넌트가 생성될 때 최초 한 번 실행된다. 따라서 불필요한 렌더링을 줄일 수 있다.)

Composition API의 setup()

setup() 함수는 Composition API의 시작점으로 state로 관리할 반응성, 라이프 사이클 훅, props 등을 반환한다.

Composition API의 State 관리

기존의 Options API에서는 아래와 같이 state를 data 객체로 묶어 반환했다.

<template>
  <div>
    <span>{{ name }}</span>
    <span>{{ count }}</span>
    <button @click="count++">카운트 증가</button>
  </div>
</template>
<script>
export default {
	data () {
    	return {
        	count: 0,
            name: '이름'
        }
    }
}
</script>

Composition API에서는 state 두가지 방식으로 관리한다.

  1. ref()
  • Options API의 data와 비슷하다고 보면 된다. state와 변경 가능한 객체를 반환해 실제 값을 변경하고자 한다면 .value라는 속성으로 접근해 변경할 수 있다.
  1. reactive()
  • 원시형 데이터 타입(String, number)에는 반응성을 가지지 않고 주로 객체, 배열에 사용

주로 ref()를 사용하는 걸 추천하는 듯 한데 두 API의 차이점에 대해서는 나중에 한번 더 학습해봐야겠다.

아래는 ref와 reactive를 사용해 state를 관리한 예시이다.

<script>
import { ref, reactive } from 'vue'
export default {
  setup() {
  	const count = ref(0);
    const obj = reactive({ name: '' });
    obj.name = '고구마';
    return {
    	count,
      	...obj
    }
  }
}
</script>
<template>
  <div>
    <span>{{ name }}</span>
    <span>{{ count }}</span>
    <button @click="count++">카운트 증가</button>
  </div>
</template>

Composition API의 라이프 사이클

라이프 사이클은 Options API와 큰 틀에서 보면 크게 다르지 않다.
라이프 사이클은 크게 4가지로 나뉜다.

1. 컴포넌트 생성 단계
2. 실제 DOM에 마운트 되는 단계
3. state가 변경되는 단계 
4. 컴포넌트가 DOM에서 사라지는 단계

Options API와 Composition API의 라이프 사이클 훅은 다음과 같다.

만약 Vue2를 사용한다면 컴포넌트가 DOM에서 사라지기 전 호출되는 beforeDestroy() 와 DOM에서 사라지고 나서 호출되는 destroyed() 훅이 beforeUnmount(), unmounted()로 변경된 것을 볼 수 있다.

약간 헷갈릴 수 있는 점은 setup()의 경우 컴포넌트 생성 단계에 해당하는 beforeCreate(), created() 라이프 사이클 훅이 없다는 점이다.
공식 문서에 따르면 setup() 함수가 beforeCreate와 created 라이프 사이클 훅 사이에 실행되므로 해당 라이프 사이클에서 작성할 로직들을 setup() 함수 안에 정의하면 된다.

<script setup>으로 Composition API 쓰기

일단 여기까지 보면 기존의 Vue2 보다 코드양이 훨씬 적어진 걸 볼 수 있다. 기존의 Vue2는 class 컴포넌트 기반인데다가 data, props, computed, methods를 각각 객체로 한땀한땀 정의해야만 해서 보일러 플레이트성 코드가 길다는 단점이 있었다.
그런데 Vue 3.0은 여기서 더 나아가 더 짧고 간단하게 Composition API를 사용할 수 있는 방법을 소개한다.
기존에 사용하던 <script> 태그 대신 <script setup> 을 사용하면 굳이 setup 함수를 선언하지 않아도 알아서 setup 함수 형태로 반환해준다. 누구나 쉽고 간단하게 개발할 수 있게 만든다는 Vue의 철학이 엿보인다.

아래는 부모 컴포넌트로부터 props를 받고 부모 컴포넌트에 emit 하는 형태의 자식 컴포넌트 코드이다.

<script setup lang="ts">
import { ref, computed, defineProps, defineEmits } from 'vue'
interface Props {
  name: string
  birth: string
}
interface EmitProps {
  (e: 'edit', name: string): void
}
const props = defineProps<Props>()
const emit = defineEmits<EmitProps>()

const newName = ref('')
const age = computed(() => {
  return props.birth ? 2022 - Number(props.birth) : ''
})
const edit = () => {
  emit('edit', newName.value)
}
</script>
<template>
  <div>
    <div>
      <p> 이름 : {{ name }}</p>
      <p> 연도 : {{ birth }}</p>
      <p> 나이 : {{ age }}</p>
    </div>
    <input v-model="newName" label="새로운 이름" />
    <btn @click="edit">수정</btn>
  </div>
</template>

setup() 함수를 따로 정의하고 사용하는 값들을 return 할 필요가 없어서 코드가 보다 간결하다.
또한 props 와 emit 도 typescrip로 정의한 후 defineProps(), defineEmit() 등의 API로 접근 가능해서 가독성도 좋다.

결론

미루던 Vue 3.0 학습을 사내 발표를 계기로 공부하게 되었는데 생각보다 실무에 적용하면 좋은 장점들이 많이 보였다. 불필요한 코드양도 줄일 수 있고 가독성도 높아서 복잡한 로직을 이해하고 구현하는 데 효과적일 것 같다.
특히 Typescript와 함께 쓰면 그 이점이 더 배가 될 것 같다.
결국 형태는 다르지만 Vue 3.0의 본질은 React Hook인 듯 하다. 공식 문서에서도 React Hook에 영감을 받아 Composition API를 만들었다고도 하니 뭐..
과연 Vue에서 주장하는대로 React 보다 퍼포먼스가 더 좋을지는 실제로 프로젝트를 진행해봐야 판단할 수 있을 것 같다.

참고

https://vuejs.org/guide/extras/composition-api-faq.html#relationship-with-class-api
https://joshua1988.github.io/web-development/vuejs/vue3-as-default/
https://www.youtube.com/watch?v=Hr_Rx9N1hzw
https://blog.vuejs.org/

3개의 댓글

comment-user-thumbnail
2022년 10월 12일

정말 유용한글인것같아요~!도움많이되었습니다!!

답글 달기
comment-user-thumbnail
2022년 10월 12일

퍼가요~♥

답글 달기
comment-user-thumbnail
2022년 10월 12일

퍼가요~♥

답글 달기