Files
fe-agent-new/skills/nuxt-component-create/SKILL.md

4.0 KiB

name, description
name description
nuxt-component-create 새로운 Vue 3 컴포넌트를 팀 컨벤션에 맞게 생성할 때 사용합니다. "컴포넌트 만들어줘", "새 컴포넌트", "UI 컴포넌트 추가", "버튼 만들어줘", "카드 컴포넌트" 등을 요청하면 트리거됩니다. (기존 컴포넌트 리뷰는 vue-component-review 스킬을 사용하세요.)

Vue 컴포넌트 신규 생성

이 skill은 팀 컨벤션에 맞는 새로운 Vue 3 컴포넌트를 생성합니다. 기존 컴포넌트의 리뷰/점검은 vue-component-review skill을 사용합니다.

작업 순서

  1. 요구사항 확인

    • 컴포넌트의 목적, 필요한 props, emits, 사용 위치를 파악
    • 불명확한 부분은 사용자에게 질문
  2. 기존 컴포넌트 탐색

    • components/ 디렉토리에서 유사/재사용 가능한 컴포넌트 확인
    • .claude/project/conventions.md 에서 프로젝트별 컴포넌트 규칙 확인
  3. 파일 위치 결정

    • 프로젝트의 기존 디렉토리 구조 패턴을 따름
    • 일반적 구조: components/base/ (Atoms), components/common/ (Molecules), components/domain/ (Organisms)
  4. 인터페이스 우선 설계

    • defineProps<T>() 제네릭 형태로 props 타입 정의
    • defineEmits<{}>() 제네릭 형태로 events 선언
    • 필요 시 defineSlots 로 슬롯 타입 정의
  5. 구현

    • <script setup lang="ts"> 사용
    • 로직이 30줄을 넘으면 composable 추출 검토
    • 템플릿의 가독성 유지
  6. 스타일링

    • Tailwind 유틸리티 클래스 우선
    • 조건부 클래스는 clsx 또는 cn 유틸리티 사용
    • @apply 는 꼭 필요한 경우에만
  7. 검증

    • TypeScript 오류 확인
    • 파일 길이 200줄 이내 확인

컴포넌트 템플릿

<script setup lang="ts">
interface Props {
  /** 버튼 라벨 */
  label: string;
  isDisabled?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  isDisabled: false,
});

const emit = defineEmits<{
  click: [event: MouseEvent];
}>();

function handleClick(event: MouseEvent) {
  if (!props.isDisabled) {
    emit('click', event);
  }
}
</script>

<template>
  <button
    :disabled="isDisabled"
    class="rounded-lg bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 disabled:opacity-50"
    @click="handleClick"
  >
    {{ label }}
  </button>
</template>

슬롯이 있는 컴포넌트 템플릿

<script setup lang="ts">
interface Props {
  title: string;
  isCollapsible?: boolean;
}

withDefaults(defineProps<Props>(), {
  isCollapsible: false,
});

const isOpen = ref(true);

function handleToggle() {
  isOpen.value = !isOpen.value;
}
</script>

<template>
  <div class="rounded-lg border border-gray-200 p-4">
    <div class="flex items-center justify-between">
      <h3 class="text-lg font-semibold">{{ title }}</h3>
      <button v-if="isCollapsible" @click="handleToggle">
        {{ isOpen ? '접기' : '펼치기' }}
      </button>
    </div>
    <div v-show="isOpen" class="mt-4">
      <slot />
    </div>
  </div>
</template>

분리 기준 가이드

상황 조치
파일 200줄 초과 하위 컴포넌트로 분리
script 로직 30줄 초과 composable로 추출
Props 5개 초과 객체 prop으로 그룹핑 검토
동일 패턴 3회 이상 반복 공통 컴포넌트로 추출

네이밍 규칙

  • 파일명: PascalCase.vue (예: UserCard.vue)
  • 이벤트 핸들러: handle* 또는 on* 접두사
  • 불리언 props: is*, has*, can*, should* 접두사
  • 상수: UPPER_SNAKE_CASE

주의사항

  • Options API 사용 금지. 반드시 <script setup lang="ts">
  • any 타입 사용 금지. 불가피 시 주석으로 사유 기재, unknown 우선 고려
  • 직접 fetch/$fetch 호출 금지. composable 또는 API wrapper 사용
  • 새 컴포넌트 생성 전 기존 컴포넌트 재사용 가능 여부를 반드시 먼저 확인
  • 사용자가 리팩토링을 요청하지 않은 경우 기존 코드를 수정하지 않음