Skip to content

Commit b30a7c8

Browse files
committed
Fix: api, auth store, coverletterview 수정
api에서 백엔드를 호출하도록 명확히 지정 지정하지 않아 일부 페이지(자소서)에서 프론트 주소를 호출 auth store에서 주기적으로 토큰 만료여부 확인 토큰 만료 시 로그아웃 처리 coverletterview에서 api 호출부 오류 일부 수정
1 parent 85c88cc commit b30a7c8

File tree

4 files changed

+97
-116
lines changed

4 files changed

+97
-116
lines changed

front/src/api/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import axios from "axios";
44
// axios 인스턴스 생성
55
// 1. Auth 인증/인가
66
const apiAuth = axios.create({
7-
baseURL: "/api/v1",
7+
baseURL: "http://localhost:8080/api/v1",
88
timeout: 5000, // 제한시간
99
})
1010

front/src/stores/auth.js

+72-44
Original file line numberDiff line numberDiff line change
@@ -3,73 +3,101 @@ import { apiAuth, api } from '@/api'
33
import { ref } from 'vue'
44

55
export const useAuthStore = defineStore('auth', () => {
6-
const user = ref(null)
6+
const loginUser = ref(null)
77
const isLoggedIn = ref(false)
8+
let tokenCheckInterval = null
89

9-
if(localStorage.getItem('token')) {
10+
if (localStorage.getItem('token')) {
1011
isLoggedIn.value = true
1112
}
12-
1313

1414
async function login(email, password) {
15-
try {
16-
console.log(email, password)
17-
const response = await api.post("/user/login", {
18-
email, password,
19-
})
20-
21-
const token = response.data["refresh-token"]
22-
user.value = { email }; // 사용자 정보 저장
23-
localStorage.setItem("users", JSON.stringify(user.value)); // 사용자 정보 저장
24-
localStorage.setItem("token", token) // 토큰저장
25-
isLoggedIn.value = true;
26-
return { success: true, message: "로그인이 완료되었습니다." };
27-
} catch (err) {
28-
if (err.response && err.response.status === 401) {
29-
return { success: false, message: "이메일 또는 비밀번호가 잘못되었습니다." };
30-
} else {
31-
console.error("로그인 오류", err);
32-
return { success: false, message: "로그인 중 문제가 발생했습니다. 다시 시도해주세요." };
33-
}
15+
try {
16+
const response = await api.post("/user/login", { email, password })
17+
const token = response.data["refresh-token"]
18+
loginUser.value = { email }
19+
localStorage.setItem("loginUser", JSON.stringify(loginUser.value))
20+
localStorage.setItem("token", token)
21+
isLoggedIn.value = true
22+
return { success: true, message: "로그인이 완료되었습니다." }
23+
} catch (err) {
24+
if (err.response && err.response.status === 401) {
25+
return { success: false, message: "이메일 또는 비밀번호가 잘못되었습니다." }
26+
} else {
27+
console.error("로그인 오류", err)
28+
return { success: false, message: "로그인 중 문제가 발생했습니다. 다시 시도해주세요." }
3429
}
30+
}
3531
}
3632

3733
async function logout() {
3834
try {
35+
const token = localStorage.getItem("token")
36+
if (!token || isTokenExpired(token)) {
37+
handleClientLogout()
38+
return { success: true, message: "로그아웃이 완료되었습니다." }
39+
}
3940
await apiAuth.post("/user/logout")
40-
localStorage.removeItem("token")
41-
localStorage.removeItem("email")
42-
user.value = null;
43-
isLoggedIn.value = false;
44-
return { success: true, message: "로그아웃이 완료되었습니다." };
41+
handleClientLogout()
42+
return { success: true, message: "로그아웃이 완료되었습니다." }
4543
} catch (err) {
46-
console.error("로그아웃 오류", err);
47-
return { success: false, message: "로그아웃 중 문제가 발생했습니다. 다시 시도해주세요." };
44+
console.error("로그아웃 오류", err)
45+
handleClientLogout()
46+
return { success: false, message: "로그아웃 중 문제가 발생했습니다. 다시 시도해주세요." }
4847
}
4948
}
5049

51-
// function checkAuth() {
52-
// // const storedUser = localStorage.getItem('user')
53-
// // if (isLoggedIn) {
54-
// // user.value = JSON.parse(storedUser)
55-
// // isLoggedIn = true
56-
// // }
57-
// }
50+
function handleClientLogout() {
51+
clearInterval(tokenCheckInterval) // 토큰 확인 중지
52+
localStorage.removeItem("token")
53+
localStorage.removeItem("loginUser")
54+
loginUser.value = null
55+
isLoggedIn.value = false
56+
}
57+
5858
function checkAuth() {
59-
const storedUser = localStorage.getItem('user');
60-
if (storedUser) {
61-
user.value = JSON.parse(storedUser);
62-
isLoggedIn.value = true;
59+
const token = localStorage.getItem('token')
60+
const storedUser = localStorage.getItem('loginUser')
61+
62+
if (token && storedUser && !isTokenExpired(token)) {
63+
loginUser.value = JSON.parse(storedUser)
64+
isLoggedIn.value = true
65+
} else {
66+
logout()
6367
}
6468
}
65-
69+
70+
function isTokenExpired(token) {
71+
try {
72+
const payload = JSON.parse(atob(token.split('.')[1])) // JWT 디코딩
73+
const currentTime = Math.floor(Date.now() / 1000) // 현재 시간 (초 단위)
74+
return payload.exp < currentTime // 만료 여부 확인
75+
} catch (err) {
76+
console.error("토큰 디코딩 오류", err)
77+
return true // 디코딩 실패 시 만료된 것으로 처리
78+
}
79+
}
80+
81+
function startTokenCheck() {
82+
if (tokenCheckInterval) return // 중복 실행 방지
83+
tokenCheckInterval = setInterval(() => {
84+
const token = localStorage.getItem("token")
85+
if (token && isTokenExpired(token)) {
86+
logout()
87+
alert("세션이 만료되었습니다. 다시 로그인해주세요.")
88+
}
89+
}, 300000) // 5분마다 확인
90+
}
91+
92+
checkAuth()
93+
startTokenCheck()
6694

6795
return {
68-
user,
96+
loginUser,
6997
isLoggedIn,
7098
login,
7199
logout,
72-
checkAuth
100+
checkAuth,
101+
startTokenCheck,
73102
}
74103
})
75-

front/src/utils/axios.js

-30
This file was deleted.

front/src/views/CoverLetterView.vue

+24-41
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
</h1>
99
<div class="flex space-x-8">
1010
<!-- Resume Tab View -->
11-
<div class="w-1/3">
12-
<div v-if="resumeData.length > 0">
11+
<div class="w-1/2">
12+
<div v-if="isResumeExist">
1313
<ResumeTabView />
1414
</div>
1515
<div v-else class="bg-white p-6 rounded-lg shadow-md text-center">
@@ -26,7 +26,7 @@
2626
</div>
2727

2828
<!-- Cover Letter Tabs -->
29-
<div class="w-2/3">
29+
<div class="w-1/2">
3030
<div class="bg-white p-6 rounded-lg shadow-md">
3131
<input
3232
v-model="coverLetterTitle"
@@ -81,11 +81,14 @@
8181
<script setup>
8282
import { ref, onMounted } from "vue";
8383
import { useRouter } from "vue-router";
84+
import { apiAuth } from "@/api/index.js";
85+
import { useAuthStore } from "@/stores/auth";
8486
import ResumeTabView from "@/components/CoverLetter/ResumeTabView.vue";
8587
import CoverLetterSection from "@/components/CoverLetter/CoverLetterSection.vue";
8688
8789
const router = useRouter();
88-
const resumeData = ref([]);
90+
const resumeData = ref({});
91+
const isResumeExist = ref(false);
8992
const coverLetterData = ref({});
9093
const currentSection = ref("motivation");
9194
const coverLetterTitle = ref("");
@@ -101,49 +104,35 @@ const coverLetterSections = [
101104
];
102105
103106
onMounted(async () => {
104-
const isLoggedIn = checkUserAuthentication();
105-
if (!isLoggedIn) {
106-
router.push("/auth");
107-
return;
108-
}
109-
110-
await fetchResumeData();
111-
await fetchCoverLetterData();
107+
fetchResumeData();
108+
fetchCoverLetterData();
112109
});
113110
114-
const checkUserAuthentication = () => {
115-
const token = localStorage.getItem("authToken");
116-
return !!token;
117-
};
118-
119111
const fetchResumeData = async () => {
120112
try {
121-
const response = await fetch("/api/resume");
122-
if (!response.ok) throw new Error("Failed to fetch resume data");
123-
const data = await response.json();
124-
resumeData.value = [
125-
{
126-
id: 1,
127-
title: data.personalInfo.name + "의 이력서",
128-
data: data,
129-
},
130-
];
113+
const response = await apiAuth.get('/resume');
114+
console.log("Resume API 응답:", response.data);
115+
const data = response.data.resume;
116+
if (data) {
117+
resumeData.value = data;
118+
isResumeExist.value = true;
119+
}
131120
} catch (error) {
132-
console.error("Failed to fetch resume data:", error);
133-
resumeData.value = [];
121+
console.error('이력서 생성 중 에러:', error);
134122
}
135123
};
136124
137125
const fetchCoverLetterData = async () => {
138126
try {
139-
const response = await fetch("/api/coverletter");
140-
const data = await response.json();
127+
const response = await apiAuth.get("/coverletters");
128+
const data = response.data.coverletter;
141129
coverLetterData.value = data.sections;
142130
coverLetterTitle.value = data.title || "";
131+
143132
} catch (error) {
144-
console.error("Failed to fetch cover letter data:", error);
133+
console.error('기존 자소서 생성 중 에러:', error);
145134
coverLetterData.value = {};
146-
coverLetterTitle.value = "";
135+
coverLetterTitle.value = ""
147136
}
148137
};
149138
@@ -153,11 +142,7 @@ const updateSection = (sectionId, content) => {
153142
154143
const saveSection = async (sectionId) => {
155144
try {
156-
const response = await fetch(`/api/v1/coverletter/${sectionId}`, {
157-
method: "POST",
158-
headers: {
159-
"Content-Type": "application/json",
160-
},
145+
const response = await apiAuth.post(`coverletters/${sectionId}`, {
161146
body: JSON.stringify({ content: coverLetterData.value[sectionId] }),
162147
});
163148
if (!response.ok) throw new Error("Failed to save section");
@@ -170,8 +155,7 @@ const saveSection = async (sectionId) => {
170155
171156
const saveCoverLetter = async () => {
172157
try {
173-
const response = await fetch("/api/coverletter", {
174-
method: "POST",
158+
const response = await apiAuth.post("/coverletters", {
175159
headers: {
176160
"Content-Type": "application/json",
177161
},
@@ -190,7 +174,6 @@ const saveCoverLetter = async () => {
190174
</script>
191175

192176
<style scoped>
193-
/* 필요한 경우 추가 스타일 */
194177
.overflow-x-auto {
195178
scrollbar-width: thin;
196179
scrollbar-color: rgba(156, 163, 175, 0.5) transparent;

0 commit comments

Comments
 (0)