Articles with Vue
vue create articles
cd articles
vue add vuex
vue add router
<template>
<div id="app">
<router-view/>
</div>
</template>
App.vue
Index
state: {
article_id : 3,
articles : [
{
id : 1,
title : 'title1',
content : 'content1',
createdAt : new Date().getTime()
},
{
id : 2,
title : 'title2',
content : 'content2',
createdAt : new Date().getTime()
},
]
},
store/index.js
state에 게시글 데이터를 만들어 준다.
필드는 id, 제목, 내용. 생성일자이다.
DB의 auto increment를 표현하기 위해 article_id를 추가로 정의해줘야 한다.
미리 입력한 글 2개가 있기 때문에 다음으로 작성되는 글은 article_id = 3 부터 지정하도록 한다.
const routes = [
{
path : '/',
name : 'index',
component : IndexView
}
]
views/IndexView.vue 를 만들어주고, router/index.js 라우터에 등록해준다.
<template>
<div>
<h1>articles</h1>
{{articles}}
</div>
</template>
<script>
export default {
name : 'IndexView',
computed : {
articles() {
return this.$store.state.articles
}
}
}
</script>
vies/IndexView.vue
state에서 articles를 불러와서 출력한다.
화면 확인
components/ArticleItem.vue 컴포넌트 만들었다.
라우터와 직접적으로 연결되지 않기 때문에 components에 만든다.
<template>
<div>
<h1>articles</h1>
<ArticleItem
v-for="article in articles"
:key = article.id
:article="article" />
</div>
</template>
<script>
import ArticleItem from '@/components/ArticleItem'
export default {
name : 'IndexView',
components : {
ArticleItem
},
computed : {
articles() {
return this.$store.state.articles
}
}
}
</script>
views/IndexView.vue
indexView 컴포넌트에서 ArticleItem 컴포넌트 등록하고,
article을 props 데이터로 전달한다.
<template>
<div>
<p>글 번호 : {{article.id}} </p>
<p>제목 : {{article.title}} </p>
</div>
</template>
<script>
export default {
name : 'ArticleItem',
props :{
article :Object
}
}
</script>
components/ArticleItem.vue
props 데이터 선언하고, 게시글 article을 출력한다.
화면 확인
Create
{
path : '/create',
name : 'create',
component : CreateView
}
router/index.js
views/CreateView.vue 만들고, 라우터 작성한다.
<template>
<div>
<h1>게시글 작성</h1>
<form action="">
<label for="title">제목 : </label>
<input type="text" id='title' v-model.trim='title'><br>
<label for="content">내용 : </label>
<textarea
id="content" cols="30" rows="10"
v-model.trim="content"></textarea><br>
<input type="submit">
</form>
</div>
</template>
<script>
export default {
name : 'CreateView',
data(){
return{
title:null,
content:null
}
}
}
</script>
views/CreateView.vue
Form 생성, 데이터 정의
.trim을 활용해서 입력값의 공백을 제거했다.
화면 확인
<form @submit.prevent="createArticle">
submit 이벤트 발생하면 주소가 바뀌기 때문에, submit 이벤트 동작 취소하고 createArticle메서드를 실행하도록 한다.
methods : {
createArticle(){
const title = this.title
const content = this.content
const payload = {
title, content
}
this.$store.dispatch('createArticle',payload)
}
}
createArticle메서드는 title, content 를 createArticle actions로 보내주면서 호출한다.
methods : {
createArticle(){
const title = this.title
const content = this.content
if (!title){
alert('제목을 입력해주세요')
}else if (!content){
alert('내용을 입력해주세요')
}else{
const payload = {
title, content
}
this.$store.dispatch('createArticle',payload)
}
}
}
제목, 내용이 없을때 경고하기 위한 코드 추가 작성했다.
actions: {
createArticle(context, payload){
const article = {
id : context.state.article_id,
title : payload.title,
contetn : payload.content,
createdAt : new Date().getTime()
}
context.commit('CREATE_ARTICLE',article)
}
},
store/index.js
actions에서는 넘어온 payload 데이터를 활용해서 article 생성해서 mutations을 호출했다.
id로 state의 article_id를 활용했다.
mutations: {
CREATE_ARTICLE(state,article){
state.articles.push(article)
state.article_id = state.article_id+1
}
},
호출된 mutation은 state 를 변경하도록 한다.
mutations에서 전달 된 article 객체를 사용해서 게시글 작성한다.
다음 게시글을 위해 article_id 값을 1 증가한다.
<template>
<div>
<h1>articles</h1>
<router-link :to = "{name : 'create'}"> 게시글 작성</router-link>
<ArticleItem
v-for="article in articles"
:key = article.id
:article="article" />
</div>
</template>
IndexView.vue 컴포넌트에 게시글 작성 페이지로 이동하는 링크를 추가한다. router-link 이용한다.
<router-link :to="{name : 'index'}"> 뒤로가기 </router-link>
views/CreateView.vue
CreateView 컴포넌트에도 Index 페이지로 이동하는 뒤로가기 링크를 추가한다.
여기까지 하면 게시글이 정상적으로 생성된다.
하지만 게시글 작성을 눌러도 아무런 반응이 없다.
게시글 생성 후 index페이지로 이동하도록 네비게이터 작성해야 한다.
this.$router.push({name:'index'})
views/CreateView.vue에서 createArticle() 메서드에 dispatch로 actions를 호출하는 코드 밑에,
index페이지로 이동하는 네비게이터를 작성했다.
Detail
{
path : '/:id',
name : 'detail',
component : DetailView
}
Detail페이지는 주소에 변수가 들어간다.
views/DetailView.vue를 생성하고 router/index.js routes에 등록한다.
path를 :id로 변수 지정한다.
<script>
export default {
name : 'DetailView',
data(){
return {
article:null
}
},
computed : {
articles(){
return this.$store.state.articles
}
}
}
</script>
views/DetailView.vue
computed에 state의 articles를 가져온다.
내가 원하는 특정 article을 담을 데이터 article도 지정한다.
methods : {
getArticleById(){
const id = this.$route.params.id
for (const article of this.articles) {
if (article.id === Number(id)){
this.article = article
break
}
}
}
}
articles에서 동적인자를 통해 받은 id에 해당하는 article을 가져오기 위한 메서드를 작성한다.
동적인자를 통해 받은 id는 str이므로 형변환 해서 비교한다.
찾은 article을 this.article에 저장한다.
<template>
<div>
<h1>Detail</h1>
<p>글 번호 : {{article.id}}</p>
<p>제목 : {{article.title}}</p>
<p>내용 : {{article.content}}</p>
<p>작성시간 : {{article.createdAt}}</p>
</div>
</template>
article 출력한다.
methods : {
getArticleById(id){
for (const article of this.articles) {
if (article.id === Number(id)){
this.article = article
break
}
}
}
},
created(){
this.getArticleById(this.$route.params.id)
}
created lifecycle hook을 통해 인스턴스가 생성되었을 때 article을 가져오는 함수를 호출한다.
화면 확인
현재 state를 통해 데이터를 동기적으로 가져오지만 실제로는 서버로부터 가져온다.
데이터를 가져오는데 시간이 걸린다.
created를 주석처리하고 데이터가 서버로부터 오는데 시간이 걸림을 가정해본다.
created가 없으면, article이 null이기 떄문에 id 등의 속성을 출력할 수 없다.
화면이 출력되지 않고 에러가 발생한다.
optional chaining(?.)을 통해 article 객체가 있을 때만 출력되도록 수정한다.
? 앞의 대상이 없으면 (undefined나 null이면) 에러가 발생하지 않고 undefined를 반환한다.
값이 있으면 뒤의 요소를 이용한다.
다시 created()를 활성화 한다.
Date in JavaScript
js에서 시간을 나타내는 Date객체는 1980년 1월 1일 UTC(협정 세계시) 자정과의 시간차이를 밀리초로 나타내는 정수값을 담는다.
<p>작성 시간 : {{articleCreatedAt}}</p>
computed : {
articles(){
return this.$store.state.articles
},
articleCreatedAt(){
const article = this.article
const createdAt = new Date(article?.createdAt).toLocaleString()
return createdAt
}
},
Date().toLocaleString()을 사용하여 로컬시간으로 변환해주는 computed를 작성해서 출력한다.
새로고침하면 작성시간이 현재 시간으로 새로고침 된다.
현재 DB를 사용하지 않고 있기 때문에, 페이지 새로고침할떄마다 createdAt을 새로 받아오고 있다.
articles에 담아둔 데이터가 새로 렌더링 되기 때문이다.
<router-link :to="{name : 'index'}"> 뒤로가기 </router-link>
index로 이동하는 뒤로가기 링크 추가한다.
<template>
<div @click="goDetail(article.id)">
<p>글 번호 : {{article.id}} </p>
<p>제목 : {{article.title}} </p>
</div>
</template>
<script>
export default {
name : 'ArticleItem',
props :{
article :Object
},
methods : {
goDetail(id){
this.$router.push({name : 'detail', params : {id}})
}
}
}
</script>
components/ArticleItem.vue
각 게시글을 클릭하면 detail페이지로 이동하도록 articleItem에 이벤트를 추가한다.
article.id를 인자로 받아서 params로 보내준다.
methods : {
goDetail(){
this.$router.push({name : 'detail', params : {id: `${this.article.id}`}})
}
}
this.article.id를 이용해서 메서드를 만들어도 된다.
delete
<button @click="deleteArticle">삭제</button><br>
views/DetailView.vue
deleteArticle(){
this.$store.commit('DELETE_ARTICLE', this.article.id)
}
DetailvIew 컴포넌트에 삭제버튼을 만들고, mutations를 호출한다. (actions 생략했다.)
DELETE_ARTICLE(state,id){
state.articles = state.articles.filter((article)=>{
return !(article.id === id)
})
}
store/index.js
삭제하는 방법은 여러가지가 있다.
filter를 이용해서 article.id가 받아온 인자인 특정 id가 아닐 경우에만 state.articles에 반영한다.
id와 같지 않다는 return에서 false인 요소는 삭제되고, true인 요소(id가 일치하지않은요소)만 articles에 저장된다.
deleteArticle(){
this.$store.commit('DELETE_ARTICLE', this.article.id)
this.$router.push({name:'index'})
}
views/DetailView.vue
삭제 후 index 페이지로 이동하도록 네비게이션을 작성한다.
404 not found
{
path : '/404-not-found',
name : 'NotFound404',
component : NotFound404
}
NotFound404 컴포넌트 생성, 라우터 작성
주소가 숫자로 시작하기 때문에 디테일로 가는 /:id로 가버리기 때문에 Detail에 대한 route보다 먼저 등록해주어야 한다. (detail 라우터보다 위에 작성해야 한다.)
path를 /404로 등록하면 404번째 게시글과 혼동되니 그렇게 쓰지 않도록 한다.
getArticleById(id){
for (const article of this.articles) {
if (article.id === Number(id)){
this.article = article
break
}
}
if (!this.article){
this.$router.push({name : 'NotFound404'})
}
},
없는 id에 해당하는 주소가 입력되면, DetailView 컴포넌트에 의해 article을 찾을 것이다.
DetailView 컴포넌트에 id에 해당하는 article이 없으면 404로 이동하도록 한다.
{
path:'*',
redirect : {name : 'NotFound404'}
}
router/index.js
요청한 리소스가 존재하지 않는 경우에 404 page로 redirect 한다.
$router.push처럼 name을 이용해서 이동할 수 있다.
최하단에 작성해야 한다.
'Front-end > Vue.js' 카테고리의 다른 글
서버, 클라이언트 / Vue, Django (Vue with DRF) - Read, Create (0) | 2022.11.14 |
---|---|
404 Not Found (0) | 2022.11.09 |
router 주소 이동 (선언적, 프로그래밍적) / Dynamic Route Matching / lazy-loading / Navigation Guard (전역 가드, 라우트 가드, 컴포넌트 가드) (0) | 2022.11.09 |
Routing , Vue Routing 시작하기 (0) | 2022.11.09 |
UX & UI / Prototyping (0) | 2022.11.09 |