Vue의 컴포넌트 기반 개발 방식인 SFC부터 프로젝트 생성·구조, 모듈/번들러, Virtual DOM, Composition API에 대해 학습했다.
Component : 재사용 가능한 코드 블록
<Root>
┌──────────┼──────────┐
<Header> <Main> <Aside>
│
<Article>×2 <Item>×3
💡 UI (User Interface) : 사용자가 마주하는 화면의 시각적 디자인
컴포넌트의 템플릿(HTML), 로직(JS), 스타일(CSS) 을 하나의 파일로 묶어낸 특수한 파일 형식 (
*.vue파일)
.vue 파일 안에 컴포넌트의 HTML, JavaScript, CSS 코드를 모두 담는 Vue의 개발 방식이다.<template> : 화면 구조 담당<script> : 로직 담당<style> : 스타일 담당📌 한 줄 요약 : template - script - style (HTML, CSS, JS를 단일 파일로 합친 게 SFC)
<!-- MyComponent.vue -->
<template>
<div class="greeting">{{ msg }}</div>
</template>
<script setup>
import { ref } from 'vue'
const msg = ref('Hello World!')
</script>
<style scoped>
.greeting {
color: red;
}
</style>
각 *.vue 파일은 세 가지 유형의 최상위 언어 블록 <template>, <script>, <style>으로 구성된다.
⚠️ 언어 블록의 작성 순서는 상관 없으나, 일반적으로 template → script → style 순서로 작성한다.
<template> 블록*.vue 파일은 최상위 <template> 블록을 하나만 포함할 수 있다.<script setup> 블록*.vue 파일은 <script setup> 블록을 하나만 포함할 수 있다. (일반 <script> 제외)setup() 함수로 사용되며, 컴포넌트의 각 인스턴스에 대해 실행된다.<style scoped> 블록*.vue 파일에는 여러 개의 <style> 태그가 포함될 수 있다.scoped가 지정되면 해당 CSS는 현재 컴포넌트에만 적용된다.💡 빌드(build) : 개발자가 쓴 소스 코드를 실행 가능한 파일로 변환하는 과정
프론트엔드 개발 도구로써, 빠른 개발 환경을 위한 빌드 도구와 개발 서버를 제공한다.
Node.js의 기본 패키지 관리자
npm install 명령어 하나로 간편하게 다운로드하여 사용할 수 있다.package.json 파일에 기록하여, 다른 사람과 협업하거나 다른 환경에서 작업할 때도 동일한 개발 환경을 쉽게 구축할 수 있도록 도와준다.💡 Server-Side : 브라우저가 아닌 서버에서 로직을 처리하는 것
Vue Project(Application) 생성 (Vite 기반 빌드)
$ npm create vue@latest
💡 TIP
- 문서나 튜토리얼에서 코드가
$로 시작한다면, 이는 터미널(Terminal) 또는 명령 프롬프트(Command Prompt)에 입력해야 할 명령어임을 나타낸다.latest는 패키지의 가장 최신 안정화 버전(stable version)을 사용하겠다는 의미다.
tab 클릭 시 기본 값 사용enter 클릭 시 결정 후 진행프로젝트에 추가할 설정 선택 (TypeScript, JSX, Router, Pinia, Vitest, ESLint, Prettier 등)
space 클릭 시 중복 선택 가능enter 클릭 시 결정 후 진행cd vue-project
npm install
npm run dev
✏️ 메모 :
npm install은package.json이 있는 곳에서 해야 한다!
$ cd vue-project # 프로젝트 폴더 이동
$ npm install # 패키지 설치
📌
npm install은 반드시package.json파일이 있는 프로젝트 루트 디렉터리에서 실행해야 한다.
또한 인터넷에서 패키지를 다운로드 하는 과정이므로, 네트워크가 연결된 상태여야 한다.
$ npm run dev # Vue 프로젝트 서버 실행
vue-project
├── node_modules
├── public
├── src
├── .gitignore
├── index.html
├── jsconfig.json
├── package-lock.json
├── package.json
├── README.md
└── vite.config.js
프로그램을 구성하는 독립적인 코드 블록 (
*.js파일)
*.js 파일 하나가 하나의 모듈)💡 의존성 : 코드가 작동하기 위해 필요한 외부 라이브러리와의 관계
💡 node_modules :
npm install로 설치한 모든 패키지들이 모여있는 저장소 (의존성의 깊이가 어마어마해서 "HEAVIEST OBJECTS IN THE UNIVERSE" 짤로 비유됨 😅)
여러 모듈과 파일을 하나(혹은 여러 개)의 번들로 묶고 최적화하여 사용할 수 있게 만들어주는 도구
⭐ Vite는 Rollup이라는 Bundler를 사용하며, 개발자가 별도로 기타 환경설정에 신경 쓰지 않도록 모두 설정해두고 있다.
💡 TIP
- 번들링의 핵심 목표는 여러 파일을 하나로 묶어 HTTP 요청을 줄여 웹 로딩 속도를 개선하는 것이다.
- JS뿐 아니라 CSS, 이미지 등 모든 정적 자원을 함께 처리하고 최적화하는 역할도 한다.
import하지 않은 코드는 '트리 쉐이킹(Tree Shaking)' 을 통해 최종 번들 파일에서 자동으로 제거된다.
public 디렉토리src 디렉토리index.htmlpackage.json, package-lock.json, node_modules)주로 다음 정적 파일을 위치시킨다. (robots.txt, manifest.json)
import 할 필요 없는 코드항상 root 절대 경로를 사용하여 참조한다.
예)public/icon.png가 있다면 다른 소스 코드에서/icon.png경로로 참조할 수 있다.
public 디렉토리에 위치한 파일을 사용한다.HelloWorld.vue, TheWelcome.vue, WelcomeItem.vue 등)App.vue를 DOM에 마운트하는 시작점.import 하고 전역 설정을 수행한다.main.js에서 App.vue 컴포넌트를 렌더링하고, index.html의 특정 위치를 마운트 시킨다.💡 마운트 : 만들어진 앱을 HTML로 보여주는 것
💡 SPA : 하나의 페이지 안에서, 내용만 바꿔가며 보여주는 웹 앱
jsconfig.json : 컴파일 옵션, 모듈 시스템 등 설정 (IDE 지원용)vite.config.js : Vite 프로젝트 설정 파일 (플러그인, 빌드 옵션, 개발 서버 설정 등)💡 컴파일 옵션 : 디버깅, 최적화 등 컴파일의 세부 방식을 제어하는 명령어
💡 플러그인 : 기존 프로그램에 새로운 기능을 추가하는 확장 프로그램
npm install 시 이를 참조하여 패키지를 설치한다. (어떤 패키지를 설치해야 하는지 결정하는 기준 제공)💡 메타데이터 : 데이터를 설명해주는 데이터
package.json을 기반으로 실제 설치된 패키지들의 "정확한 버전 정보" 를 기록하는 파일.특징
npm install 결과가 반영되어 매번 자동 업데이트🛒 비유 : "장바구니에 담긴 물건들의 정확한 브랜드와 생산일자가 적힌 구매 내역서"
package.json과 package-lock.json에 따라 실제로 설치된 모든 패키지가 저장되는 곳.특징
npm install을 통해 설치된 모든 패키지(모듈)들이 실제로 저장된다.npm install 시 자동 관리된다.npm install로 언제든 재생성 가능하다..gitignore에 포함됨).🏗️ 비유 : "계획서와 내역서대로 확보한 실제 건축 자재들이 쌓여 있는 창고"
| 파일 | 역할 | 비유 |
|---|---|---|
package.json | 어떤 패키지가 필요하고 어떤 버전 범위를 허용할지 정의 | 설계도 |
package-lock.json | 실제로 설치한 패키지의 정확한 버전을 기록 | 상세 내역서 |
node_modules | 설계도와 내역서에 따라 내려받은 실제 패키지 | 자재 창고 |
App.vue 제외)App.vue 코드 초기화<!-- App.vue -->
<template>
<h1>App.vue</h1>
</template>
<script setup>
</script>
components 폴더 하위에 MyComponent.vue 생성
<!-- components/MyComponent.vue -->
<template>
<div>
<h2>MyComponent</h2>
</div>
</template>
<script setup>
</script>
App 컴포넌트에 MyComponent를 등록한다.@ 는 src/ 경로를 뜻하는 약어다.<!-- App.vue -->
<template>
<h1>App.vue</h1>
<MyComponent /> <!-- ② 자리 배치 -->
</template>
<script setup>
// ① import
// import MyComponent from './components/MyComponent.vue'
import MyComponent from '@/components/MyComponent.vue'
</script>
⭐ 등록 순서 : ① import → ② 템플릿에 자리 배치
MyComponentItem.vue 생성MyComponentItem은 MyComponent의 자식 컴포넌트 → 같은 컴포넌트를 여러 번 등록해 재사용성을 확인할 수 있다.<!-- MyComponent.vue -->
<template>
<div>
<h2>MyComponent</h2>
<MyComponentItem />
<MyComponentItem />
<MyComponentItem />
</div>
</template>
<script setup>
import MyComponentItem from '@/components/MyComponentItem.vue'
</script>
<!-- MyComponentItem.vue -->
<template>
<p>MyComponentItem</p>
</template>
Pascal case : < />
Kebab case : <> </>
이 둘 중 무엇을 쓰든 상관없다.
querySelector, createElement, addEventListener 등ref()와 Lifecycle Hooks 함수를 사용해 간접적으로 접근하여 조작할 것가상의 DOM을 메모리에 저장하고 실제 DOM과 동기화하는 프로그래밍 개념
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
<div id="app"></div> <!-- Vue의 영역(Virtual DOM) -->
<script type="module" src="/src/main.js"></script>
</body>
</html>
템플릿 ─①→ 렌더 함수 코드 ─②→ 가상 DOM 트리 ─③(마운트/패치)→ 실제 DOM
↑ │
└────────── ④ 컴포넌트 반응형 상태 ───┘
실제 DOM에 직접 접근하지 말 것
querySelector, createElement, …) 사용 금지ref()와 Lifecycle Hooks 함수를 사용해 간접적으로 접근하여 조작할 것ref 속성을 사용하여 특정 DOM 엘리먼트에 직접적인 참조를 얻을 수 있다.<template>
<input ref="input">
</template>
<script setup>
import { ref, onMounted } from 'vue'
// 변수명은 템플릿 ref 값과 일치해야 함
const input = ref(null)
onMounted(() => {
console.log(input.value) // <input>
})
</script>
import해서 가져온 API 함수들을 사용하여 컴포넌트의 로직을 정의한다.<template>
<button @click="increment">{{ count }}</button>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const count = ref(0)
function increment() { count.value++ }
onMounted(() => {
console.log(`숫자 세기의 초기값은 ${count.value}`)
})
</script>
data, methods, mounted 같은 객체를 사용하여 컴포넌트의 로직을 정의한다.<template>
<button @click="increment">{{ count }}</button>
</template>
<script>
export default {
data() { return { count: 0 } },
methods: {
increment() { this.count++ }
},
mounted() {
console.log(`숫자 세기의 초기값은 ${this.count}`)
}
}
</script>
-> Composition API로 일관성 있게 코드 짜는게 좋다.
모든 컴포넌트에는 최상단 HTML 요소가 작성되는 것이 권장된다.
<!-- ❌ bad -->
<template>
<h2>Heading</h2>
<p>Paragraph</p>
<p>Paragraph</p>
</template>
<!-- ✅ good -->
<template>
<div>
<h2>Heading</h2>
<p>Paragraph</p>
<p>Paragraph</p>
</div>
</template>
<style scoped>를 사용하면 해당 컴포넌트 내부의 스타일이 현재 컴포넌트 내부 요소에게만 적용되도록 범위를 제한한다.<style scoped> </style>
CSS Scoped를 사용하지 않는다면?
<style>에 scoped를 붙이지 않으면, 해당 스타일은 전역(모든 컴포넌트) 에 영향을 미친다.div 태그를 사용했다면 그 스타일이 함께 적용됨<!-- App.vue (부모) -->
<template>
<h1>App.vue</h1>
<MyComponent />
</template>
<style scoped>
div {
color: red;
}
</style>
<!-- MyComponent.vue (자식) -->
<template>
<div> <!-- 이 root element(div)는 부모와 본인의 CSS 모두의 영향을 받음 -->
<h2>MyComponent</h2>
</div>
</template>
→ App(부모)에 scoped가 작성되어 있지만, MyComponent(자식)의 최상위 요소(div)는 부모와 본인의 CSS 모두의 영향을 받기 때문에 부모 컴포넌트에 지정한 스타일이 적용된다.
CSS scoped를 적용한 이유
⭐ 최상위 App 컴포넌트에서 레이아웃 스타일을 전역적으로 구성할 수 있지만,
다른 모든 컴포넌트는 범위가 지정된(scoped) 스타일을 사용하는 것을 권장한다.
새로운 프로젝트나 모듈을 시작할 때, 초기 구조와 코드를 자동으로 생성하는 과정
npm create vue@latest 같은 명령어로, 필요한 초기 파일과 코드가 자동으로 만들어진다.npm install을 입력하는 위치node_modules 폴더 관리 주의npm install을 통해 재생성 할 수 있으므로, 직접 수정하거나 Git으로 관리할 필요 없음package.json과 package-lock.json 직접 편집 자제npm install 패키지명 명령을 통해 자동 업데이트하는 것이 안전node_modules 폴더를 삭제한 뒤 다시 npm install 실행template/script/style을 하나의 .vue 파일에 담는 Vue 개발 방식이다.npm create vue@latest로 스캐폴딩 → npm install → npm run dev. npm install은 package.json이 있는 루트에서!package.json(설계도) / package-lock.json(내역서) / node_modules(자재 창고) 3종 세트.@는 src/의 약어.<style scoped> 를 권장한다.