✅ 프로젝트 개요
- Vue 3 + TypeScript 기반의 게시판 프로젝트
- 글 작성, 삭제, 외부 API 게시글 보기 기능 구현
- Composition API와 커스텀 훅, 컴포넌트 구조, 상태관리(Pinia), 라우팅 등 실제 사용 흐름 중심으로 학습
1. PostForm.vue
– 입력과 유효성 검사 (ref, computed, emit)
핵심 코드
const title = ref('')
const body = ref('')
const titleError = computed(() =>
title.value.length > 30 ? '제목은 30자 이내여야 합니다' : ''
)
const emit = defineEmits(['add-post'])
function submit() {
if (titleError.value) return
emit('add-post', { title: title.value, body: body.value })
title.value = ''
body.value = ''
}
학습 포인트
개념 |
설명 |
ref() |
반응형 입력 상태 (title , body )를 선언 |
computed() |
제목 길이에 따라 에러 메시지를 실시간 계산 |
defineEmits() |
부모 컴포넌트에 이벤트 전달 (@add-post ) |
Composition API |
setup 안에서 상태, 함수, 로직 분리 가능 |
2. PostList.vue
– 리스트 렌더링 (v-for, props, emit)
<ul>
<li v-for="post in posts" :key="post.id">
<h3>{{ post.title }}</h3>
<p>{{ post.body }}</p>
<button @click="$emit('delete-post', post.id)">삭제</button>
</li>
</ul>
defineProps<{ posts: Post[] }>()
학습 포인트
개념 |
설명 |
v-for |
전달받은 posts 배열을 반복 렌더링 |
props |
부모로부터 posts 를 전달받음 |
emit |
@click 이벤트를 통해 post.id 를 부모에게 전달 |
:key |
DOM 최적화를 위한 필수 식별자 지정 |
3. usePostStore.ts
– 상태관리 (Pinia, actions, store 사용법)
export const usePostStore = defineStore('post', {
state: () => ({
posts: [] as Post[]
}),
actions: {
addPost(post) {
this.posts.unshift({ ...post, id: Date.now() })
},
deletePost(id) {
this.posts = this.posts.filter(p => p.id !== id)
}
}
})
학습 포인트
개념 |
설명 |
Pinia |
전역 상태 저장소 선언 및 사용 |
defineStore |
상태(posts )와 메서드(addPost , deletePost ) 정의 |
actions |
store 메서드는 this로 state 접근 |
unshift() |
새 글을 목록 앞에 추가 |