Choorai
Lv.2 Vue 3

Vue 3 프론트엔드

HTML에 가까운 템플릿 문법으로 React와 동일한 기능을 구현합니다. 같은 API, 다른 프론트엔드 철학을 직접 체험하세요.

왜 Vue인가요?

  • HTML에 가까운 템플릿 문법 → 초보자 접근성 높음
  • v-model로 양방향 바인딩이 간단함
  • React와 동일한 TanStack Query 사용 가능
  • Composition API로 React Hooks와 유사한 패턴

React vs Vue 3 비교

React Vue 3
useState ref() / reactive()
useEffect onMounted() / watch()
@tanstack/react-query @tanstack/vue-query
react-router-dom vue-router
JSX SFC Template (.vue)
children props <slot />

프로젝트 구조

폴더 구조
examples/b2b-admin/web-vue/
├── src/
│   ├── main.ts              # 앱 진입점
│   ├── App.vue              # 루트 컴포넌트
│   ├── api/
│   │   └── client.ts        # API 클라이언트 (React와 동일)
│   ├── types/
│   │   └── api.ts           # 타입 정의 (React와 동일)
│   ├── composables/         # Vue Hooks (React의 hooks/)
│   │   ├── useHealth.ts
│   │   └── useProjects.ts
│   ├── router/
│   │   └── index.ts         # Vue Router 설정
│   ├── components/
│   │   ├── HealthStatus.vue
│   │   ├── AppLayout.vue
│   │   ├── ProjectForm.vue
│   │   └── ProjectCard.vue
│   └── pages/
│       ├── ProjectsPage.vue
│       └── ProjectDetailPage.vue
├── package.json
├── vite.config.ts
└── tailwind.config.js

Composition API 예제

상태 관리 (ref)

React

React
const [name, setName] = useState('');

<input
  value={name}
  onChange={(e) => setName(e.target.value)}
/>

Vue 3

Vue
const name = ref('');

<input v-model="name" />

Vue Query Composable

composables/useProjects.ts
// composables/useProjects.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/vue-query';
import { computed, type Ref } from 'vue';
import { apiClient } from '../api/client';

export function useProjects(page: Ref<number> = ref(1)) {
  return useQuery({
    queryKey: computed(() => ['projects', page.value]),
    queryFn: () =>
      apiClient.get(`/api/v1/projects?page=${page.value}`),
  });
}

export function useCreateProject() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (data) =>
      apiClient.post('/api/v1/projects', data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['projects'] });
    },
  });
}

Vue 컴포넌트 예제

components/ProjectForm.vue
<script setup lang="ts">
import { ref } from 'vue';
import { useCreateProject } from '../composables/useProjects';

const name = ref('');
const description = ref('');
const { mutate: createProject, isPending } = useCreateProject();

const handleSubmit = () => {
  if (!name.value.trim()) return;

  createProject(
    { name: name.value.trim(), description: description.value.trim() },
    {
      onSuccess: () => {
        name.value = '';
        description.value = '';
      },
    }
  );
};
</script>

<template>
  <form @submit.prevent="handleSubmit" class="space-y-4">
    <div>
      <label class="block text-sm font-medium text-gray-700 mb-1">
        프로젝트 이름
      </label>
      <input
        v-model="name"
        type="text"
        class="w-full px-3 py-2 border border-gray-300 rounded-lg"
        placeholder="프로젝트 이름 입력"
        required
      />
    </div>
    <button
      type="submit"
      :disabled="isPending"
      class="w-full bg-blue-600 text-white py-2 rounded-lg"
    >
      {{ isPending ? '생성 중...' : '프로젝트 생성' }}
    </button>
  </form>
</template>

로컬 실행

터미널
# 1. 백엔드 실행 (Hono 권장)
cd examples/b2b-admin/api-hono
npm install
npm run dev

# 2. Vue 프론트엔드 실행 (새 터미널)
cd examples/b2b-admin/web-vue
npm install
npm run dev

# 브라우저에서 http://localhost:5174 접속

Cloudflare Pages 배포

React와 동일하게 Cloudflare Pages에 배포할 수 있습니다. public/_redirects 파일이 이미 포함되어 있어 SPA 라우팅 문제가 해결됩니다.

Framework preset Vite
Build command npm run build
Build output directory dist
Root directory examples/b2b-admin/web-vue

핵심 포인트

  • API 클라이언트타입 정의는 React와 100% 동일
  • TanStack Query는 React/Vue 모두 지원
  • 프레임워크가 달라도 동일한 백엔드 API 사용 가능

마지막 업데이트: 2026년 2월 22일 · 버전: v0.0.1

피드백 보내기

입력한 내용으로 새 이슈 페이지를 엽니다.

GitHub 이슈로 보내기