본문 바로가기

FrontEnd

[Vue3] 일반적인(intrinsic한) 컴포넌트 만들기 with typescript

반응형

리액트를 사용하는 경우 해당 링크의 예제처럼 일반적인 컴포넌트를 만듭니다.

 

아토믹 디자인을 활용한 디자인 시스템 도입기

카카오엔터테인먼트 FE 기술블로그

fe-developers.kakaoent.com

그런데 Vue3는 아무리 찾아봐도 해당 패턴이 없더라구요?

아마 암시적인 Fallthrough Attribute 기능 때문인 것 같습니다.

$attrs 객체는
컴포넌트의 props 또는 emit 옵션(예: 클래스, 스타일, v-on 리스너 등)에 의해
선언되지 않은 모든 속성을 포함합니다.
(주 : props 필터링을 통해 서브 컴포넌트의 계층화, 관심사의 분리에도 활용 가능)

해당 $attrs 객체의 typing을 지원한다면, 좀 더 쉽게 구현을 가이드할 수 있지 않을까요?

Vue3은 runtime-dom.d.ts 파일에서 해당 속성들에 대한 타입을 지원합니다.

(안타깝지만 defineProps내부 객체에서 클래스로 해당 타입을 지원하는 방법은 없는 것 같네요 ㅠ)

 

intrinsic한 버튼 컴포넌트의 예제를 만들어 보았습니다.

사용법은 다음과 같습니다.

 

1. 루트 컴포넌트(버튼)의 타입을 확장합니다.

interface Props extends ButtonHTMLAttributes {
  msg?: string;
  count?: string;
}

2. defineProps를 이용해 타입을 정의합니다.

JS의 경우는 애초에 런타임 타입체킹 밖에 안되니, 문서화로 적절하게 넘어가는 방법밖에 없을 것 같습니다.

TS의 경우는 아래와 같이 작성하면 타입스크립트를 이용해 컴파일러가 props를 생성해 줍니다.

<script setup lang="ts">
import { ButtonHTMLAttributes } from "vue";
interface Props extends ButtonHTMLAttributes {
  msg?: string;
  count?: string;
}
defineProps<Props>();
</script>

root 컴포넌트가 버튼일 경우 2번에서 끝입니다.

아닐 경우 3,4번을 작업합니다.

3. (optional) inheritAttrs 속성을 false로 설정

해당 설정은 setup과 함께 쓸 수 없습니다. 다른 script 블락을 사용해, 루트 컴포넌트로 fallthrough attributes를 넘기는 디폴트 설정을 끕니다.

<script setup lang="ts">
import { ButtonHTMLAttributes } from "vue";
interface Props extends ButtonHTMLAttributes {
  msg?: string;
  count?: string;
}
defineProps<Props>();
</script>
<script lang="ts">
export default {
  inheritAttrs: false,
};
</script>

4. (optional) 명시적으로 $attrs 바인딩.

useAttrs 컴포저블을 사용해도 됩니다.

<template>
  <h1>{{ msg }}</h1>
  <div class="card">
    <button type="button" v-bind="$attrs">
      at Children {{ count || "1" }}
    </button>
  </div>
</template>

예제는 https://github.com/i0boy/vue-generic-component-typescript 에서 확인할 수 있습니다.

codesandbox로 공유할려 했는데, git repo는 embed 지원을 안하네요;

codesandbox의 Vue3지원도 좋지 않습니다.

괜히 리액트를 쓰는게 아닌듯

반응형