본문으로 건너뛰기

14.2 환경 설정 — create-vue, Vite, 프로젝트 구조, Vue DevTools

Vue 프로젝트 시작하기​

Vue 3 프로젝트를 시작하는 공식 방법은 create-vue 스캐폴딩 도구를 사용하는 것입니다. create-vue는 내부적으로 Vite 를 번들러로 사용하며, TypeScript, Vue Router, Pinia, 테스트 도구 등 다양한 옵션을 대화형으로 선택할 수 있습니다.

Node.js 18 이상 이 필요합니다. node --version으로 버전을 확인하세요.


create-vue로 프로젝트 생성​

npm create vue@latest

명령어를 실행하면 대화형 프롬프트가 나타납니다:

✔ Project name: … my-vue-app
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit Testing? … No / Yes
✔ Add an End-to-End Testing Solution? … No / Cypress / Nightwatch / Playwright
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formatting? … No / Yes
✔ Add Vue DevTools 7 extension for debugging? (experimental) … No / Yes

Scaffolding project in ./my-vue-app...
Done.

이후 다음 명령어로 개발 서버를 시작합니다:

cd my-vue-app
npm install
npm run dev

브라우저에서 http://localhost:5173을 열면 Vue 앱이 실행됩니다.


Vite란 무엇인가?​

Vite(비트, 프랑스어로 "빠르다")는 Evan You가 만든 차세대 프론트엔드 빌드 도구입니다. Vue CLI(webpack 기반)를 대체하는 공식 권장 도구입니다.

Vite의 핵심 특징​

특징설명
즉시 서버 시작번들링 없이 네이티브 ES Module로 제공 → 수백 ms 내 시작
빠른 HMR변경된 모듈만 교체 → 앱 크기와 무관하게 즉각 반영
최적화된 빌드프로덕션 빌드 시 Rollup 사용, tree-shaking 내장
플러그인 생태계Rollup 플러그인 호환, Vue/React/Svelte 등 공식 지원

Vite vs Vue CLI (webpack) 성능 비교​

Vue CLI (webpack):
콜드 스타트 → [모든 파일 번들링] → 서버 준비 (수십 초)
HMR → [관련 청크 재빌드] → 브라우저 업데이트 (수 초)

Vite:
콜드 스타트 → [ES Module 직접 제공] → 서버 준비 (< 1초)
HMR → [변경 모듈만 업데이트] → 브라우저 업데이트 (< 50ms)

프로젝트 구조 이해​

create-vue로 생성된 프로젝트의 기본 구조입니다:

my-vue-app/
├── public/ # 정적 파일 (그대로 복사됨)
│ └── favicon.ico
├── src/ # 소스 코드
│ ├── assets/ # 이미지, 폰트 등 (Vite가 처리)
│ ├── components/ # 재사용 가능한 Vue 컴포넌트
│ │ └── HelloWorld.vue
│ ├── composables/ # Composition API 로직 재사용 (컨벤션)
│ ├── router/ # Vue Router 설정 (선택)
│ │ └── index.js
│ ├── stores/ # Pinia 상태 관리 (선택)
│ │ └── counter.js
│ ├── views/ # 페이지 레벨 컴포넌트 (라우팅 대상)
│ │ ├── HomeView.vue
│ │ └── AboutView.vue
│ ├── App.vue # 루트 컴포넌트
│ └── main.js # 앱 진입점
├── index.html # HTML 진입점 (Vite 방식)
├── vite.config.js # Vite 설정
├── package.json
└── README.md

index.html — Vite의 HTML 진입점​

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Vue App</title>
</head>
<body>
<div id="app"></div>
<!-- type="module"이 Vite의 ES Module 방식 핵심 -->
<script type="module" src="/src/main.js"></script>
</body>
</html>

src/main.js — 앱 진입점​

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import './assets/main.css'

const app = createApp(App)

app.use(createPinia()) // Pinia 상태 관리 플러그인
app.use(router) // Vue Router 플러그인

app.mount('#app') // index.html의 #app div에 마운트

src/App.vue — 루트 컴포넌트​

<script setup>
import { RouterLink, RouterView } from 'vue-router'
</script>

<template>
<header>
<nav>
<RouterLink to="/">홈</RouterLink>
<RouterLink to="/about">소개</RouterLink>
</nav>
</header>

<!-- 현재 라우트에 해당하는 뷰가 여기에 렌더링됨 -->
<RouterView />
</template>

Vite 설정 파일​

vite.config.js는 Vite의 동작을 세밀하게 제어합니다.

기본 설정​

// vite.config.js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
plugins: [
vue(), // Vue SFC 지원
],
resolve: {
alias: {
// '@'를 src/ 폴더로 단축 (import '@/components/...' 가능)
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
})

고급 설정 — 개발 서버, 프록시, 환경 변수​

// vite.config.js (고급)
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
plugins: [vue()],

resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},

// 개발 서버 설정
server: {
port: 3000, // 기본 5173 대신 3000 사용
open: true, // 서버 시작 시 브라우저 자동 열기
proxy: {
// '/api'로 시작하는 요청을 백엔드 서버로 프록시
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},

// 빌드 설정
build: {
outDir: 'dist',
sourcemap: true, // 프로덕션 소스맵 생성
rollupOptions: {
output: {
// 청크 분할 전략
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
},
},
},
},
})

환경 변수 설정​

Vite는 .env 파일로 환경 변수를 관리합니다. 클라이언트 코드에서 접근 가능한 변수는 VITE_ 접두사 가 필요합니다.

# .env (공통 — 모든 환경에 적용)
VITE_APP_TITLE=My Vue App

# .env.development (개발 환경)
VITE_API_BASE_URL=http://localhost:8080/api

# .env.production (프로덕션 환경)
VITE_API_BASE_URL=https://api.myapp.com
<script setup>
// 컴포넌트에서 환경 변수 사용
const apiBase = import.meta.env.VITE_API_BASE_URL
const appTitle = import.meta.env.VITE_APP_TITLE
console.log(import.meta.env.MODE) // "development" 또는 "production"
</script>

Single File Component(SFC) 구조 심화​

.vue 파일은 세 블록으로 구성됩니다:

<!-- MyComponent.vue -->

<!-- 1. Template: 컴포넌트의 HTML 구조 -->
<template>
<div class="my-component">
<h1>{{ title }}</h1>
<slot /> <!-- 슬롯: 부모가 내용을 주입할 수 있는 자리 -->
</div>
</template>

<!-- 2. Script: 컴포넌트의 JavaScript 로직 -->
<script setup>
// setup 컴파일러 매크로 사용
defineProps({
title: {
type: String,
required: true,
},
})
</script>

<!-- 3. Style: 컴포넌트 전용 스타일 -->
<!-- scoped: 이 컴포넌트에만 적용되는 스타일 -->
<style scoped>
.my-component {
padding: 1rem;
}
h1 {
color: #42b883; /* Vue 초록색 */
}
</style>

스타일 블록 옵션​

<!-- scoped: 컴포넌트 범위 내에서만 적용 -->
<style scoped>
.btn { color: red; } /* 이 컴포넌트의 .btn만 빨간색 */
</style>

<!-- module: CSS Modules — 클래스 이름을 JS 객체로 사용 -->
<style module>
.btn { color: blue; }
</style>
<!-- 사용: :class="$style.btn" -->

<!-- lang="scss": SCSS/Sass 사용 (npm install -D sass 필요) -->
<style lang="scss" scoped>
$primary: #42b883;
.container {
.title { color: $primary; }
}
</style>

<!-- lang="less": Less 사용 -->
<style lang="less" scoped>
@primary: #42b883;
</style>

실전 예제 — 완전한 프로젝트 초기 셋업​

새 Vue 3 프로젝트를 처음부터 설정하는 전체 과정입니다.

1단계: 프로젝트 생성​

npm create vue@latest my-blog-app
# TypeScript: Yes
# Vue Router: Yes
# Pinia: Yes
# ESLint: Yes
# Prettier: Yes

cd my-blog-app
npm install

2단계: 경로 별칭 확인​

// vite.config.ts
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
})

3단계: TypeScript 설정​

// tsconfig.json (핵심 부분)
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"paths": {
"@/*": ["./src/*"]
}
}
}

4단계: 전역 CSS 및 디자인 토큰 설정​

/* src/assets/base.css */
:root {
--color-primary: #42b883;
--color-secondary: #35495e;
--color-background: #ffffff;
--color-text: #2c3e50;
--font-size-base: 16px;
--border-radius: 8px;
}

*, *::before, *::after {
box-sizing: border-box;
}

body {
font-family: system-ui, -apple-system, sans-serif;
font-size: var(--font-size-base);
color: var(--color-text);
background: var(--color-background);
margin: 0;
}
/* src/assets/main.css */
@import './base.css';

5단계: 첫 번째 페이지 컴포넌트​

<!-- src/views/HomeView.vue -->
<script setup lang="ts">
import { ref, onMounted } from 'vue'

interface Post {
id: number
title: string
excerpt: string
date: string
}

const posts = ref<Post[]>([])
const loading = ref(true)

onMounted(async () => {
// 실제로는 API 호출
await new Promise(resolve => setTimeout(resolve, 500))
posts.value = [
{ id: 1, title: 'Vue 3 시작하기', excerpt: 'Composition API의 매력', date: '2025-01-01' },
{ id: 2, title: 'Pinia로 상태 관리', excerpt: '직관적인 상태 관리', date: '2025-01-15' },
]
loading.value = false
})
</script>

<template>
<main>
<h1>블로그 홈</h1>
<div v-if="loading" class="loading">불러오는 중...</div>
<ul v-else class="post-list">
<li v-for="post in posts" :key="post.id" class="post-item">
<h2>{{ post.title }}</h2>
<p>{{ post.excerpt }}</p>
<time>{{ post.date }}</time>
</li>
</ul>
</main>
</template>

<style scoped>
.post-list {
list-style: none;
padding: 0;
}
.post-item {
border: 1px solid #ddd;
border-radius: var(--border-radius);
padding: 1rem;
margin-bottom: 1rem;
}
</style>

Vue DevTools​

Vue DevTools 는 Vue 컴포넌트 트리, 반응형 상태, 이벤트, 라우터 상태 등을 실시간으로 검사할 수 있는 브라우저 확장 프로그램입니다.

설치 방법​

  1. 브라우저 확장: Chrome Web Store 또는 Firefox Add-ons에서 "Vue.js devtools" 설치
  2. Vite 플러그인(선택): create-vue 스캐폴딩 시 DevTools 옵션 활성화
# 플러그인 방식으로 설치
npm install -D vite-plugin-vue-devtools
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import VueDevTools from 'vite-plugin-vue-devtools'

export default defineConfig({
plugins: [
vue(),
VueDevTools(), // 개발 모드에서만 활성화됨
],
})

DevTools 주요 탭​

탭기능
Components컴포넌트 트리 탐색, props/state/emit 실시간 확인
Pinia스토어 상태 조회 및 직접 수정
Router현재 라우트, 히스토리, 라우트 매칭 정보
Timeline컴포넌트 이벤트, 사용자 이벤트, 퍼포먼스 타임라인
Assets이미지, 폰트 등 에셋 확인

고수 팁​

npm 스크립트 커스터마이징​

// package.json
{
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"test:unit": "vitest",
"test:e2e": "playwright test",
"lint": "eslint . --ext .vue,.js,.ts --fix",
"format": "prettier --write src/"
}
}

절대 경로 임포트 활용​

// ❌ 상대 경로 (깊어질수록 지저분해짐)
import MyComponent from '../../../components/MyComponent.vue'

// ✅ 절대 경로 (@ = src/)
import MyComponent from '@/components/MyComponent.vue'
import { useAuth } from '@/composables/useAuth'
import { useUserStore } from '@/stores/user'

Vite 플러그인 생태계​

# 자동 임포트 (import 문 자동 생성)
npm install -D unplugin-auto-import unplugin-vue-components
// vite.config.js
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'

export default defineConfig({
plugins: [
vue(),
AutoImport({
imports: ['vue', 'vue-router', 'pinia'],
dts: 'src/auto-imports.d.ts',
}),
Components({
dts: 'src/components.d.ts',
}),
],
})

자동 임포트를 설정하면 ref, computed, onMounted 등을 매 파일마다 임포트하지 않아도 됩니다.

빌드 결과물 분석​

# 번들 크기 시각화
npm install -D rollup-plugin-visualizer
// vite.config.js
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
plugins: [
vue(),
visualizer({ open: true }), // 빌드 후 stats.html 자동 오픈
],
})
npm run build
# → dist/ 생성 + stats.html로 번들 구성 시각화