[Vue.js] 컴포지션 API

2022. 3. 24. 23:29공부/Vue.js

상기 이미지와 같이 우리는 여러가지 기능 또는 컴포넌트가 혼재하는 페이지를 자주 만들게 된다.

위의 간단한 예제를 살펴보면 다음과 같이 각 로직을 구성하는 요소들이 여기저기 혼재해 있는 것을 확인할 수 있다.

메세지를 거꾸로 뒤집는 로직은 빨간색, 카운트 로직은 노란색이다.

로직이 몇개 없는 상황에서야 상관이 없지만, 수십개의 로직이 얽혀있는 경우

참조되는 변수 및 메소드가 너무 멀리 떨어져있으면(단편화) 코드의 가독성이 현저하게 저하된다.

 

따라서, 관련있는 부분을 한곳으로 묶어주는 기능이 필요한데 이것이 바로 컴포지션 API이다.

빨간색은 빨간색끼리, 노란색은 노란색끼리 묶였다.

Composition API 기초

컴포지션 API는 setup() 함수 안에서 작성할 수 있다.

setup() 컴포넌트 옵션은 컴포넌트가 생성되기 전에 props가 한번 resolved 될 때 실행된다.

컴포넌트가 생성되기 전이므로 setup 옵션 내에서는 this가 존재하지 않는다.

즉, props를 제외한 나머지 다른 속성에 접근할 수 없다는 점을 주의해야 한다.

 

setup에서 반환된 모든 것은 컴포넌트 템플릿, 나머지 컴포넌트 (computed properties, methods, 라이프사이클 훅) 등에 노출된다.

// src/components/UserRepositories.vue

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: { type: String }
  },
  setup(props) {
    console.log(props) // { user: '' }

    return {} // 여기서 반환된 내용은 컴포넌트의 "rest"에서 사용할 수 있습니다
  }
  // 컴포넌트의 "rest" 부분
}

 

변환

위 내용을 변환하게 되면 다음과 같이 변환할 수 있다.

그러나 이렇게 하면 반응성이 사라지게 된다.

 

반응성을 얻게 하려면 ref를 활용한 변수를 활용해야 한다.

ref가 있는 반응성 변수

Vue 3.0에서는 ref를 이용하여 어디서나 변수를 반응성있도록 만들 수 있다.

ref를 사용하게 되면 객체 데이터를 반환하게 되므로 연산에 사용할 때는 .value를 사용해야 한다.

return 시킬 때는 굳이 value를 사용하지 않아도 그대로 값이 출력된다.

 

기본 옵션과 라이프사이클

data 변환

method 변환

computed 변환

setup 내부에서 computed라는 기능을 구현하기 위해 computed를 객체 구조분해를 통해 가져온다.

변수에 computed 메소드를 초기화 한 뒤 computed 내부에는 화살표 함수로 로직을 작성한다.

.value를 사용해야 함에 유의한다.

watch 변환

vue에서 watch 또한 객체 구조분해를 통해 가져온다.

감시하고자 하는 대상을 .value를 사용하지 않고 그대로 첫번째 매개변수를 통해 받는다.

두번째 매개변수에는 콜백 함수가 들어가는데, 화살표함수를 통해 로직을 작성한다.

Created, Mounted 변환

setup은 beforeCreate와 created 라이프사이클 훅 사이에 실행되는 시점이므로, 두 라이프사이클은 명시적으로 정의할 필요가 없다.

따라서 위 두 훅에서 작성되는 모든 코드는 setup() 펑션 내부에 직접 작성돼야 한다.

Mounted는 위의 표에서처럼 onMounted 함수와 콜백함수를 이용하여 선언한다.

export default {
  setup() {
    // mounted
    onMounted(() => {
      console.log('컴포넌트가 mounted 되었습니다!')
    })
  }
}

 

Props, context

setup 펑션은 2가지 전달인자를 가진다.

그것이 바로 props와 context이다.

 

Props

setup 함수의 첫번째 전달인자이다.

표준 컴포넌트에서 예상하는 것 처럼 setup 내부의 props는 반응성이 있고, 새로운 props가 전달되면 업데이트 된다.

export default {
  props: {
    title: String
  },
  setup(props) {
    console.log(props.title)
  }
}

그러나 ES6의 구조분해할당을 사용한다면 props의 반응성이 제거된다.

props의 구조분해할당이 필요한 경우, setup 펑션의 toRefs를 사용하면 반응성이 유지된다.

import { toRefs } from 'vue'

setup(props) {
	const { title } = toRefs(props)

	console.log(title.value)
}

Context

setup 함수의 두번째 전달인자이다.

context는 3가지 컴포넌트 프로터피를 가지는 일반 JavaScript 객체이다.

export default {
  setup(props, context) {
    // Attributes (Non-reactive object)
    console.log(context.attrs)

    // Slots (Non-reactive object)
    console.log(context.slots)

    // Emit Events (Method)
    console.log(context.emit)
  }
}

Context 객체는 일반적인 JavaScript 객체이므로 반응성이 존재하지 않는다.

즉, ES6에서 구조분해할당을 안정하게 사용할 수 있다.

export default {
  setup(props, { attrs, slots, emit }) {
    ...
  }
}

attrs와 slots는 컴포넌트 자체가 업데이트 될 때, 항상 업데이트 되는 상태저장 객체이다.

따라서 attrs와 slots에 구조분해할당을 피하고, 항상 속성을 attrs.x 또는 slots.x 의 형태로 참조해야 한다.

반응성이 없으므로 attrs와 slots의 변경으로 인한 사이드이펙트를 의도하려면 onUpdated 라이프사이클 훅을 이용한다.

 

props, context 사용 예제

변환 전

mounted 변환 후 (위에 적힌 mounted는 삭제돼야 하지만 비교를 위해 남겨두었다.)

methods 변환 후