JaeWon's Devlog
article thumbnail
반응형

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(유튜브)에 기초 강의를 올리고 계십니다.

참고해보셔도 좋을 것 같습니다.

 

9Diin

구디사는 개발자🤔 9Diin의 엉망진창 개발 시리즈

www.youtube.com


4. Vuex  구성하기

- Vuex는 Vue.js 애플리케이션에 대한 상태 관리 패턴 + 라이브러리입니다.

- 애플리케이션의 모든 컴포넌트에 대한 중앙 집중식 저장소 역할을 하며 예측 가능한 방식으로 상태를 변경할 수 있습니다.

- Vuex에 대해서 좀 더 자세한 내용은 동료 개발자의 블로그를 참고 부탁드립니다.

 

Vuex란 무엇인가?

Vue.js라는 언어를 가지고 웹 개발을 하는 사람들에게 Vuex는 반드시 사용되어야 할 편리한 도구이다. 최근 Pinia라는 Vue.js의 공식 상태 관리 라이브러리가 새롭게 개발되어 이제 아니 앞으로 Vuex 사

sungjaecloud.tistory.com

 

- 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

- https://sungjaecloud.tistory.com/359

- https://nykim.work/74

- https://carrotweb.tistory.com/118

반응형
profile

JaeWon's Devlog

@Wonol

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!