세 번째로 Vuex PersistedState를 사용하는 방법을 알아보도록 하겠습니다.
Vuex PersistedState는 Local Storage(로컬 스토리지)를 사용하여 state를 저장하고 유지합니다.
Vuex PersistedState 설치하기
콘솔을 실행하고 Vue 프로젝트가 있는 C:\workspaces\nodeserver\testvue 폴더로 이동합니다.
Vue 프로젝트에 vuex-persistedstate를 설치하기 위해 콘솔에서 npm install 명령어를 실행합니다.
npm install --save vuex-persistedstate
또는 버전 적용
npm install --save vuex-persistedstate@4.1.0
npm install에 옵션으로 --save를 추가하면 자동으로 package.json 파일의 "dependencies"에 "vuex-persistedstate" 항목이 추가됩니다.
package.json 파일의 "dependencies"입니다.
"dependencies": {
"axios": "^0.21.1",
"core-js": "^3.6.5",
"vue": "^3.0.0",
"vue-router": "^4.0.0-0",
"vue3-cookies": "^1.0.6",
"vuex": "^4.0.2",
"vuex-persistedstate": "^4.1.0" --> 추가
}
테스트를 위해 cookie(쿠키)의 데이터는 삭제해 주시기 바랍니다.
그리고 C:\workspaces\nodeserver\testvue\src\store\modules\login.js 파일을 오픈합니다.
cookie가 적용된 부분을 주석 처리하거나 삭제합니다.
import axios from 'axios';
//import { useCookies } from "vue3-cookies";
const axiosRefresh = axios.create();
//const { cookies } = useCookies();
mutations에서 reset() 함수에 있는 cookie를 주석 처리하거나 삭제합니다.
saveStateToStorage() 함수와 readStateFromStorage() 함수를 주석 처리하거나 삭제합니다.
mutations: {
// memberId를 설정합니다.
setMmemberId(state, memberId) {
state.memberId = memberId;
},
// Access-Token를 설정합니다.
setAccessToken(state, accessToken) {
state.accessToken = accessToken;
},
// Refresh-Token를 설정합니다.
setRefreshToken(state, refreshToken) {
state.refreshToken = refreshToken;
},
// 초기화시킵니다.
reset(state) {
state.memberId = '';
state.accessToken = '';
state.refreshToken = '';
//cookies.remove("testvue.login.memberId");
//cookies.remove("testvue.login.accessToke");
//cookies.remove("testvue.login.refreshToken");
//cookies.remove("testvue.login");
}
/*
// Storage에 state를 저장합니다.
saveStateToStorage(state) {
//cookies.set("testvue.login.memberId", state.memberId, 60 * 60 * 24 * 30);
//cookies.set("testvue.login.accessToke", state.accessToken, 60 * 60 * 24 * 30);
//cookies.set("testvue.login.refreshToken", state.refreshToken, 60 * 60 * 24 * 30);
//cookies.set("testvue.login", JSON.stringify(state), 60 * 60 * 24 * 30);
},
// Storage에서 state를 읽어옵니다.
readStateFromStorage(state) {
//if (cookies.get("testvue.login.memberId") != null) {
// state.memberId = cookies.get("testvue.login.memberId");
//}
//if (cookies.get("testvue.login.accessToken") != null) {
// state.accessToken = cookies.get("testvue.login.accessToken");
//}
//if (cookies.get("testvue.login.refreshToken") != null) {
// state.refreshToken = cookies.get("testvue.login.refreshToken");
//}
//if (cookies.get("testvue.login") != null) {
// let storage = cookies.get("testvue.login");
// if (storage.memberId != null) {
// state.memberId = storage.memberId;
// }
// if (storage.accessToken != null) {
// state.accessToken = storage.accessToken;
// }
// if (storage.memberId != null) {
// state.refreshToken = storage.refreshToken;
// }
//}
}
*/
}
actions에서 doLogin() 함수에서 commit('saveStateToStorage') 메서드를 주석 처리하거나 삭제합니다. 그리고 doReadStateFromStorage() 함수를 주석 처리 또는 삭제합니다.
actions: {
// 로그인합니다.
async doLogin({ commit }, memberInfo) {
let result = false;
let resultErr = null;
try {
let res = await axios.post("http://localhost:9000/members/login", memberInfo);
if (res.data.success == true) {
console.log("로그인되었습니다.");
commit('setMmemberId', memberInfo.id);
commit('setAccessToken', res.data.accessToken);
commit('setRefreshToken', res.data.refreshToken);
// Storage에 state를 저장합니다.
//commit('saveStateToStorage');
axios.defaults.headers.common['Access-Token'] = res.data.accessToken;
result = true;
} else {
console.log("로그인되지 않았습니다.");
let err = new Error("Request failed with status code 401");
err.status = 401;
err.response = {data:{"success":false, "errormessage":"로그인되지 않았습니다."}};
resultErr = err;
}
} catch(err) {
if (!err.response) {
err.response = {data:{"success":false, "errormessage":err.message}};
}
resultErr = err;
}
return new Promise((resolve, reject) => {
if (result) {
resolve();
} else {
reject(resultErr);
}
});
},
// 로그아웃합니다.
doLogout({commit}) {
commit('reset');
delete axios.defaults.headers.common['Access-Token'];
},
// Access-Token를 갱신합니다.
async doRefreshToken({commit, state}) {
let result = false;
let resultErr = null;
if (state.accessToken != "") {
let token = { id: state.memberId, accessToken : state.accessToken, refreshToken : state.refreshToken };
try {
let res = await axiosRefresh.post("http://localhost:9000/members/refresh", token);
if (res.data.success == true) {
console.log("Access-Token이 갱신되었습니다.");
commit('setAccessToken', res.data.accessToken);
console.log(res.data.accessToken);
axios.defaults.headers.common['Access-Token'] = res.data.accessToken;
result = true;
} else {
console.log("Access-Token이 갱신되지 않았습니다.");
let err = new Error("Request failed with status code 401");
err.status = 401;
err.response = {data:{"success":false, "errormessage":"Access-Token이 갱신되었습니다."}};
resultErr = err;
}
} catch(err) {
//console.log(err);
if (!err.response) {
err.response = {data:{"success":false, "errormessage":err.message}};
}
resultErr = err;
}
} else {
let err = new Error("Access-Token does not exist");
err.status = 401;
err.response = {data:{"success":false, "errormessage":"Access-Token이 없습니다."}};
resultErr = err;
}
return new Promise((resolve, reject) => {
if (result) {
resolve();
} else {
reject(resultErr);
}
});
}
/*
doReadStateFromStorage({commit}) {
commit('readStateFromStorage');
}
*/
}
그리고 C:\workspaces\nodeserver\testvue\src\App.vue 파일을 오픈하여 <script>에서 mounted() 함수를 주석 처리하거나 삭제합니다.
<script>
export default {
name : 'App',
methods : {
Logout() {
this.$store.dispatch("loginStore/doLogout");
this.$router.push('/');
}
},
computed : {
isLogin() {
return this.$store.getters['loginStore/isLogin'];
}
}
/*
mounted() {
this.$store.dispatch("loginStore/doReadStateFromStorage");
}
*/
};
</script>
Vuex PersistedState로 state 저장하고 읽기
1. C:\workspaces\nodeserver\testvue\src\store\index.js 파일을 오픈합니다.
vuex-persistedstate를 import 합니다.
import createPersistedState from "vuex-persistedstate";
createPersistedState를 사용하여 로그인 모듈(loginStore)을 추가합니다.
const storageState = createPersistedState({
paths: ['loginStore']
});
여러 개의 모듈을 paths에 콤마(,) 추가할 수 있습니다.
createStore() 메서드에 plugin으로 추가합니다.
export default createStore({
modules: {
scoreStore: scoreStore,
loginStore: loginStore
},
plugins: [storageState]
});
만약, 개별 모듈이 아닌 전체 모듈을 저장하고 싶을 경우에는 모듈 추가 없이 바로 적용하면 됩니다.
export default createStore({
modules: {
scoreStore: scoreStore,
loginStore: loginStore
},
plugins: [createPersistedState()]
});
전체 소스입니다.
import { createStore } from 'vuex';
import createPersistedState from "vuex-persistedstate";
import scoreStore from '@/store/modules/score.js';
import loginStore from '@/store/modules/login.js';
const storageState = createPersistedState({
paths: ['loginStore']
});
export default createStore({
modules: {
scoreStore: scoreStore,
loginStore: loginStore
},
plugins: [storageState]
});
2. 콘솔을 실행하고 Node.js 레스트 API 서버 프로젝트가 있는 C:\workspaces\nodeserver\testrestapi 폴더로 이동합니다. 그리고 npm run 명령어를 실행합니다.
npm run start
3. 콘솔을 실행하고 Vue 프로젝트가 있는 C:\workspaces\nodeserver\testvue 폴더로 이동합니다. 그리고 콘솔에서 npm run 명령어를 실행합니다.
npm run serve
4. 로그인을 합니다. 그러면 Application의 Local Storage(로컬 스토리지)에 vuex로 로그인 모듈(loginStore)의 state가 JSON 객체로 저장되어 있는 것을 확인할 수 있습니다.
그리고 웹 브라우저에서 새로고침(F5)을 하여도 로그인이 유지되는 것을 확인할 수 있습니다.
5. 로그아웃을 클릭합니다. 그러면 Application의 Local Storage(로컬 스토리지)에 vuex가 내용이 없는 빈 JSON 객체로 저장되는 것을 확인할 수 있습니다.
Vuex PersistedState를 사용하면 쉽게 state를 저장하고 유지할 수 있습니다.
프로젝트에 따라 개발 환경에 따라 Local Storage(로컬 스토리지)나 cookie(쿠키)로 사용해야 부분이 있습니다.
그리고 Vuex PersistedState는 모듈 자체를 저장하기 때문에 저장이 필요하지 않은 state도 저장되어 모듈 설계에 따라 비효율적일 수 있습니다. 그래서 모듈을 설계할 때 고려하시거나 개별 저장이 효율적이라고 판단될 때는 Local Storage(로컬 스토리지)를 사용하시길 바랍니다.
cookie(쿠키)는 만료 기간이 정해져 있고 웹 브라우저마다 cookie(쿠키)가 차단될 수 있습니다. 그래서 cookie(쿠키)를 사용하실 때는 사용자에게 cookie(쿠키) 사용을 확인받아 설정해야 하는 부분을 고려해야 합니다.