Vue3 - 게시판 CRUD - UI 구성, 라우터 맵핑
목차
게시판 CRUD를 하기 위해 우선 페이지 컴포넌트를 만들고 라우터 맵핑을 진행한다.
views/posts
경로에 PostCreateView.vue
, PostListView.vue
, PostEditView.vue
, PostDetailView.vue
CRUD 역할을 할 4개의 페이지 컴포넌트를 생성한다. UI는 대충 하자. 이게 중요한게 아니니깐.
그런다음 이 페이지를 라우터로 이동할 수 있게끔 맵핑해주어야 한다. router/index.js
로 이동.
기존에 있던 컴포넌트 임포트 방식과 동일하게 컴포넌트를 추가한다.
import PostCreateView from '@/views/posts/PostCreateView.vue';
import PostDetailView from '@/views/posts/PostDetailView.vue';
import PostEditView from '@/views/posts/PostEditView.vue';
import PostListView from '@/views/posts/PostListView.vue';
그리고 routes에 이어서 맵핑 추가
{
path: '/posts',
name: 'PostList',
component: PostListView
},
{
path: '/posts/create',
name: 'PostCreate',
component: PostCreateView
},
{
path: '/posts/:id',
name: 'PostDetail',
component: PostDetailView
},
{
path: '/posts/:id/edit',
name: 'PostEdit',
component: PostEditView
}
설명해주자면 :id
는 같은 url인데 다른 데이터가 존재해야하는 곳에 :네임
이런식으로 콜론과 params 이름을 붙이면 된다.
예를 들어보자, 유저가 150명이다. 근데 유저 이름이 제 각각 다르다. A, B, C 그럼 페이지 라우터를 /A /B /C 이렇게 하나하나 수정할 수 없는 노릇이다. 그래서 :paramName
이런식으로 맵핑하여 $route.params.id
이런식으로 라우트의 파람 값을 가져와서 데이터 를 불러오는 방식으로 사용하는 것이 훨~씬 낫다.
$route.params
를 콘솔이나 템플릿 코드안에 값을 찍어보면 이해될 것임.
그 외에도 query
, hash
를 이용할 수 있음
/post?search=Daniel
>> $route.query.search는 Daniel 값
이 찍히는 것을 알 수 있음.
해쉬는 /search#hashValue
면 $route.hash
를 찍어보면 >> #hashValue
값이 출력됨
이제 Header.vue
를 좀 꾸미고 클릭 이벤트를 구현하여 라우터를 이동시키자.
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<RouterLink class="nav-link" active-class="active" to="/">Home</RouterLink>
</li>
<li class="nav-item">
<RouterLink class="nav-link" active-class="active" to="/about">About</RouterLink>
</li>
<li class="nav-item">
<RouterLink class="nav-link" active-class="active" to="/posts">게시글</RouterLink>
</li>
</ul>
<div class="d-flex">
<button class="btn btn-outline-light" type="button" @click="goPage">글쓰기</button>
</div>
</div>
그 다음 goPage()
함수 기능을 작성
<script setup>
import { useRouter } from 'vue-router';
const router = useRouter();
const goPage = () => {
router.push('/posts/create');
};
</script>
이제 Bootstrap을 활용하여 마크업을 진행한다. (알아서)
부트스트랩의 Form control 예제 코드를 긁어와서 수정한다.
PostCreateView.vue
<template>
<div>
<h2>게시글 등록</h2>
<hr class="my-4" />
<form @submit.prevent.stop>
<div class="mb-3">
<label for="title" class="form-label">제목</label>
<input type="text" class="form-control" id="title" />
</div>
<div class="mb-3">
<label for="content" class="form-label">내용</label>
<textarea class="form-control" id="content" rows="3"></textarea>
</div>
<div class="pt-4">
<button type="button" class="btn btn-outline-dark me-2" @click="goBack">목록</button>
<button type="submit" class="btn btn-primary">저장</button>
</div>
</form>
</div>
</template>
그 다음 PostLiveView.vue
를 마크업 할건데 부트스트랩 Cards 컴포넌트의 마음에 드는 예제 코드를 사용한다.
우선, components/posts
경로를 생성하고, PostItem.vue
를 만들어서 마크업과 Script 코드에 Setup 방식으로 Props를 정의한다.
<script setup>
defineProps({
title: {
type: String,
required: true // 필수 값
},
content: {
type: String,
required: true
},
createdAt: {
type: [String, Date, Number] // String, Date, Number 중 들어오는 타입 허용
}
});
</script>
<template>
<div class="card">
<div class="card-body">
<h5 class="card-title">{{ title }}</h5>
<p class="card-text">{{ content }}</p>
<p class="text-muted">
{{ createdAt }}
</p>
</div>
</div>
</template>
<style scoped></style>
이제 PostLiveView.vue
로 가서 리스트에 뿌려줄 아이템 컴포넌트를 소환한다.
<script setup>
import PostItem from '@/components/posts/PostItem.vue';
</script>
<template>
<div>
<h2>게시글 리스트</h2>
<hr class="my-4" />
<PostItem />
</div>
</template>
Api
src/api
경로에 posts.js
를 만들자.
// axios
const posts = [
{ id: 1, title: '제목1', content: '내용1', createdAt: '2021-02-11' },
{ id: 2, title: '제목2', content: '내용2', createdAt: '2023-02-01' },
{ id: 3, title: '제목3', content: '내용3', createdAt: '2023-03-01' },
{ id: 4, title: '제목4', content: '내용4', createdAt: '2023-04-01' },
{ id: 5, title: '제목5', content: '내용5', createdAt: '2023-05-01' },
{ id: 6, title: '제목6', content: '내용6', createdAt: '2023-06-01' },
{ id: 7, title: '제목7', content: '내용7', createdAt: '2023-07-01' }
];
export function getPosts() {
return posts;
}
이제부터 아래는 소스코드이다.
PostListView.vue
<script setup>
import PostItem from '@/components/posts/PostItem.vue';
import { getPosts } from '@/api/posts';
import { ref } from 'vue';
import { useRouter } from 'vue-router';
const posts = ref([]);
const router = useRouter();
const fetchPosts = () => {
posts.value = getPosts();
};
fetchPosts();
const goPage = id => {
// router.push(`/posts/${id}`);
// 다른 방법
router.push({
// 이름을 가진 라우터는 이렇게
name: 'PostDetail',
params: {
id
}
});
};
</script>
<template>
<div>
<h2>게시글 리스트</h2>
<hr class="my-4" />
<div class="row g-3">
<div class="col-4" v-for="post in posts" :key="post.id">
<PostItem :title="post.title" :content="post.content" :created-at="post.createdAt" @click="goPage(post.id)" />
</div>
</div>
</div>
</template>
<style scoped></style>
postCreateView.vue
<script setup>
import { useRouter } from 'vue-router';
const router = useRouter();
const goListPage = () => {
router.push({ name: 'PostList' });
};
</script>
<template>
<div>
<h2>게시글 등록</h2>
<hr class="my-4" />
<form @submit.prevent.stop>
<div class="mb-3">
<label for="title" class="form-label">제목</label>
<input type="text" class="form-control" id="title" />
</div>
<div class="mb-3">
<label for="content" class="form-label">내용</label>
<textarea class="form-control" id="content" rows="3"></textarea>
</div>
<div class="pt-4">
<button type="button" class="btn btn-outline-dark me-2" @click="goListPage">목록</button>
<button type="submit" class="btn btn-primary">저장</button>
</div>
</form>
</div>
</template>
<style scoped></style>
PostDetailView.vue
<script setup>
import { useRouter, useRoute } from 'vue-router';
const route = useRoute();
const router = useRouter();
const id = route.params.id;
const goListPage = () => {
router.push({
name: 'PostList'
});
};
const goEditPage = () => {
router.push({
name: 'PostEdit',
params: {
id
}
});
};
</script>
<template>
<div>
<h2>제목</h2>
<p>내용</p>
<p class="text-muted">2020-01-01</p>
<hr class="my-4" />
<div class="row g-2">
<div class="col-auto">
<button class="btn btn-outline-dark">이전글</button>
</div>
<div class="col-auto">
<button class="btn btn-outline-dark">다음글</button>
</div>
<div class="col-auto me-auto"></div>
<div class="col-auto">
<button class="btn btn-outline-dark" @click="goListPage">목록</button>
</div>
<div class="col-auto">
<button class="btn btn-outline-primary" @click="goEditPage">수정</button>
</div>
<div class="col-auto">
<button class="btn btn-outline-danger">삭제</button>
</div>
</div>
</div>
</template>
<style scoped></style>
PostEditView.vue
<script setup>
import { useRoute, useRouter } from 'vue-router';
const router = useRouter();
const route = useRoute();
const goDetailPage = () => {
router.push({
name: 'PostDetail',
params: {
id: route.params.id
}
});
};
</script>
<template>
<div>
<h2>게시글 수정</h2>
<hr class="my-4" />
<form @submit.prevent.stop>
<div class="mb-3">
<label for="title" class="form-label">제목</label>
<input type="text" class="form-control" id="title" />
</div>
<div class="mb-3">
<label for="content" class="form-label">내용</label>
<textarea class="form-control" id="content" rows="3"></textarea>
</div>
<div class="pt-4">
<button type="button" class="btn btn-outline-danger me-2" @click="goDetailPage">취소</button>
<button type="submit" class="btn btn-primary">수정</button>
</div>
</form>
</div>
</template>
<style scoped></style>
TheHeader.vue
글쓰기 버튼에 함수를 추가한다.
<div class="d-flex">
<button class="btn btn-outline-light" type="button" @click="goPage">글쓰기</button>
</div>
<script setup>
import { useRouter } from 'vue-router';
const router = useRouter();
const goPage = () => {
router.push({
name: 'PostCreate'
});
};
'🖥Frontend > Vue.js' 카테고리의 다른 글
Vue3 - 페이지 컴포넌트에 Props 전달과 ref, reactive.. (복사/재귀함수) (2) | 2023.01.31 |
---|---|
Vue3 - Not Found, 중첩된 라우트(Nested Routes) (0) | 2023.01.31 |
Vue3 - 뷰 라우터 Vue Router (0) | 2023.01.31 |
Vue3 - 프로젝트 구성, ESLint, Prettier 설정, Bootstrap 적용 (0) | 2023.01.31 |
Vue3 - 웹팩(Webpack)은 무엇이고 설정은 어떻게 (0) | 2023.01.18 |
댓글