vue create todo2
npm install axios
{
"todos": [
{ "id": 1, "title": "과제", "done": false },
{ "id": 2, "title": "시험공부", "done": false },
{ "id": 3, "title":"코딩훈련", "done": false }
]
}
json-server --watch src/todos-db.json
<template>
<div id="MyTodo">
<h1>할 일</h1>
<table>
<tr><td>ID</td><td>할일</td></tr>
<tr v-for="todo in todos" v-bind:key="todo.id">
<td>{{ todo.id }}</td>
<td>{{ todo.title }}</td>
</tr>
</table>
</div>
</template>
<script>
import axios from 'axios';
axios.defaults.baseURL = "http://localhost:3000";
// 백엔드 API URL 앞 부분을 미리 설정한다
export default {
name: "MyTodo",
data() {
return {
todos: [ ]
}
},
mounted() { // 컴포넌트가 처음 화면에 보이게 될 때, 자동으로 호출되는 메소드
this.reloadTodos(); // 데이터를 로드한다
},
methods: {
async reloadTodos() { // 백엔드 API를 호출하여 데이터를 로드한다
try {
const response = await axios.get("/todos");
this.todos = response.data;
} catch (error) {
alert('조회 에러: ' + (error instanceof Error ? error.message : error));
}
}
}
}
</script>
<style scoped>
div#MyTodo { padding: 30px; margin: 30px auto; width: 400px;
border: 1px solid #ccc; box-shadow: 3px 3px 3px #aaa; }
h1 { border-bottom: 1px solid gray; }
table { border-collapse: collapse; margin: 20px 0; width: 100%; }
tr:nth-child(1) { background-color: #eee; text-align: center; }
td { border: 1px solid gray; padding: 6px; }
td:nth-child(1) { text-align: center; width: 30px; }
</style>
처음에 json 서버를 끄고 배시셸에 npm run serve
를 실행시키는 줄 알았다..그랬더니 AxiosError 발생..
진짜 거짓말 안하고 한 3시간 동안 그 오류를 해결하려 했는데 강의 영상 다시 보니.. 배시셸을 두개 만들어서 하나는 json 서버 하나는 뷰를 실행해야했다....
const response = await axios.get("/todos");
this.todos = response.data;
<template>
<div>
<h1>할 일</h1>
<table>
<tr><td>ID</td><td>할일</td></tr>
<tr v-for="todo in todos" v-bind:key="todo.id">
<td>{{ todo.id }}</td>
<td>{{ todo.title }}</td>
</tr>
</table>
<input type="text" v-model="title" />
<button type="button" v-on:click="addTodo">추가</button>
</div>
</template>
<script>
import axios from 'axios';
axios.defaults.baseURL = "http://localhost:3000";
export default {
name: "MyTodo",
data() {
return {
todos: [ ],
title: ""
}
},
mounted() {
this.reloadTodos();
},
methods: {
async reloadTodos() {
try {
const response = await axios.get("/todos");
this.todos = response.data;
} catch (error) {
alert('조회 에러: ' + (error instanceof Error ? error.message : error));
}
},
async addTodo() {
try {
const todo = {id: 0, title: this.title};
await axios.post("/todos", todo); // 새 todo를 백엔드에 등록한다
this.reloadTodos(); // todo 목록 다시 로드
this.title = ""; // input 태그를 빈칸으로
} catch (error) {
alert('저장 에러: ' + (error instanceof Error ? error.message : error));
}
}
}
}
</script>
<style scoped>
h1 { border-bottom: 1px solid gray; }
div { padding: 30px; margin: 30px auto; width: 400px;
border: 1px solid #ccc; box-shadow: 3px 3px 3px #aaa; }
table { border-collapse: collapse; margin: 20px 0; width: 100%; }
tr:nth-child(1) { background-color: #eee; text-align: center; }
td { border: 1px solid gray; padding: 6px; }
td:nth-child(1) { text-align: center; width: 30px; }
input[type=text] { padding: 5px; margin-right: 5px; width: 300px; }
button { padding: 0.3em 1.5em; }
</style>
async addTodo() {
try {
const todo = {id: 0, title: this.title};
await axios.post("/todos", todo); // 새 todo를 백엔드에 등록한다
this.reloadTodos(); // todo 목록 다시 로드
this.title = ""; // input 태그를 빈칸으로
} catch (error) {
alert('저장 에러: ' + (error instanceof Error ? error.message : error));
}
}
const todo = {id: 0, title: this.title};
객체의 id: 0
으로 설정된 이유는 서버에서 고유한 ID를 할당받기 때문에, 여기서는 임시로 0으로 설정하여 새로운 할 일 항목임을 나타내고, 나중에 서버로부터 ID가 할당되면 업데이트
Post 방식을 이용하여 http://localhost:3000/todos API 주소와 todo 객체 인자로 넘겨 데이터를 보내준다.
await axios.post("/todos", todo);
<template>
<div>
<h1>할 일</h1>
<table>
<tr><td>ID</td><td>할일</td></tr>
<tr v-for="todo in todos" v-bind:key="todo.id">
<td>{{ todo.id }}</td>
<td>
{{ todo.title }}
<span v-on:click="deleteTodo(todo.id)">x</span>
</td>
</tr>
</table>
<input type="text" v-model="title" />
<button type="button" v-on:click="addTodo">추가</button>
</div>
</template>
<script>
import axios from 'axios';
axios.defaults.baseURL = "http://localhost:3000";
default {
name: "MyTodo",
data() {
return {
todos: [ ],
title: ""
}
},
mounted() {
this.reloadTodos();
},
methods: {
async reloadTodos() {
try {
const response = await axios.get("/todos");
this.todos = response.data;
} catch (error) {
alert('조회 에러: ' + (error instanceof Error ? error.message : error));
}
},
async addTodo() {
try {
const todo = {id: 0, title: this.title};
await axios.post("/todos", todo);
this.reloadTodos();
this.title = "";
} catch (error) {
alert('저장 에러: ' + (error instanceof Error ? error.message : error));
}
},
async deleteTodo(id) {
try {
if (confirm("삭제하시겠습니까?") == false) return;
await axios.delete("/todos/" + id);
this.reloadTodos();
} catch (error) {
alert('삭제 에러: ' + (error instanceof Error ? error.message : error));
}
}
}
}
</script>
<style scoped>
h1 { border-bottom: 1px solid gray; }
div { padding: 30px; margin: 30px auto; width: 400px;
border: 1px solid #ccc; box-shadow: 3px 3px 3px #aaa; }
table { border-collapse: collapse; margin: 20px 0; width: 100%; }
tr:nth-child(1) { background-color: #eee; text-align: center; }
td { border: 1px solid gray; padding: 6px; }
td:nth-child(1) { text-align: center; width: 30px; }
input[type=text] { padding: 5px; margin-right: 5px; width: 300px; }
button { padding: 0.3em 1.5em; }
span { font-weight: bold; margin-left: 10px; cursor: pointer; float: right; }
</style>
async deleteTodo(id) {
try {
if (confirm("삭제하시겠습니까?") == false) return;
await axios.delete("/todos/" + id);
this.reloadTodos();
} catch (error) {
alert('삭제 에러: ' + (error instanceof Error ? error.message : error));
}
}
<template>
<div>
<h1>할 일</h1>
<table>
<tr><td>ID</td><td>할일</td></tr>
<tr v-for="todo in todos" v-bind:key="todo.id" v-bind:class="{done: todo.done}">
<td>{{ todo.id }}</td>
<td>
<input type="checkbox" v-model="todo.done" v-on:click="toggleTodo(todo)"/>
{{ todo.title }}
<span v-on:click="deleteTodo(todo.id)">x</span>
</td>
</tr>
</table>
<input type="text" v-model="title" />
<button type="button" v-on:click="addTodo">추가</button>
</div>
</template>
<script>
import axios from 'axios';
axios.defaults.baseURL = "http://localhost:3000";
export default {
name: "MyTodo",
data() {
return {
todos: [ ],
title: ""
}
},
mounted() {
this.reloadTodos();
},
methods: {
async reloadTodos() {
try {
const response = await axios.get("/todos");
this.todos = response.data;
} catch (error) {
alert('조회 에러: ' + (error instanceof Error ? error.message : error));
}
},
async addTodo() {
try {
const todo = {id: 0, title: this.title };
await axios.post("/todos", todo);
this.reloadTodos();
this.title = "";
} catch (error) {
alert('저장 에러: ' + (error instanceof Error ? error.message : error));
}
},
async deleteTodo(id) {
try {
if (confirm("삭제하시겠습니까?") == false) return;
await axios.delete("/todos/" + id);
this.reloadTodos();
} catch (error) {
alert('삭제 에러: ' + (error instanceof Error ? error.message : error));
}
},
async toggleTodo(todo) {
try {
todo.done = !todo.done;
await axios.put("/todos/" + todo.id, todo);
this.reloadTodos();
} catch (error) {
alert('저장 에러: ' + (error instanceof Error ? error.message : error));
}
},
}
}
</script>
<style scoped>
h1 { border-bottom: 1px solid gray; }
div { padding: 30px; margin: 30px auto; width: 400px;
border: 1px solid #ccc; box-shadow: 3px 3px 3px #aaa; }
table { border-collapse: collapse; margin: 20px 0; width: 100%; }
tr:nth-child(1) { background-color: #eee; text-align: center; }
td { border: 1px solid gray; padding: 6px; }
td:nth-child(1) { text-align: center; width: 30px; }
input[type=text] { padding: 5px; margin-right: 5px; width: 300px; }
button { padding: 0.3em 1.5em; }
span { font-weight: bold; margin-left: 10px; cursor: pointer; float: right; }
tr.done { background-color: #f8f8f8; color: #bbb; text-decoration: line-through; }
input[type=checkbox] { accent-color: #bbb; }
</style>
async toggleTodo(todo) {
try {
todo.done = !todo.done;
await axios.put("/todos/" + todo.id, todo);
this.reloadTodos();
} catch (error) {
alert('저장 에러: ' + (error instanceof Error ? error.message : error));
}
},
수정은 Put 방식으로 데이터를 보냄. http://localhost:3000/todos/todos.id의 API 주소와 todo 객체를 넘겨 수정(done 속성값이 변경된 todo 객체)
<input type="checkbox" v-model="todo.done" v-on:click="toggleTodo(todo)"/>
이 코드에서 체크 박스를 클릭하면, 아직 체크 상태가 변경되기도 전에 클릭 이벤트가 먼저 발생하여 toggleTodo(todo) 메소드가 호출
따라서 아직 체크 상태가 변경되기도 전에 메소드가 호출되었으므로 done 속성값을 method에서 변경해야함.
todo.done의 초기 값은 false
만약 사용자가 체크박스를 클릭했다면 toggleTodo 메소드가 호출되고 false였던 todo.done의 값을 반전 시킴 ▶︎ rue
값이 true가 되면 v-bind:class="{done: todo.done} style 태그에 정의된 속성도 true가 되므로 css서식이 바뀜