diff --git a/frontend/src/composables/useAuth.ts b/frontend/src/composables/useAuth.ts index b4341a5..6128485 100644 --- a/frontend/src/composables/useAuth.ts +++ b/frontend/src/composables/useAuth.ts @@ -30,9 +30,12 @@ export function useAuth() { await api.post('/auth/reset-password', { token, password }) } - // 로그인 - async function login(payload: LoginPayload): Promise { - return (await api.post('/auth/login', payload)) as unknown as AuthUser + // 로그인 — silent 옵션 시 전역 에러 토스트를 띄우지 않고 호출측이 직접 처리 + async function login( + payload: LoginPayload, + opts?: { silent?: boolean }, + ): Promise { + return (await api.post('/auth/login', payload, opts)) as unknown as AuthUser } // 로그아웃 (쿠키 제거) diff --git a/frontend/src/pages/relay/LoginPage.vue b/frontend/src/pages/relay/LoginPage.vue index d3f5098..67885c6 100644 --- a/frontend/src/pages/relay/LoginPage.vue +++ b/frontend/src/pages/relay/LoginPage.vue @@ -4,10 +4,12 @@ import { computed, ref } from 'vue' import { useRoute, useRouter } from 'vue-router' import RelayMark from '@/components/RelayMark.vue' import { useAuthStore } from '@/stores/auth.store' +import { useDialog } from '@/composables/useDialog' const route = useRoute() const router = useRouter() const authStore = useAuthStore() +const dialog = useDialog() // 폼 상태 const email = ref('') @@ -35,25 +37,36 @@ async function onLogin() { if (submitting.value) return submitting.value = true try { - await authStore.login({ - email: email.value.trim(), - password: password.value, - keepLogin: keepLogin.value, - }) + // silent: 미인증(403)은 토스트 없이 화면 전환으로 처리하므로 전역 에러 알림을 끈다 + await authStore.login( + { + email: email.value.trim(), + password: password.value, + keepLogin: keepLogin.value, + }, + { silent: true }, + ) await router.push('/projects') } catch (err) { - // 미인증 계정(서버 403) → 인증 메일을 즉시 발송하고 '발송 완료' 화면으로 이동 - const status = (err as { response?: { status?: number } })?.response?.status - if (status === 403) { + const res = ( + err as { response?: { status?: number; data?: { error?: { message?: string } } } } + )?.response + // 미인증 계정(서버 403) → 인증 메일을 즉시 발송하고 '발송 완료' 화면으로 조용히 이동 + if (res?.status === 403) { const addr = email.value.trim() try { await authStore.resendVerification(addr) } catch { - // 재발송 실패는 API 인터셉터가 전역 처리 + // 재발송 실패는 무시(화면 전환 우선) } await router.push({ path: '/signup', query: { sent: addr } }) + return } - // 그 외(자격 불일치 등) 에러 메시지는 API 인터셉터에서 전역 처리됨 + // 그 외(자격 불일치 등) — 서버 메시지를 직접 안내 + void dialog.alert( + res?.data?.error?.message ?? '이메일 또는 비밀번호가 올바르지 않습니다.', + { variant: 'danger' }, + ) } finally { submitting.value = false } diff --git a/frontend/src/stores/auth.store.ts b/frontend/src/stores/auth.store.ts index 26f683e..67d52b1 100644 --- a/frontend/src/stores/auth.store.ts +++ b/frontend/src/stores/auth.store.ts @@ -24,9 +24,12 @@ export const useAuthStore = defineStore('auth', () => { fetchMe, } = useAuth() - // 로그인 - async function login(payload: LoginPayload): Promise { - user.value = await loginApi(payload) + // 로그인 — silent 옵션은 그대로 전달(에러 토스트 억제, 호출측 직접 처리) + async function login( + payload: LoginPayload, + opts?: { silent?: boolean }, + ): Promise { + user.value = await loginApi(payload, opts) } // 회원가입 — 인증 메일 발송 후 대기. 자동 로그인하지 않으므로 user 는 갱신하지 않는다.