SPA(Single Page Application) 구조의 Vue 프로젝트에서는 백엔드(Spring Boot 등)로부터 발급받은 AccessToken과 RefreshToken을 이용해 인증을 처리할 수 있습니다. 이 포스팅에서는 AccessToken 자동 재발급 흐름을 구현하는 방법을 중심으로 다룹니다.
📦 전체 인증 구조 요약
[Login]
⬇
AccessToken + RefreshToken 발급
⬇
API 요청 시 → AccessToken 사용
⬇
AccessToken 만료 → RefreshToken 전송
⬇
서버에서 AccessToken 재발급 → 재요청
📁 프로젝트 구조 예시
src/
├── api/
│ └── axios.js # Axios 설정 (인터셉터 포함)
├── auth/
│ └── tokenService.js # 토큰 저장/관리 유틸
└── views/
└── LoginView.vue
1️⃣ 토큰 관리 유틸 (tokenService.js)
js
// src/auth/tokenService.js
export default {
getAccessToken() {
return localStorage.getItem("accessToken");
},
getRefreshToken() {
return localStorage.getItem("refreshToken");
},
saveTokens({ accessToken, refreshToken }) {
localStorage.setItem("accessToken", accessToken);
localStorage.setItem("refreshToken", refreshToken);
},
clear() {
localStorage.removeItem("accessToken");
localStorage.removeItem("refreshToken");
},
};
2️⃣ Axios 인터셉터 설정 (axios.js)
js
// src/api/axios.js
import axios from "axios";
import tokenService from "@/auth/tokenService";
const api = axios.create({
baseURL: "http://localhost:8080", // 백엔드 주소
});
// 요청에 AccessToken 자동 삽입
api.interceptors.request.use((config) => {
const token = tokenService.getAccessToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// 응답 에러 처리 (AccessToken 만료 시)
api.interceptors.response.use(
(res) => res,
async (error) => {
const originalRequest = error.config;
// AccessToken이 만료된 경우
if (error.response && error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
const refreshRes = await axios.post("http://localhost:8080/auth/refresh", null, {
headers: {
Authorization: `Bearer ${tokenService.getRefreshToken()}`,
},
});
const newAccessToken = refreshRes.data.accessToken;
tokenService.saveTokens({
accessToken: newAccessToken,
refreshToken: tokenService.getRefreshToken(), // 그대로 유지 or 서버에서 갱신
});
// 원래 요청 재시도
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
return api(originalRequest);
} catch (e) {
tokenService.clear();
window.location.href = "/login";
return Promise.reject(e);
}
}
return Promise.reject(error);
}
);
export default api;
3️⃣ 로그인 처리 예시 (LoginView.vue)
vue
<template>
<div>
<input v-model="username" />
<input v-model="password" type="password" />
<button @click="login">로그인</button>
</div>
</template>
<script>
import axios from "@/api/axios";
import tokenService from "@/auth/tokenService";
export default {
data() {
return {
username: "",
password: "",
};
},
methods: {
async login() {
const res = await axios.post("/auth/login", {
username: this.username,
password: this.password,
});
tokenService.saveTokens(res.data);
this.$router.push("/home");
},
},
};
</script>
🧠 다이어그램: 자동 토큰 재발급 흐름
🛡️ 보안 팁
항목 |
권장 방식 |
RefreshToken 저장 위치 |
httpOnly 쿠키 (XSS 방지) or localStorage |
토큰 만료 시간 |
Access: 15분, Refresh: 7일 이상 |
인터셉터 처리 |
401 시 자동 Refresh → 재시도 |
로그아웃 처리 |
localStorage 초기화 & 서버 저장소 RefreshToken 삭제 |
✨ 마무리
- Axios 인터셉터를 사용하면 사용자 경험을 해치지 않고 자동으로 AccessToken을 재발급할 수 있습니다.
- RefreshToken은 민감한 정보이므로 httpOnly 쿠키 저장 방식을 고려하면 더 안전합니다.
- 서버와 클라이언트가 함께 설계된 토큰 구조를 갖추면, 안정적인 인증 시스템을 만들 수 있습니다.