Vue의 <component :is /> 완벽 정리: 동적 렌더링의 모든 것

<component /> 란?

<component :is=""> 란 Vue에서 동적으로 컴포넌트를 렌더링하는 방법이다. 이 값은 세 가지 형태로 전달될 수 있다

  • 컴포넌트 이름 (문자열): 등록된 컴포넌트의 이름.
  • 컴포넌트 정의 객체: 직접 정의한 컴포넌트 객체.
  • VNode (가상 노드): Vue의 가상 DOM 노드.

컴포넌트만 전달이 가능한가??

아니다

<component :is /> 는 단순히 컴포넌트만 렌더링하는 것이 아니라, 더 유연한 렌더링을 지원한다. 세 가지 경우를 예제와 함께 정리해보자.

  1. 컴포넌트 이름 (문자열):
<template>
  <component :is="currentComponent" />
</template>

<script setup>
import MyComponent from './MyComponent.vue'; // 별도 파일에서 불러온 컴포넌트
const currentComponent = ref('MyComponent');

// 컴포넌트를 등록해야 동작 (Nuxt에서는 자동 임포트 가능)
defineComponents({
  MyComponent,
});
</script>
  • :is에 문자열 'MyComponent'를 전달하면, Vue는 등록된 컴포넌트를 찾아 렌더링
  1. 컴포넌트 객체:
<template>
  <div>
    <component :is="dynamicComponent" />
  </div>
</template>

<script setup>
const dynamicComponent = {
  template: `<div>{{ message }}</div>`,
  setup() {
    return {
      message: "직접 정의된 컴포넌트입니다!",
    };
  },
};
</script>
  • :is에 객체를 전달하면, Vue는 이 객체를 컴포넌트로 간주하고 내부 template을 컴파일해 렌더링.
  1. VNode (가상 노드):
<template>
  <div>
    <component v-for="node in slotNodes" :key="node.props?.id" :is="node" />
  </div>
</template>

<script setup>
import { useSlots } from 'vue';
const slots = useSlots();
const slotNodes = slots.default?.() || []; // 슬롯에서 VNode 배열 가져오기
</script>
  • :is에 VNode를 전달하면, Vue는 이를 추가 변환 없이 바로 렌더링합니다.
  • 슬롯(slots.default())이나 h() 함수로 생성된 VNode가 여기에 해당합니다.

VNode란 무엇인가?

VNode(가상 노드)는 Vue가 실제 DOM을 생성하기 전 사용하는 가상 DOM의 기본 단위이다. <template>render 함수가 컴파일되면 VNode로 변환.

VNode의 구조

<template>
  <div>Hello</div>
</template>

위 코드는 다음과 같은 VNode로 변환:

{
  "type": "div",
  "props": {},
  "children": "Hello"
}
  • type: 태그 이름(문자열), 컴포넌트 객체, 또는 함수.
  • props: 속성(예: class, id)이나 컴포넌트에 전달할 props.
  • children: 자식 콘텐츠(문자열, 배열, 또는 다른 VNode).

에서 바로 렌더링되는 이유?

  • DOM 직전 단계: VNode는 컴파일이 끝난 결과물로, 추가 처리 없이 가상 DOM에 삽입 가능.
  • Vue의 설계: 값이 VNode면 이를 바로 렌더링하도록 설계.

실전 예제: 탭 기반 코드 뷰어

<template>
  <div>
    <component :is="activeNode" />
  </div>
</template>

<script setup>
import { useSlots, computed, ref } from 'vue';
const slots = useSlots();
const activeTab = ref('js');
const slotNodes = slots.default?.() || []; // VNode 배열

const activeNode = computed(() =>
  slotNodes.find(node => node.props?.language === activeTab.value)
);
</script>

부모 컴포넌트 사용 예시:

<CodeViewer>
  <pre language="js">console.log("JS")</pre>
  <pre language="py">print("Python")</pre>
</CodeViewer>
  • 동작: slotNodes는
     태그의 VNode 배열이고, 는 선택된 VNode를 바로 렌더링. *Vue의 런타임 시점 기준

컴파일과 VNode 생성 과정

  • Vue는 .vue 파일을 처리할 때 두 단계로 나뉨.
  1. 빌드 타임 (컴파일 단계): 소스 코드를 JavaScript로 변환.
  2. 런타임: 변환된 코드를 실행하며 실제 렌더링.