13308a82ef
- 가계부: 대시보드(예산 대비 지출·분류별 파이·기간별 막대·순자산 추이), 내역(검색·필터·태그), 계좌(은행/카드/대출/투자), 예산, 분류, 정기 거래, 태그, 투자 포트폴리오(종목·매수/매도·평가손익) - 게시판: 목록/상세/작성(마크다운)/댓글/태그/열람제한 - 공통: 인증(Bearer 토큰·401 처리), 모바일 반응형(드로어·아이콘 버튼), Slim Budget 브랜딩(로고/타이틀/푸터), 홈 대시보드 자리 - 문서: docs/FRONTEND.md Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
68 lines
1.7 KiB
JavaScript
68 lines
1.7 KiB
JavaScript
import { defineStore } from 'pinia'
|
|
import { ref, computed } from 'vue'
|
|
import { authApi } from '@/api/authApi'
|
|
|
|
// 세션은 Redis(서버) + localStorage(클라이언트) 양쪽에 보관한다.
|
|
const TOKEN_KEY = 'token'
|
|
const USER_KEY = 'auth_user'
|
|
|
|
export const useAuthStore = defineStore('auth', () => {
|
|
const token = ref(localStorage.getItem(TOKEN_KEY) || '')
|
|
const user = ref(safeParse(localStorage.getItem(USER_KEY)))
|
|
|
|
const isAuthenticated = computed(() => !!token.value)
|
|
|
|
function persist() {
|
|
if (token.value) localStorage.setItem(TOKEN_KEY, token.value)
|
|
else localStorage.removeItem(TOKEN_KEY)
|
|
|
|
if (user.value) localStorage.setItem(USER_KEY, JSON.stringify(user.value))
|
|
else localStorage.removeItem(USER_KEY)
|
|
}
|
|
|
|
async function login(loginId, password) {
|
|
const res = await authApi.login({ loginId, password })
|
|
token.value = res.token
|
|
user.value = res.member
|
|
persist()
|
|
return res
|
|
}
|
|
|
|
async function signup(payload) {
|
|
return authApi.signup(payload)
|
|
}
|
|
|
|
// 서버 세션 기준으로 현재 사용자 정보 동기화 (토큰 유효성 확인 용도)
|
|
async function fetchMe() {
|
|
const me = await authApi.me()
|
|
user.value = { ...(user.value || {}), ...me }
|
|
persist()
|
|
return me
|
|
}
|
|
|
|
async function logout() {
|
|
try {
|
|
await authApi.logout()
|
|
} catch {
|
|
// 서버 세션이 이미 만료됐어도 클라이언트는 정리
|
|
}
|
|
clear()
|
|
}
|
|
|
|
function clear() {
|
|
token.value = ''
|
|
user.value = null
|
|
persist()
|
|
}
|
|
|
|
return { token, user, isAuthenticated, login, signup, fetchMe, logout, clear }
|
|
})
|
|
|
|
function safeParse(value) {
|
|
try {
|
|
return value ? JSON.parse(value) : null
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|