Vue + SpringBoot + Mysql 를 이용한 Todo 구현(1) - 프로젝트 구성하기
Vue + SpringBoot + Mysql 를 이용한 Todo 구현(2) - 프로젝트 환경 설정하기
Vue + SpringBoot + Mysql 를 이용한 Todo 구현(3) - Todo API 개발하기(1)
Vue + SpringBoot + Mysql 를 이용한 Todo 구현(4) - Todo API 개발하기(2)
Vue + SpringBoot + Mysql 를 이용한 Todo 구현(5) - Todo 화면 개발하기(1) - 컴포넌트 구성
(끝) Vue + SpringBoot + Mysql 를 이용한 Todo 구현(7) - Todo 화면 개발하기(3) - 화면 개발
이전 글에서는 화면 컴포넌트 구성 및 css를 적용하였습니다.
이번 글에서는 각 화면 컴포넌트의 기능을 구현해보도록 하겠습니다.
번외로... 지인 프론트 개발자분이 Vue.js 에 대해서 조그맣게 Youtube(유튜브)에 기초 강의를 올리고 계십니다.
참고해보셔도 좋을 것 같습니다.
4. Vuex 구성하기
- Vuex는 Vue.js 애플리케이션에 대한 상태 관리 패턴 + 라이브러리입니다.
- 애플리케이션의 모든 컴포넌트에 대한 중앙 집중식 저장소 역할을 하며 예측 가능한 방식으로 상태를 변경할 수 있습니다.
- Vuex에 대해서 좀 더 자세한 내용은 동료 개발자의 블로그를 참고 부탁드립니다.
- Vuex 를 사용하기 위해 아래 명령어를 통해 설치합니다.
npm install vuex --save
- 추가로 서버 통신을 위해 axios도 같이 설치합니다.
npm install axios
- vuex를 관리하기 위해 store.js를 생성합니다.
- store.js 는 아래와 같이 구성됩니다.(구성은 아래와 설명되어있지만, 해당 글에서는 올바르게 사용되지 않을 수 있습니다... 틀린 부분은 언제든지 지적해주세요!!!)
- state(상태 변수)
- getters(가져오는 함수)
- mutations(변경 함수 / 설정하는 함수)
- actions(액션 함수 / 실행하는 함수)
- 아래 이미지와 같이 파일을 생성합니다.
4-1. store.js
import { createApp } from 'vue'
import Vuex from 'vuex'
import storage from "./modules/storage";
import * as getters from "./modules/getters";
import * as mutations from "./modules/mutations";
const app = createApp();
app.use(Vuex);
export const store = new Vuex.Store({
state: {
todoItems: storage.fetch(),
userName: storage.fetchName(),
todoOldestOrder: true
},
getters: getters,
mutations: mutations
});
- state : 상태 변수
- vuex는 single state tree(단일 상태 트리)를 사용한다.
- 애플리케이션마다 하나의 저장소만 갖게 된다는 것을 의미한다.
- state는 key:value로 구성되며, 콤마(,)로 다중으로 선언 가능하다.
- todoItems : storage.fetch()
- 화면에서 Todo 아이템 목록.
- storage.js 에 있는 fetch() 함수를 통해서 관리한다.(아래에서 설명) - userName: strage.fetchName()
- 화면에서의 사용자 이름.
- storage.js 에 있는 fetchName() 함수를 통해서 관리한다. - todoOldestOrder
- 아이템 목록의 정렬 순서
- getters : state를 계산 또는 변형 시 호출
- getters는 콤마(,)로 다중 함수를 선언 가능하다.
- 함수의 첫 번째 인자는 state(상태)이고 두 번째 인자는 getters 이다.
- getters.js 에서 관리한다.(아래에서 설명)
- mutations : state를 변경할 때 호출
- state는 직접 변경할 수 없다.
- 핸들러 함수를 통해서 state를 변경해야 한다.(setter 로 생각)
- 함수의 첫 번째 인자는 state(상태)이고 두 번째 인자는 payload(전송되는 데이터)이다.
- mutations.js 에서 관리한다.(아래에서 설명)
4-2. storage.js
import axios from 'axios';
import {store} from "@/store/store";
const storage = {
async fetch(orderState) {
var setState = true;
/* 서버 통신 */
const arr = [];
if(orderState == undefined || orderState === null){
setState == true;
}
else if(orderState != null || orderState != ""){
setState = orderState;
}
await axios
.get('/todos/' + setState)
.then(res => {
const jsonData = res.data;
if(jsonData.length > 0){
for(let i = 0 ; i < jsonData.length; i++){
arr.push(
jsonData[i]
);
}
}
});
store.state.todoItems = arr;
},
fetchName() {
// 로컬 스토리지의 사용자 이름 가져오기
if (localStorage.getItem("userName")) {
const userName = localStorage.getItem("userName");
return userName;
}
}
}
export default storage;
- fetch()
- 아이템 목록을 조회한다.
- async await 을 사용하여 비동기로 관리한다.
- axios를 사용하여 서버와 통신하며, api의 경로인 /todos/true 를 호출하여, List 데이터를 state의 todoItems에 저장한다.
- fetchName()
- 사용자 이름을 저장한다.
- 로컬 스토리지를 사용하여 사용자의 이름을 관리한다.
4-3. getters.js
const storedTodoItems = (state) => {
return state.todoItems;
};
const storedName = (state) => {
return state.userName;
};
const storedTodoItemsCount = (state, getters) => {
return getters.storedTodoItems.length;
}
export { storedTodoItems, storedName, storedTodoItemsCount };
- storedTodoItems()
- state 에 있는 todoItems 를 가져온다.
- storedName()
- state 에 있는 userName 을 가져온다.
- sotredTodoItemsCount()
- state 에 있는 todoItems의 총개수를 가져온다.
4-4. mutations.js
- mutations.js 를 개발하기 전에 이전 TodoTitle에 created()에 있는 날짜 관련 함수를 getDate.js 파일로 만들어 관리한다.
- src/assets/common/getDate.js
// getDate.js
export default () => {
const now = new Date();
const month = now.getMonth() + 1;
const date = now.getDate();
const weekList = new Array(
"Sun.",
"Mon.",
"Tue.",
"Wed.",
"Thu.",
"Fri.",
"Sat."
);
const week = weekList[now.getDay()];
const time = now.getTime();
const hour = now.getHours();
let daytime = "";
if (hour < 12) {
daytime = 'morning';
} else if (hour < 18) {
daytime = 'afternoon';
} else {
daytime = 'evening';
}
const dateInfo = {
month,
date,
week,
time,
daytime
}
return dateInfo
}
- 아래에 있는 기능들이 actions.js로 구성되어야 할지, mutation.js로 구성되어야 할지 아직 잘 모르겠다... 일단, mutations.js 에 개발하였다...
import getDate from "./../../assets/common/getDate";
import axios from 'axios';
import storage from "@/store/modules/storage";
// 아이템 하나 추가
const addOneItem = async (state, todoItem) => {
/* 서버 통신 */
var jsonValue = {
item: todoItem,
date: `${getDate().date} ${getDate().week}`,
time: getDate().time,
completed: false
}
await axios
.post('/todos/save', JSON.stringify(jsonValue))
.then(res => {
if(res.data == "ok"){
storage.fetch(state.todoOldestOrder);
} else {
alert("등록 실패");
}
});
}
// 아이템 하나 삭제
const removeOneItem = (state, payload) => {
/* 서버 통신 */
axios
.put('/todos/delete/' + payload.todoItem.id)
.then(res => {
if(res.data == "ok"){
storage.fetch(state.todoOldestOrder);
} else {
alert("삭제 실패");
}
});
}
// 아이템 하나 완료 토글
const toggleOneItem = (state, payload) => {
/* 서버 통신 */
var jsonValue = {
id: payload.todoItem.id,
completed: !payload.todoItem.completed
}
axios
.put('/todos/' + payload.todoItem.id, JSON.stringify(jsonValue))
.then(res => {
if(res.data == "ok"){
storage.fetch(state.todoOldestOrder);
} else {
alert("업데이트 실패");
}
});
}
// 모든 아이템 삭제
const clearAllItem = (state) => {
var todoItems = state.todoItems;
if(todoItems.length > 0){
axios
.put('/todos/clear')
.then(res => {
if(res.data == "ok"){
storage.fetch(state.todoOldestOrder);
} else {
alert("클리어 실패");
}
});
}
}
// 최신순 정렬
const sortTodoLatest = (state) => {
state.todoOldestOrder = false;
storage.fetch(state.todoOldestOrder);
}
// 오래된 순 정렬
const sortTodoOldest = (state) => {
state.todoOldestOrder = true;
storage.fetch(state.todoOldestOrder);
}
// 사용자 이름 추가
const setUserName = (state, userName) => {
localStorage.setItem("userName", userName);
state.userName = userName;
}
export { addOneItem, removeOneItem, toggleOneItem, clearAllItem, sortTodoLatest, sortTodoOldest, setUserName };
- addOneItem()
- Todo 아이템을 추가한다.
- async await 을 사용하여 비동기 관리한다.
- axios를 사용하여 서버와 통신하며, api의 경로인 /todos/save 를 호출하여, json 으로 저장하고자 하는 데이터를 전달한다.
- 저장 이후 storage.fetch()를 호출하여 todoItems를 다시 조회한다.
- removeOneItem()
- Todo 아이템을 삭제한다.
- axios를 사용하여 서버와 통신하며, api의 경로인 /todos/delete/id 를 호출하여, id에 맞는 아이템을 삭제한다.
- 삭제 이후 storage.fetch()를 호출하여 todoItems를 다시 조회한다.
- toggleOneItem()
- Todo 완료/비완료 처리한다.
- axios를 사용하여 서버와 통신하며, api의 경로인 /todos/id 를 호출하여, id에 맞는 아이템을 상태 변경한다.
- 수정 이후 storage.fetch()를 호출하여 todoItems를 다시 조회한다.
- clearAllItem()
- Todo 목록을 전체 삭제한다.
- axios를 사용하여 서버와 통신하며, api의 경로인 /todos/clear 를 호출하여, Todo 리스트를 삭제한다.
- 삭제 이후 storage.fetch()를 호출하여 todoItems를 다시 조회한다.
- sortTodoLatest()
- 최신 작성 순으로 정렬한다.
- 정렬 상태를 전달하면서, storage.fetch()를 호출하여 todoItems를 다시 조회한다.
- sortTodoOldest()
- 오래된 작성 순으로 정렬한다.
- 정렬 상태럴 전달하면서, storage.fetch()를 호출하여 todoItems를 다시 조회한다.
- setUserName()
- 사용자 이름을 관리한다.
- 로컬 스토리지에서 사용자 이름을 가져와서 관리한다.
5. 서버 통신을 위한 추가 설정하기
- 서버 통신을 위해서 main.js 를 수정한다.
import { createApp } from 'vue'
import App from './App.vue'
import { store } from './store/store';
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:8787/';
axios.defaults.headers.get['Content-Type'] = 'application/json';
axios.defaults.headers.post['Content-Type'] = 'application/json';
axios.defaults.headers.put['Content-Type'] = 'application/json';
const app = createApp(App);
app.config.globalProperties.axios = axios;
app.use(store).mount('#app')
- axios.defaults.baseURL
- axios 통신 시 설정되는 기본 URL이다.(즉, BackEnd 프로젝트를 실행시켰을 때의 URL 정보)
- axios.defaults.headers.get/post/put
- axios 통신 시 헤더 정보의 상태를 설정한다.
- 기본적으로 json으로 통신을 하기 때문에, application/json 으로 설정하여 통신한다.
- 해당 내용을 추가하는 이유는 기본적으로 x-www-form-urlencoded 로 설정되어 있어, restful 통신 시 415 에러가 발생한다.
- Vue.js에서 서버 통신까지 구현하였습니다. 이제 각 컴포넌트에서 해당 기능들을 가져와 사용하며 화면 개발을 마무리하도록 하겠습니다.
참고
- https://www.inflearn.com/course/age-of-vuejs/dashboard