728x90
반응형
모듈화
Axios Instance (엑시오스 인터셉터)
(https://axios-http.com/kr/docs/instance)

1. src\apis\axiosInterceptor.js 파일을 수정합니다.
import axios from 'axios';
import { useLoginStore } from '@/stores/useLoginStore';
import { useRouter } from 'vue-router';
export const createAxiosInstance = (baseURL, timeout) => {
const loginStore = useLoginStore();
const router = useRouter();
const axiosInstance = axios.create({
baseURL : baseURL,
timeout : timeout
});
:
baseURL은 기본 URL입니다. baseURL이 설정되어 있으면 메소드의 url과 합쳐집니다.
timeout은 요청에 대한 응답이 길어질 경우 중단을 하기 위한 시간입니다. (timeout 시간을 짧게 하면 서버 timeout(응답 코드 408, timeout)) 전에도 중단할 수 있습니다.)
2. src\apis에 lunchmenuApi.js 파일을 생성합니다.

import { createAxiosInstance } from './axiosInterceptor';
let axiosInstance = null;
// LunchmenuApi를 위한 Axios 인스턴스 생성
function createLunchmenuApi() {
if (axiosInstance == null) {
axiosInstance = createAxiosInstance("http://127.0.0.1:9000", 60);
}
return axiosInstance;
}
// 전체 점심 메뉴 리스트를 가져옵니다.
export function getLunchMenuList() {
return createLunchmenuApi().get("/api/v1/lunchmenus/");
}
// 점심 메뉴 번호로 검색하여 점심 메뉴를 가져옵니다.
export function getLunchMenuItem(seq) {
return createLunchmenuApi().get("/api/v1/lunchmenus/" + seq);
}
// 점심 메뉴를 추가합니다.
export function addLunchMenuItem(lunchMenuItem) {
return createLunchmenuApi().post("/api/v1/lunchmenus/", lunchMenuItem);
}
// 점심 메뉴를 수정합니다.
export function editLunchMenuItem(seq, lunchMenuItem) {
return createLunchmenuApi().put("/api/v1/lunchmenus/" + seq, lunchMenuItem);
}
// 점심 메뉴를 삭제합니다.
export async function removeLunchMenuItem(seq) {
return createLunchmenuApi().delete("/api/v1/lunchmenus/" + seq);
}
3. src\views\LunchMenu\LunchMenuListView.vue 파일을 수정합니다.
<script>
:
// import { createAxiosInstance } from '@/apis/axiosInterceptor';
import * as lunchmenuApi from '@/apis/lunchmenuApi';
:
export default {
name : 'LunchMenuListView',
setup() {
:
//const loginStore = useLoginStore();
:
async function getLunchMenuList() {
try {
//const res = await createAxiosInstance("http://127.0.0.1:9000").get("/api/v1/lunchmenus/");
const res = await lunchmenuApi.getLunchMenuList();
console.log(res);
lunchMenuList.value = res.data.data;
} catch(err) {
console.log(err);
}
}
:
4. src\views\LunchMenu\LunchMenuAddView.vue 파일을 수정합니다.
<script>
:
//import axios from 'axios';
import * as lunchmenuApi from '@/apis/lunchmenuApi';
:
export default {
name : 'LunchMenuAddView',
setup() {
:
function addClick() {
const lunchMenuItem = { menuName : state.lunchMenuName };
//axios.post("http://127.0.0.1:9000/api/v1/lunchmenus/", lunchMenuItem).then(() => {
lunchmenuApi.addLunchMenuItem(lunchMenuItem).then(() => {
router.replace({name : 'lunchmenulist'});
}).catch((err) => {
console.log(err);
});
}
function getLunchMenuItem() {
//axios.get("http://127.0.0.1:9000/api/v1/lunchmenus/" + route.query.seq).then((res) => {
lunchmenuApi.getLunchMenuItem(route.query.seq).then((res) => {
state.lunchMenuName = res.data.data.menu_name;
}).catch((err) => {
console.log(err);
});
}
function editClick() {
if (state.lunchMenuName == "") {
alert("점심 메뉴 명을 입력하세요.");
lunchMenuNameInput.value.focus();
return;
}
var result = confirm("수정하시겠습니까?");
if (result) {
const lunchMenuItem = { menuName : state.lunchMenuName };
//axios.put("http://127.0.0.1:9000/api/v1/lunchmenus/"+ route.query.seq, lunchMenuItem).then(() => {
lunchmenuApi.editLunchMenuItem(route.query.seq, lunchMenuItem).then(() => {
router.replace({name : 'lunchmenuread', query : {seq : route.query.seq}});
}).catch((err) => {
console.log(err);
});
}
}
:
5. src\views\LunchMenu\LunchMenuReadView.vue 파일을 수정합니다.
<script>
:
//import axios from 'axios';
import * as lunchmenuApi from '@/apis/lunchmenuApi';
:
export default {
name : 'LunchMenuReadView',
setup() {
:
function getLunchMenuItem() {
//axios.get("http://127.0.0.1:9000/api/v1/lunchmenus/" + route.query.seq).then((res) => {
lunchmenuApi.getLunchMenuItem(route.query.seq).then((res) => {
console.log(res);
lunchMenuItem.value = res.data.data;
}).catch((err) => {
console.log(err);
});
}
:
async function deleteClick() {
var result = confirm("삭제하시겠습니까?");
if (result) {
try {
//const res = await axios.delete("http://127.0.0.1:9000/api/v1/lunchmenus/" + route.query.seq);
const res = await lunchmenuApi.removeLunchMenuItem(route.query.seq);
console.log(res);
if (res.data.code == 200) {
alert("삭제되었습니다.");
router.push({name : 'lunchmenulist'});
} else {
alert("삭제되지 않았습니다.");
}
} catch(err) {
console.log(err);
}
}
}
:
로그인 /로그아웃, 로그인 여부 확인
1. src\stores\useLoginStore.js 파일을 수정합니다.
import { defineStore } from 'pinia';
import { ref } from 'vue';
import axios from 'axios';
export const useLoginStore = defineStore('login', () => {
const access_token = ref("");
const refresh_token = ref("");
const member_id = ref("");
const member_name = ref("");
// 로그인 여부를 가져옵니다.
/*
const isLogin = computed(() => {
return refresh_token.value == "" ? false : true;
});
*/
function isLogin() {
let login = false;
if (refresh_token.value != "") {
// refresh_token에서 .(도트)로 분리하여 payload를 가져옵니다.
let base64Payload = refresh_token.value.split('.')[1];
// URL과 호환되지 않는 문자를 base64 표준 문자로 교체합니다.
base64Payload = base64Payload.replace(/-/g, '+').replace(/_/g, '/');
// atob() 메소드로 복호화합니다.
base64Payload = atob(base64Payload);
// JSON 객체로 변환합니다.
const payloadObject = JSON.parse(base64Payload);
// refresh_token의 만료 시간을 확인합니다.
const currentDate = new Date().getTime() / 1000;
if (payloadObject.exp <= currentDate) {
console.log("token expired");
} else {
console.log("token valid");
login = true;
}
}
return login;
}
// 로그인합니다.
async function doLogin(memberInfo) {
let result = false;
let resultErr = null;
try {
const res = await axios.post("http://127.0.0.1:9000/api/v1/member/login", memberInfo);
if (res.data.code == 200) {
console.log("로그인되었습니다.");
access_token.value = res.data.access_token;
refresh_token.value = res.data.refresh_token;
// access_token에서 .(도트)로 분리하여 payload를 가져옵니다.
let base64Payload = access_token.value.split('.')[1];
// URL과 호환되지 않는 문자를 base64 표준 문자로 교체합니다.
base64Payload = base64Payload.replace(/-/g, '+').replace(/_/g, '/');
// atob() 메소드로 복호화합니다.
base64Payload = atob(base64Payload);
// JSON 객체로 변환합니다.
const payloadObject = JSON.parse(base64Payload);
member_id.value = payloadObject.sub;
member_name.value = decodeURIComponent(escape(payloadObject.name));
result = true;
}
} catch(err) {
:
// 로그아웃합니다.
async function doLogout() {
console.log("로그아웃되었습니다.");
access_token.value = "";
refresh_token.value = "";
member_id.value = "";
member_name.value = "";
}
return {
access_token,
refresh_token,
member_id,
member_name,
isLogin,
doLogin,
isAccessTokenExpire,
doRefreshToken,
doLogout
}
:
computed를 삭제합니다.
2. src\router\index.js 파일을 수정합니다.
:
const Authentication = () => (to, from, next) => {
const loginStore = useLoginStore();
if (to.name.startsWith("lunchmenu")) {
if (!loginStore.isLogin()) {
next({path: '/login', query: { returnUrl: to.fullPath }});
} else {
next();
}
} else {
next();
}
};
:
3. src\App.vue 파일을 수정합니다.
<template>
<div class="header">
<div class="topline">
<div class="headmenu">
<div v-if="isLogin()">
{{greetingMessage}}
<span @click="Logout()">로그아웃</span>
</div>
<div v-else>
<router-link :to="{ name: 'Login' }">로그인</router-link>
</div>
</div>
</div>
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<router-link to="/lunchmenu/list">LunchMenu</router-link>
</nav>
</div>
<router-view/>
</template>
<script>
import { computed } from 'vue';
import { useLoginStore } from '@/stores/useLoginStore';
import { useRouter } from 'vue-router';
export default {
name: 'AppView',
setup() {
const loginStore = useLoginStore();
const router = useRouter();
// 로그인 여부를 가져옵니다.
function isLogin() {
return loginStore.isLogin();
}
// 로그아웃합니다
function Logout() {
loginStore.doLogout();
router.replace({name : 'home'});
}
// 환영 메시지를 가져옵니다.
const greetingMessage = computed(() => {
return `${loginStore.member_name}님, 안녕하세요.`;
});
return {
isLogin,
Logout,
greetingMessage
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
nav {
padding: 30px;
}
nav a {
font-weight: bold;
color: #2c3e50;
}
nav a.router-link-exact-active {
color: #42b983;
}
.header {
position:relative;
}
.header .topline {
position:relative;
height:30px;
margin:0 10px;
}
.header .topline .headmenu {
position:absolute;
right:0;
top:4px;
}
.header .topline .headmenu a {
font-weight:bold;
color:#2c3e50;
}
.header .topline .headmenu span {
font-weight:bold;
color:#2c3e50;
cursor:pointer;
text-decoration:underline;
}
</style>
4. 웹 브라우저에서 확인하기 (http://localhost:8080) ← 로그인 전

5. 웹 브라우저에서 확인하기 (http://localhost:8080/lunchmenu/list) ← 로그인 후

728x90
반응형
'Vue.js 3 & NodeJS > 사내교육 - 점심 메뉴' 카테고리의 다른 글
| Android 설치 - Hello Android 실행 (0) | 2026.08.07 |
|---|---|
| [17] Frontend 개발 - Store State 유지, Cookies, localStorage, Pinia Plugin Persistedstate (0) | 2026.08.05 |
| [16] Frontend 개발 - Login 관리, Axios Interceptor (0) | 2026.08.05 |
| [15] Frontend 개발 - Login 관리, Access Token, Refresh Token (0) | 2026.08.05 |
| [14] Backend 개발 - 사용자 API 개발, 라우트 별 인증 처리, 인가 처리 (0) | 2026.08.05 |