인사이트

인사이트리포트

디지털프렌스포메이션 최신 정보 및 트렌드를 제공합니다.

SW 테크놀로지 데이터 관리 클라우드 플랫폼

Vue 3 Composition API 맛보기

2023.05.17이윤석 프로
다운로드

들어가며

컴포지션 (Composition) API는 Vue 3 및 Vue 2.7에 내장된 기능이다.

컴포지션 API는 옵션을 선언하는 대신 import한 함수를 사용하여 Vue 컴포넌트를 작성할 수 있는 API 세트이다. 이것은 다음 API를 다루는 포괄적인 용어이다. 반응형 (Reactivity) API: ref(), reactive()를 사용하여 반응형 상태, 계산된 상태, 감시자를 생성할 수 있다. 생명주기 훅: onMounted(), onUnmounted() 등을 사용하여 컴포넌트 생명주기에 프로그래밍적으로 연결할 수 있다. 의존성 주입: provide(), inject()를 사용하면 반응형 API를 사용하는 동안 Vue의 의존성 주입 시스템을 활용할 수 있다.

셀렉트박스는 웹 애플리케이션에서 많이 사용되는 사용자 인터페이스 중 하나로 사용자가 원하는 값을 선택할 수 있는 입력 폼이다.

본 아티클에서는 컴포지션 API 사용법에 대해서 셀렉트박스 컴포넌트를 구현하면서 설명을 하도록 하겠다.

 

반응형

reactive() 함수를 사용하여 객체 또는 배열을 반응형으로 만들 수 있다. 반응형 객체는 일반 객체처럼 작동한다. 일반 객체와의 차이점은 Vue가 속성에 접근 가능하고 반응형 객체의 변경사항을 감지할 수 있다는 것이다.

컴포넌트의 템플릿에서 반응형 상태를 사용하려면 컴포지션 API에서만 사용되는 특별한 훅인 setup() 함수에서 반응형 상태를 선언하고 반환해야 한다.

마찬가지로 반응형 상태를 변경하는 함수를 같은 범위에서 선언하고 상태와 함께 메서드로 노출할 수 있다. 노출된 메서드도 템플릿에서 이벤트 리스너로 사용이 가능하다.

 

setup() 함수를 통해 상태와 메서드를 매번 반환하여 노출하는 것은 장황할 수 있다. 그런 경우 싱글 파일 컴포넌트 사용 시, <script setup> 사용으로 단순화할 수 있다.

명시적 반환이 없어졌기 때문에 코드가 많이 간결해진 것을 알 수 있다. <script setup>에서 최상위 레벨로 선언된 변수나 함수는 템플릿에서 바로 사용할 수 있다.

 

reactive() API는 두 가지 제한 사항이 있다. 객체, 배열, Map, Set과 같은 컬렉션 타입에만 작동한다. string, number, boolean과 같은 기본 타입에는 사용할 수 없다.

그리고 항상 반응형 객체에 대한 동일한 참조를 유지해야 한다. 이는 반응형 객체를 쉽게 교체할 수 없음을 의미한다.

다음 코드에서 ({ expanded: false })는 더 이상 추적되지 않는다.

 

또한, 반응형 객체의 속성을 지역 변수에 할당하거나 구조 분해 할당하거나 함수에 전달할 때 반응형 연결이 끊어짐을 의미한다.

다음 코드에서 지역 변수 b의 값을 변경해도 원본 상태 (state.expanded) 에 영향을 미치지 않는다.

someFunction() 함수는 state.expanded의 변경 사항을 감지할 수 없다.

reactive()의 제한 사항을 해결하기 위해, Vue는 어떠한 타입이라도 반응형으로 재정의할 수 있는 ref() 함수를 제공한다. ref()는 받은 인자를 .value 속성을 포함하는 ref 객체에 래핑 후 반환한다. ref의 .value 속성은 반응형이다.

최상위 속성의 ref를 템플릿에서 접근하면 자동으로 언래핑하므로 .value를 사용할 필요가 없다.

 

Props

<script setup>을 사용하는 싱글 파일 컴포넌트에서는 defineProps()를 사용하여 props를 선언할 수 있다.

items props는 셀렉트박스의 목록에 표시할 값을 가지고 있는 배열이다.

부모 코드

<script setup>을 사용하지 않는 컴포넌트에서 props는 props 옵션을 사용하여 선언한다. setup()은 첫 번째 인자로 props를 받는다. 또한 템플릿에서 사용하기 위해서는 props를 반환하여 노출해야 한다.

이벤트

반응형 상태 객체에 selectedItem 속성을 추가하여 셀렉트박스의 목록에서 선택한 항목값을 표시한다.

셀렉트박스에서 선택한 항목은 부모 컴포넌트에서 값을 수신할 수 있어야 한다.

컴포넌트 내장 메서드 $emit를 사용하여 템플릿 표현식에서 직접 사용자 정의 이벤트를 발신 할 수 있다. 이벤트와 함께 특정 값을 내보내야 할 때도 있는데 $emit에 추가 인자를 전달하여 특정 값을 전달할 수 있다.

 

부모에서는 v-on을 사용하여 수신할 수 있다. 이벤트를 수신할 때 인라인 화살표 함수를 리스너로 사용하여 이벤트 인자에 접근할 수 있다.

또는 이벤트 핸들러가 메서드인 경우 인자 값이 메서드의 첫 번째 파라미터로 전달된다.

컴포넌트는 defineEmits() 매크로를 사용하여 명시적으로 발신할 이벤트를 선언할 수 있다.

<template>에서 사용한 $emit 메서드는 <script setup> 내에서 접근할 수 없지만, defineEmits()는 $emit 대신에 사용할 수 있는 동등한 함수를 반환한다.

setup() 함수를 사용하는 경우 emit 메서드에 접근하는 별도의 방법을 제공한다. setup()은 두번째 인자로 Setup Context 객체인 context를 받는다. context 객체는 setup() 안에서 유용하게 사용할 값들 (attrs, slots, emit, expose) 을 노출한다. context 객체는 구조 분해 할당하여 사용할 수 있다.

계산된 속성

셀렉트박스에는 사용자가 적절한 값을 입력할 수 있도록 도와주는 도움말인 placeholder을 표시해야 하는 경우가 있다.

템플릿 내 표현식은 편리하지만 매우 많은 논리를 넣으면 비대해져 유지보수가 어려워질 수 있는데 이러한 경우, 반응형 데이터를 포함하는 복잡한 논리인 경우 계산된 (computed) 속성을 사용하는 것이 좋다.

위의 코드는 계산된 속성을 사용하여 다음과 같이 바꿀 수 있다.

computed() 함수에는 함수가 인자로 전달되어야 하며, computed() 함수의 반환값은 computed ref이다. 일반 ref와 유사하게 계산된 결과를 selection.value로 접근할 수 있다. 계산된 ref는 템플릿에서 자동으로 언래핑되므로 템플릿 표현식에서 .value 없이 참조할 수 있다.

 

클래스 바인딩

클래스 바인딩은 옵션 API와 큰 차이는 없다. 클래스를 동적으로 토글하기 위해 객체를 :class에 전달할 수 있다. state.expanded의 true/false 여부에 의해 expanded 클래스의 존재 여부가 결정된다.

state.expanded가 true일 경우 다음과 같이 렌더링 되고

state.expanded가 false일 경우 다음과 같이 렌더링 된다.

 

감시자

셀렉트박스에서 항목을 선택할 때마다 서버와 통신을 하는 등의 상태 변경에 대한 반응으로 사이드 이펙트를 수행해야 하는 경우가 있다. watch() 함수를 사용하여 반응형 속성이 변경될 때마다 함수를 실행할 수 있다.

watch()는 ref에서 직접 작동한다. 그러나 다음과 같이 reactive 객체의 속성을 감시할 수는 없다. state.selectedItem라는 boolean 값을 watch()에 전달할 뿐이기 때문에 동작하지 않는다.

대신에 getter를 사용해야 한다.

대신에 getter를 사용해야 한다.

reactive 객체에서 watch()를 직접 호출하면 암시적으로 깊은 감시자가 생성되며, 콜백은 중첩된 모든 변경에서 트리거된다.

생명주기 훅

생명주기 훅은 import를 해서 사용한다. onMounted() 훅은 컴포넌트가 마운트된 이후 콜백 함수를 실행하는데 this를 통해 컴포넌트 인스턴스에 접근할 수 없다.

컴포지션 API에서 제공하는 생명주기 훅은 다음과 같다.

onMounted(): 컴포넌트가 마운트된 후 호출될 콜백을 등록한다.

onUpdated(): 반응형 상태 변경으로 DOM 트리를 업데이트한 후 호출될 콜백을 등록한다.

onUnmounted(): 컴포넌트가 마운트 해제된 후 호출될 콜백을 등록한다.

onBeforeMount(): 컴포넌트가 마운트되기 직전에 호출될 콜백을 등록한다.

onBeforeUpdate(): 반응형 상태 변경으로 DOM 트리를 업데이트하기 직전에 호출될 콜백을 등록한다.

onBeforeUnmount(): 컴포넌트 인스턴스가 마운트 해제되기 직전에 호출될 콜백을 등록한다.

onErrorCaptured(): 자식 컴포넌트에서 전파된 에러가 캡쳐되었을 때 호출될 콜백을 등록한다.

onRenderTracked(): 컴포넌트의 렌더 이펙트에 의해 반응형 의존성이 추적됐을 때 호출될 디버그 콜백을 등록한다.

onRenderTriggered(): 컴포넌트의 렌더 이펙트가 반응형 의존성에 의해 다시 실행되도록 트리거된 경우 호출될 디버그 콜백을 등록한다.

onActivated(): <KeepAlive>로 캐시된 컴포넌트 인스턴스가 DOM 트리의 일부로 삽입된 후 호출될 콜백을 등록한다.

onDeactivated(): <KeepAlive>로 캐시된 컴포넌트 인스턴스가 DOM 트리에서 제거된 후 호출될 콜백을 등록한다.

onServerPrefetch(): 컴포넌트 인스턴스가 서버에서 렌더링되기 전에 완료되어야 하는 비동기 함수를 등록한다.

 

 

Provide / inject 

일반적으로 부모에서 자식 컴포넌트로 데이터를 전달해야 할 때 props를 사용한다. 그러나 큰 컴포넌트 트리가 있고 깊이 중첩된 먼 조상 컴포넌트의 무언가가 필요한 경우를 상상해 보면 전체 부모 체인에 동일한 props을 전달해야 한다.

 

마치며 – 시스템의 보안과 관리

실제 자주 사용되는 컴포넌트 예제를 사용하여 컴포지션 API에 대해 간략히 알아보았다. 구현 방식은 더 직관적으로 변하여 컴포지션 API를 사용하지 않을 이유는 찾기 어려워 보인다. 컴포지션 API를 사용하여 로직 재사용성을 높이고 보다 유연하게 코드를 구성하는 것이 좋을 것 같다.

# References

https://vuejs.org

이윤석 프로

이윤석 프로

소프트웨어사업부 개발플랫폼그룹

에스코어에서 프론트엔드 개발을 하고 있습니다.

연관 아티클

  • Redis를 활용한 안전하게 동시성 이슈 제어하기
    오픈소스 SW2024.02.21

    Redis를 활용한 안전하게 동시성 이슈 제어하기

    자세히 보기
  • Webpack5 Module Federation 소개
    SW 테크놀로지2024.02.02

    Webpack5 Module Federation 소개

    자세히 보기
  • EU 탄소국경조정제도(EU CBAM)의 이해
    데이터 관리2024.01.23

    EU 탄소국경조정제도(EU CBAM)의 이해

    자세히 보기