본문으로 건너뛰기
Advertisement

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로 번들 구성 시각화
Advertisement