Clarify auth states
This commit is contained in:
@@ -86,3 +86,5 @@
|
||||
- 내역 헤더의 검색과 필터 버튼을 `assets/icons/search.svg`, `assets/icons/tune.svg` 인라인 SVG로 교체했다.
|
||||
- CSV 내보내기 컬럼을 한글 컬럼명과 앱의 거래 흐름에 맞는 순서로 정리하고 Excel 호환 BOM을 추가했다.
|
||||
- 빈 데이터 상태 화면을 공통 컴포넌트로 분리하고 내역, 통계, 자산, 설정, 공유 화면에 적용했다.
|
||||
- 로그인 화면에 세션 만료/로그인 필요 안내를 추가하고, 로그인 후 원래 접근하려던 화면으로 돌아가도록 했다.
|
||||
- 로그인/회원가입 입력값을 요청 전에 먼저 검증하고 네트워크 오류 메시지를 더 명확하게 표시하도록 했다.
|
||||
|
||||
@@ -56,3 +56,4 @@ user.email: zenn.message@gmail.com
|
||||
|
||||
- 의미 있는 수정 단위마다 변경 내역을 문서에 남기고 커밋/푸시한다.
|
||||
- 원격 저장소는 `https://git.sori.studio/zenn/money.sori.studio.git`를 사용한다.
|
||||
- 인증이 필요한 화면에서 로그인 화면으로 이동할 때는 로그인 필요/세션 만료처럼 사용자가 이유를 이해할 수 있는 안내를 함께 제공한다.
|
||||
|
||||
@@ -2,6 +2,8 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
||||
if (to.path === '/login' || to.path.startsWith('/share/')) return
|
||||
|
||||
const session = useState<{ id: string; email: string; name: string } | null>('session')
|
||||
const sessionCookie = useCookie<string | null>('money_session')
|
||||
const authReason = sessionCookie.value ? 'expired' : 'required'
|
||||
|
||||
if (session.value === undefined) {
|
||||
try {
|
||||
@@ -12,6 +14,12 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
||||
}
|
||||
|
||||
if (!session.value) {
|
||||
return navigateTo('/login')
|
||||
return navigateTo({
|
||||
path: '/login',
|
||||
query: {
|
||||
reason: authReason,
|
||||
redirect: to.fullPath === '/' ? undefined : to.fullPath
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -46,7 +46,12 @@
|
||||
required
|
||||
>
|
||||
</div>
|
||||
<p v-if="error" class="text-sm font-medium text-red-600">{{ error }}</p>
|
||||
<p v-if="authNotice && !error" class="rounded-md bg-brand-50 px-3 py-2 text-sm font-semibold text-brand-700">
|
||||
{{ authNotice }}
|
||||
</p>
|
||||
<p v-if="error" class="rounded-md bg-red-50 px-3 py-2 text-sm font-semibold text-red-600">
|
||||
{{ error }}
|
||||
</p>
|
||||
<button class="btn-primary w-full" type="submit" :disabled="pending">
|
||||
{{ pending ? '확인 중...' : mode === 'login' ? '로그인' : '가입하고 시작하기' }}
|
||||
</button>
|
||||
@@ -74,18 +79,35 @@ const password = ref('')
|
||||
const error = ref('')
|
||||
const pending = ref(false)
|
||||
const session = useState('session')
|
||||
const route = useRoute()
|
||||
const isDev = import.meta.dev
|
||||
const redirectPath = computed(() => typeof route.query.redirect === 'string' ? route.query.redirect : '/')
|
||||
const authNotice = computed(() => {
|
||||
if (route.query.reason === 'expired') return '로그인 세션이 만료되었습니다. 다시 로그인해 주세요.'
|
||||
if (route.query.reason === 'required') return '가계부를 보려면 먼저 로그인해 주세요.'
|
||||
return ''
|
||||
})
|
||||
|
||||
async function submit() {
|
||||
const normalizedEmail = email.value.trim().toLowerCase()
|
||||
if (!normalizedEmail || !normalizedEmail.includes('@')) {
|
||||
error.value = '이메일 형식을 확인해 주세요.'
|
||||
return
|
||||
}
|
||||
if (password.value.length < 8) {
|
||||
error.value = '비밀번호는 8자 이상 입력해 주세요.'
|
||||
return
|
||||
}
|
||||
|
||||
pending.value = true
|
||||
error.value = ''
|
||||
try {
|
||||
const endpoint = mode.value === 'login' ? '/api/auth/login' : '/api/auth/register'
|
||||
session.value = await $fetch(endpoint, {
|
||||
method: 'POST',
|
||||
body: { name: name.value, email: email.value, password: password.value }
|
||||
body: { name: name.value, email: normalizedEmail, password: password.value }
|
||||
})
|
||||
await navigateTo('/')
|
||||
await navigateTo(redirectPath.value)
|
||||
} catch (err: any) {
|
||||
const status = err?.statusCode || err?.response?.status
|
||||
const statusMessage = err?.statusMessage || err?.response?._data?.statusMessage || ''
|
||||
@@ -97,6 +119,8 @@ async function submit() {
|
||||
: '가입 처리 중 문제가 생겼습니다. 잠시 후 다시 시도해 주세요.'
|
||||
} else if (mode.value === 'register') {
|
||||
error.value = '가입 처리 중 문제가 생겼습니다. 잠시 후 다시 시도해 주세요.'
|
||||
} else if (!status) {
|
||||
error.value = '서버에 연결할 수 없습니다. 네트워크 상태를 확인해 주세요.'
|
||||
} else {
|
||||
error.value = '이메일 또는 비밀번호를 확인해 주세요.'
|
||||
}
|
||||
@@ -110,9 +134,12 @@ async function devLogin() {
|
||||
error.value = ''
|
||||
try {
|
||||
session.value = await $fetch('/api/auth/dev-login', { method: 'POST' })
|
||||
await navigateTo('/')
|
||||
} catch {
|
||||
error.value = '개발용 로그인에 실패했습니다.'
|
||||
await navigateTo(redirectPath.value)
|
||||
} catch (err: any) {
|
||||
const status = err?.statusCode || err?.response?.status
|
||||
error.value = status === 404
|
||||
? '개발 환경에서만 사용할 수 있는 로그인입니다.'
|
||||
: '개발용 로그인에 실패했습니다.'
|
||||
} finally {
|
||||
pending.value = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user