From a4cb9936ab8fbc636b178b84104afe59b5b657b1 Mon Sep 17 00:00:00 2001 From: yoon Date: Mon, 3 Feb 2025 15:07:29 +0900 Subject: [PATCH 01/15] watchEffect -> watch --- src/components/input/FormSelect.vue | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/components/input/FormSelect.vue b/src/components/input/FormSelect.vue index 43c41b5..2ff0c3a 100644 --- a/src/components/input/FormSelect.vue +++ b/src/components/input/FormSelect.vue @@ -16,7 +16,7 @@ From 4258c5751201aa113a4d4d9be3533689576df168 Mon Sep 17 00:00:00 2001 From: yoon Date: Mon, 3 Feb 2025 15:07:50 +0900 Subject: [PATCH 02/15] =?UTF-8?q?remember=20=EC=B2=B4=ED=81=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/user/LoginForm.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/user/LoginForm.vue b/src/components/user/LoginForm.vue index 71a9b1d..36a7e3b 100644 --- a/src/components/user/LoginForm.vue +++ b/src/components/user/LoginForm.vue @@ -25,7 +25,7 @@
- +
등록신청 @@ -45,6 +45,7 @@ const password = ref(''); const idAlert = ref(false); const passwordAlert = ref(false); + const remember = ref(false); const handleIdChange = value => { id.value = value; @@ -62,7 +63,7 @@ const response = await $api.post('user/login', { loginId: id.value, password: password.value, - remember: false, + remember: remember.value, }); if (response.status === 200) { From 4ffb98b7cf1000454153799f38ef046122c1f7ff Mon Sep 17 00:00:00 2001 From: yoon Date: Mon, 3 Feb 2025 15:11:25 +0900 Subject: [PATCH 03/15] =?UTF-8?q?=EC=A4=84=20=EB=A7=9E=EC=B6=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/user/RegisterForm.vue | 78 ++++++++++++++-------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/components/user/RegisterForm.vue b/src/components/user/RegisterForm.vue index 7aa5e86..f6d44c8 100644 --- a/src/components/user/RegisterForm.vue +++ b/src/components/user/RegisterForm.vue @@ -295,50 +295,50 @@ // 회원가입 const handleSubmit = async () => { - idAlert.value = id.value.trim() === ''; - passwordAlert.value = password.value.trim() === ''; - passwordcheckAlert.value = passwordcheck.value.trim() === ''; - pwhintResAlert.value = pwhintRes.value.trim() === ''; - nameAlert.value = name.value.trim() === ''; - birthAlert.value = birth.value.trim() === ''; - addressAlert.value = address.value.trim() === ''; - phoneAlert.value = phone.value.trim() === ''; + idAlert.value = id.value.trim() === ''; + passwordAlert.value = password.value.trim() === ''; + passwordcheckAlert.value = passwordcheck.value.trim() === ''; + pwhintResAlert.value = pwhintRes.value.trim() === ''; + nameAlert.value = name.value.trim() === ''; + birthAlert.value = birth.value.trim() === ''; + addressAlert.value = address.value.trim() === ''; + phoneAlert.value = phone.value.trim() === ''; - // 프로필 이미지 체크 - if (!profile.value) { - profilerr.value = '프로필 이미지를 선택해주세요.'; - profilAlert.value = true; - } else { - profilerr.value = ''; - profilAlert.value = false; - } + // 프로필 이미지 체크 + if (!profile.value) { + profilerr.value = '프로필 이미지를 선택해주세요.'; + profilAlert.value = true; + } else { + profilerr.value = ''; + profilAlert.value = false; + } - if (profilAlert.value || idAlert.value || idErrorAlert.value || passwordAlert.value || passwordcheckAlert.value || - passwordcheckErrorAlert.value || pwhintResAlert.value || nameAlert.value || birthAlert.value || addressAlert.value || phoneAlert.value) { - return; - } + if (profilAlert.value || idAlert.value || idErrorAlert.value || passwordAlert.value || passwordcheckAlert.value || + passwordcheckErrorAlert.value || pwhintResAlert.value || nameAlert.value || birthAlert.value || addressAlert.value || phoneAlert.value) { + return; + } - const formData = new FormData(); - formData.append('memberIds', id.value); - formData.append('memberPwd', password.value); - formData.append('memberPwh', pwhintList[pwhint.value]); - formData.append('memberPwr', pwhintRes.value); - formData.append('memberNam', name.value); - formData.append('memberArr', address.value); - formData.append('memberDtl', detailAddress.value); - formData.append('memberZip', postcode.value); - formData.append('memberBth', birth.value); - formData.append('memberTel', phone.value); - formData.append('memberCol', color.value); - formData.append('memberMbt', mbti.value); - formData.append('memberPrf', profile.value); + const formData = new FormData(); + formData.append('memberIds', id.value); + formData.append('memberPwd', password.value); + formData.append('memberPwh', pwhintList[pwhint.value]); + formData.append('memberPwr', pwhintRes.value); + formData.append('memberNam', name.value); + formData.append('memberArr', address.value); + formData.append('memberDtl', detailAddress.value); + formData.append('memberZip', postcode.value); + formData.append('memberBth', birth.value); + formData.append('memberTel', phone.value); + formData.append('memberCol', color.value); + formData.append('memberMbt', mbti.value); + formData.append('memberPrf', profile.value); - const response = await $api.post('/user/join', formData, { isFormData : true }); + const response = await $api.post('/user/join', formData, { isFormData : true }); - if (response.status === 200) { - toastStore.onToast('등록신청이 완료되었습니다. 관리자 승인 후 이용가능합니다.', 's'); - router.push('/login'); - } + if (response.status === 200) { + toastStore.onToast('등록신청이 완료되었습니다. 관리자 승인 후 이용가능합니다.', 's'); + router.push('/login'); + } }; From 21f48d0e2723e7decfd66b4da74b38776d5db0bb Mon Sep 17 00:00:00 2001 From: yoon Date: Tue, 4 Feb 2025 11:10:31 +0900 Subject: [PATCH 04/15] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=96=88?= =?UTF-8?q?=EC=9D=84=20=EC=8B=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EA=B0=80=EC=9E=85=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99X?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/index.js | 54 +++++++++++++++++++++++++++++++++----- src/stores/useAuthStore.js | 43 ++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 src/stores/useAuthStore.js diff --git a/src/router/index.js b/src/router/index.js index c72c9ad..3d06bf2 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,9 +1,12 @@ import { createRouter, createWebHistory } from 'vue-router' +import { useAuthStore } from '@s/useAuthStore'; + // 초기 렌더링 속도를 위해 지연 로딩 사용 const routes = [ { path: '/', + name: "Home", component: () => import('@v/MainView.vue'), // meta: { requiresAuth: true } }, @@ -38,18 +41,20 @@ const routes = [ }, { path: '/login', + name: 'Login', component: () => import('@v/user/TheLogin.vue'), - meta: { layout: 'NoLayout' }, + meta: { layout: 'NoLayout', requiresGuest: true }, + }, + { + path: '/register', + name: 'Register', + component: () => import('@v/user/TheRegister.vue'), + meta: { layout: 'NoLayout', requiresGuest: true }, }, { path: '/vacation', component: () => import('@v/vacation/VacationManagement.vue'), }, - { - path: '/register', - component: () => import('@v/user/TheRegister.vue'), - meta: { layout: 'NoLayout' }, - }, { path: '/voteboard', component: () => import('@v/voteboard/TheVoteBoard.vue'), @@ -80,4 +85,41 @@ const router = createRouter({ routes: routes, }) +router.beforeEach(async (to, from, next) => { + const authStore = useAuthStore(); + await authStore.checkAuthStatus(); // 로그인 상태 확인 + + if (to.meta.requiresAuth && !authStore.isAuthenticated) { + // 로그인이 필요한 페이지인데 로그인되지 않은 경우 → 로그인 페이지로 이동 + next({ name: 'Login' }); + } else if (to.meta.requiresGuest && authStore.isAuthenticated) { + // 비로그인 사용자만 접근 가능한 페이지인데 로그인된 경우 → 홈으로 이동 + next({ name: 'Home' }); + } else { + next(); + } +}); + + +router.beforeEach(async (to, from, next) => { + const authStore = useAuthStore() + + // 최초 앱 로드 시 인증 상태 체크 + await authStore.checkAuthStatus() + + // 현재 라우트에 인증이 필요한지 확인 + const requiresAuth = to.meta.requiresAuth === true + + if (requiresAuth && !authStore.isAuthenticated) { + // 인증되지 않은 사용자를 로그인 페이지로 리다이렉트 + // 원래 가려던 페이지를 쿼리 파라미터로 전달 + next({ + name: 'Login', + query: { redirect: to.fullPath } + }) + } else { + next() + } +}) + export default router diff --git a/src/stores/useAuthStore.js b/src/stores/useAuthStore.js new file mode 100644 index 0000000..b944993 --- /dev/null +++ b/src/stores/useAuthStore.js @@ -0,0 +1,43 @@ +import { ref, computed } from 'vue'; +import { defineStore } from 'pinia'; +import axios from "@api"; + +export const useAuthStore = defineStore('auth', () => { + const user = ref(null); + + // 로그인 여부 확인 + const isAuthenticated = computed(() => user.value !== null); + + // 로그인 상태 확인 + const checkAuthStatus = async () => { + const response = await axios.get('user/isLogin'); + if (response.data.status === "OK" && response.data.data) { + user.value = response.data.data; + } else { + user.value = null; + } + }; + + // 로그아웃 + const logout = async () => { + + const response = await axios.get('user/logout'); + + // 로그아웃 성공 시 사용자 상태 초기화 + if (response.data.status === "OK") { + user.value = null; + return true; + } else { + console.error("로그아웃 실패:", response.data.data); + return false; + } + + }; + + return { + user, + isAuthenticated, + checkAuthStatus, + logout + }; +}); From 97cebc26d8b3ac2ead8883e6be03db990b1fd0d6 Mon Sep 17 00:00:00 2001 From: yoon Date: Tue, 4 Feb 2025 11:11:24 +0900 Subject: [PATCH 05/15] =?UTF-8?q?logout,=20=EC=95=84=EC=9D=B4=EC=BD=98=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/layouts/TheTop.vue | 163 ++++------------------------------------- 1 file changed, 13 insertions(+), 150 deletions(-) diff --git a/src/layouts/TheTop.vue b/src/layouts/TheTop.vue index c5cb75e..41fb494 100644 --- a/src/layouts/TheTop.vue +++ b/src/layouts/TheTop.vue @@ -8,158 +8,11 @@ From 0a91dbef093288a9848365590262a048547ce16a Mon Sep 17 00:00:00 2001 From: yoon Date: Thu, 6 Feb 2025 09:24:01 +0900 Subject: [PATCH 10/15] =?UTF-8?q?=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=ED=9E=8C=ED=8A=B8=20=EA=B3=B5=ED=86=B5=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=B0=9B=EC=95=84=EC=98=A4=EB=8A=94=20=EA=B1=B8=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/user/RegisterForm.vue | 36 +++++++--------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/src/components/user/RegisterForm.vue b/src/components/user/RegisterForm.vue index f6d44c8..64a0395 100644 --- a/src/components/user/RegisterForm.vue +++ b/src/components/user/RegisterForm.vue @@ -58,9 +58,11 @@ :is-essential="true" :is-row="false" :is-label="true" + :is-common="true" :data="pwhintList" @update:data="pwhint = $event" /> + diff --git a/src/router/index.js b/src/router/index.js index 3d06bf2..0c6965f 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -51,6 +51,12 @@ const routes = [ component: () => import('@v/user/TheRegister.vue'), meta: { layout: 'NoLayout', requiresGuest: true }, }, + { + path: '/pw', + name: 'Password', + component: () => import('@v/user/ThePassword.vue'), + meta: { layout: 'NoLayout', requiresGuest: true }, + }, { path: '/vacation', component: () => import('@v/vacation/VacationManagement.vue'), diff --git a/src/views/user/ThePassword.vue b/src/views/user/ThePassword.vue new file mode 100644 index 0000000..854fbb0 --- /dev/null +++ b/src/views/user/ThePassword.vue @@ -0,0 +1,16 @@ + + + + + From 5d1f220f14bcba0d45fa079003b17e634fbeef5e Mon Sep 17 00:00:00 2001 From: yoon Date: Thu, 6 Feb 2025 10:49:08 +0900 Subject: [PATCH 12/15] =?UTF-8?q?=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EC=9E=AC=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/user/FindPassword.vue | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/components/user/FindPassword.vue b/src/components/user/FindPassword.vue index d4e7bca..77a8666 100644 --- a/src/components/user/FindPassword.vue +++ b/src/components/user/FindPassword.vue @@ -44,13 +44,13 @@
취소 - +
{{ passwordcheckError }}
- +
From 30a111c8e07eeecf40e75c9e55a4708a742c66bb Mon Sep 17 00:00:00 2001 From: yoon Date: Thu, 6 Feb 2025 14:20:13 +0900 Subject: [PATCH 13/15] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=8B=9C?= =?UTF-8?q?=20user=EC=A0=95=EB=B3=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/user/LoginForm.vue | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/components/user/LoginForm.vue b/src/components/user/LoginForm.vue index 36a7e3b..3727703 100644 --- a/src/components/user/LoginForm.vue +++ b/src/components/user/LoginForm.vue @@ -40,6 +40,8 @@ import router from '@/router'; import { ref } from 'vue'; import UserFormInput from '@c/input/UserFormInput.vue'; + import { useUserStore } from '@s/useUserStore'; + import { useToastStore } from '@s/toastStore'; const id = ref(''); const password = ref(''); @@ -47,6 +49,9 @@ const passwordAlert = ref(false); const remember = ref(false); + const userStore = useUserStore(); + const toastStore = useToastStore(); + const handleIdChange = value => { id.value = value; idAlert.value = false; @@ -58,21 +63,19 @@ }; const handleSubmit = async () => { + const response = await $api.post('user/login', { + loginId: id.value, + password: password.value, + remember: remember.value, + }); - try { - const response = await $api.post('user/login', { - loginId: id.value, - password: password.value, - remember: remember.value, - }); - - if (response.status === 200) { - console.log('로그인 성공', response.data); - router.push('/'); - } - } catch (error) { - console.error('로그인 실패', error); + if (response.status === 200) { + await userStore.userInfo(); + router.push('/'); + } else { + toastStore.onToast('아이디 혹은 비밀번호가 틀렸습니다.', 'e'); } + }; From 81d68ea2f183d120a26b5230b4a45670cbc7dd9f Mon Sep 17 00:00:00 2001 From: yoon Date: Thu, 6 Feb 2025 14:20:30 +0900 Subject: [PATCH 14/15] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=95=9C?= =?UTF-8?q?=20user=20=ED=94=84=EB=A1=9C=ED=95=84=20=EA=B0=80=EC=A0=B8?= =?UTF-8?q?=EC=98=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/layouts/TheTop.vue | 137 +++++------------------------------------ 1 file changed, 16 insertions(+), 121 deletions(-) diff --git a/src/layouts/TheTop.vue b/src/layouts/TheTop.vue index 41fb494..1a664a7 100644 --- a/src/layouts/TheTop.vue +++ b/src/layouts/TheTop.vue @@ -12,10 +12,10 @@ - + - - - - - -
  • @@ -266,9 +152,7 @@