Vue3 #5 [새로운 기능 2 - Teleport와 Fragments]

Teleport와 Fragments의 간단한 소개.


Vue3는 현재 총 7편의 시리즈로 구성되어 있습니다.

  1. Vue3 #0 [Vue3를 만나다]
  2. Vue3 #1 [변경된 기능 1 - 전역 API]
  3. Vue3 #2 [변경된 기능 2 - v-model]
  4. Vue3 #3 [변경된 기능 3 - v-for와 v-if]
  5. Vue3 #4 [새로운 기능 1 - Composition API]
  6. Vue3 #5 [새로운 기능 2 - Teleport와 Fragments]-(현재글)
  7. Vue3 #6 [제거된 기능들]


들어가며

안녕하세요. 지난 포스팅에는 Composition API라는 새로운 기능에 대해서 소개해 드렸습니다. 이번 포스팅도 역시 새롭게 추가된 기능 중에서 가장 흥미로운 두 가지 사항에 대해서 소개해 드릴까 합니다.


Teleport

vue는 기본적으로 컴포넌트 간 데이터 통신은 단방향이며 한세대를 뛰어넘어 통신하는 것은 불가능합니다. (물론, 전역 스토어 관리를 사용하면 되지만 여기에서는 컴포넌트 간 데이터 통신에 대해 서만 얘기해 봅시다.)

개발을 하다 보면 여러 가지 UI들을 만들게 되는데 반복하여 사용하는 UI는 재사용성 확보를 위하여 컴포넌트화하여 사용하는 것이 일반적입니다. 컴포넌트로 분리된 조각들은 DOM의 적절한 위치에서 임포트 되어 사용되는데 이는 몇 가지 소소한 고민거리를 야기하곤 합니다.

가장 빈번한 것은 부모 요소로부터 받는 CSS 영향이 있겠죠. 이런 문제를 해소하기 위해서 보통은 해당 컴포넌트를 문서의 가장 끝부분에 위치시키거나 하는 등의 방법을 사용하는데, vue에서는 이렇게 재사용성을 확보하기 위하여 만든 컴포넌트들을 어디에 위치시키느냐에 따라서 컴포넌트 간 데이터 통신에 영향을 받기 때문에 늘 어려운 문제였습니다.

이제 3버전에서는 teleport를 사용하면 이 문제를 해결할 수 있습니다.


사용법

teleport로 감싼 뒤 to 속성을 이용하여 위치할 곳을 명시해 줍니다. to 속성의 값은 쿼리 셀렉트가 가능한 유효한 엘리먼트여야 합니다. id나 class도 가능하겠죠?

// parentComponent
<template>
	<teleport to="body">
		<child-component :title="childTitle"></child-component>
	</teleport>
</template>

<script>
import ChildComponent from '@/components/ChildComponent.vue';

export default {
	data() {
		return {
			childTitle: 'hello world!!!',
		}
	}
}
</script>

이제 childComponent는 parentComponent가 아닌 body의 마크업 제일 후위에 렌더링 됩니다. 렌더링 위치가 바뀌었지만 여전히 논리적 위치는 parentComponent의 자식 컴포넌트이기 때문에 props를 정상적으로 내려받을 수 있습니다.

disabled 속성도 사용할 수 있습니다. 불린 값을 가지는 속성으로 teleport의 기능을 일시적으로 무력화할 수 있습니다. 다양한 응용 방법이 있겠습니다.

// parentsComponent
<template>
	<teleport to="body" :disabled="isNotUse">
		<child-component :title="childTitle"></child-component>
	</teleport>
</template>

<script>
import ChildComponent from '@/components/ChildComponent.vue';

export default {
	data() {
		return {
			childTitle: 'hello world!!!'
		}
	}
}
</script>

Fragments

2.x 버전에서는 지원하지 않았던 다중 루트 노드를 이제 3버전에서는 공식적으로 지원합니다. 이제 컴포넌트의 가장 최상위 마크업은 여러 개 위치할 수 있습니다. 다중 루트 노드를 사용할 경우 non-prop attribute를 명시적으로 지정해야 합니다.

//2.x
<template>
  <div>
    <p>가장 최상위 마크업은 무조건 1개 이하여야 한다.</p>
  </div>
</template>

//3.x
<template>
  <div>
    <p>이제 3버전부터는</p>
  </div>
  <div v-bind="$attrs">
    <p>다중 루트 노드를 지원한다.</p>
  </div>
</template>

단일 루트 노드 컴포넌트에서는 non-prop attribute를 자동으로 내려받았지만 다중 루트 노드에서는 명시하지 않으면 런타임 에러가 발생되므로 주의해야 합니다.

// child component
<template>
  <div class="childComponent">
    <p>나는 자식이다.</p>
  </div>
</template>

// parent component
<child-component style="background: #ff0;"></child-component>

// 렌더링 된 child component
<div class="childComponent" style="background: #ff0;">
  <p>나는 자식이다.</p>
</div>

non-prop attribute란?

사용자 컴포넌트에 직접적으로 선언한 html 속성이 자동으로 자식 컴포넌트에 정의되는 것을 말합니다. class, id, style 등이 해당될 수 있습니다. 이벤트 리스너도 상속될 수 있습니다. 루트 엘리먼트가 change 이벤트를 리스닝 할 수 있는 상태라면 다음과 같은 상속도 유용하게 사용될 수 있겠습니다.

<child-component @Change="onSubmit"></child-component>

마치며

이렇게 해서 간단하게 3버전의 새로운 기능을 소개해 드렸습니다. 앞으로의 Vue3 포스팅 계획은 마지막으로 변경점에 대해서 한 번 더 짚고 마무리할까 합니다. 포스팅이 너무 오래 지속되니 읽는 사람도 피곤..

공부를 할 때마다 느끼는 거지만 vue는 정말 너무 편리한 거 같아요.

그럼 새해 복 많이 받으시고, 다음에 뵐게요!


참고자료


추천 글