feat: 가계부·게시판 프론트엔드 구현 + Slim Budget 브랜딩/모바일 대응
- 가계부: 대시보드(예산 대비 지출·분류별 파이·기간별 막대·순자산 추이), 내역(검색·필터·태그), 계좌(은행/카드/대출/투자), 예산, 분류, 정기 거래, 태그, 투자 포트폴리오(종목·매수/매도·평가손익) - 게시판: 목록/상세/작성(마크다운)/댓글/태그/열람제한 - 공통: 인증(Bearer 토큰·401 처리), 모바일 반응형(드로어·아이콘 버튼), Slim Budget 브랜딩(로고/타이틀/푸터), 홈 대시보드 자리 - 문서: docs/FRONTEND.md Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
import http from './http'
|
||||
|
||||
// 백엔드 /api/account 엔드포인트와 매핑 (본인 데이터만)
|
||||
export const accountApi = {
|
||||
list({ year, month, type, category, walletId, keyword, tagId } = {}) {
|
||||
return http.get('/account/entries', { params: { year, month, type, category, walletId, keyword, tagId } })
|
||||
},
|
||||
summary({ year, month } = {}) {
|
||||
return http.get('/account/summary', { params: { year, month } })
|
||||
},
|
||||
stats({ unit, year, month } = {}) {
|
||||
return http.get('/account/stats', { params: { unit, year, month } })
|
||||
},
|
||||
categoryStats({ type, year, month } = {}) {
|
||||
return http.get('/account/category-stats', { params: { type, year, month } })
|
||||
},
|
||||
netWorthTrend({ months } = {}) {
|
||||
return http.get('/account/networth/trend', { params: { months } })
|
||||
},
|
||||
create(payload) {
|
||||
return http.post('/account/entries', payload)
|
||||
},
|
||||
update(id, payload) {
|
||||
return http.put(`/account/entries/${id}`, payload)
|
||||
},
|
||||
remove(id) {
|
||||
return http.delete(`/account/entries/${id}`)
|
||||
},
|
||||
|
||||
// 계좌/카드 (사용자별)
|
||||
wallets() {
|
||||
return http.get('/account/wallets')
|
||||
},
|
||||
netWorth() {
|
||||
return http.get('/account/networth')
|
||||
},
|
||||
repayment(payload) {
|
||||
return http.post('/account/repayment', payload)
|
||||
},
|
||||
|
||||
// 정기/반복 거래
|
||||
recurrings() {
|
||||
return http.get('/account/recurrings')
|
||||
},
|
||||
createRecurring(payload) {
|
||||
return http.post('/account/recurrings', payload)
|
||||
},
|
||||
updateRecurring(id, payload) {
|
||||
return http.put(`/account/recurrings/${id}`, payload)
|
||||
},
|
||||
removeRecurring(id) {
|
||||
return http.delete(`/account/recurrings/${id}`)
|
||||
},
|
||||
runRecurrings() {
|
||||
return http.post('/account/recurrings/run')
|
||||
},
|
||||
|
||||
// 예산 (사용자별)
|
||||
budgets() {
|
||||
return http.get('/account/budgets')
|
||||
},
|
||||
budgetStatus({ year, month }) {
|
||||
return http.get('/account/budgets/status', { params: { year, month } })
|
||||
},
|
||||
budgetPeriod({ unit, year, month }) {
|
||||
return http.get('/account/budgets/period', { params: { unit, year, month } })
|
||||
},
|
||||
createBudget(payload) {
|
||||
return http.post('/account/budgets', payload)
|
||||
},
|
||||
updateBudget(id, payload) {
|
||||
return http.put(`/account/budgets/${id}`, payload)
|
||||
},
|
||||
removeBudget(id) {
|
||||
return http.delete(`/account/budgets/${id}`)
|
||||
},
|
||||
createWallet(payload) {
|
||||
return http.post('/account/wallets', payload)
|
||||
},
|
||||
updateWallet(id, payload) {
|
||||
return http.put(`/account/wallets/${id}`, payload)
|
||||
},
|
||||
removeWallet(id) {
|
||||
return http.delete(`/account/wallets/${id}`)
|
||||
},
|
||||
walletEntries(id) {
|
||||
return http.get(`/account/wallets/${id}/entries`)
|
||||
},
|
||||
|
||||
// 투자 포트폴리오 (C) — 종목/매매
|
||||
investHoldings(walletId) {
|
||||
return http.get('/account/invest/holdings', { params: { walletId } })
|
||||
},
|
||||
createHolding(payload) {
|
||||
return http.post('/account/invest/holdings', payload)
|
||||
},
|
||||
updateHolding(id, payload) {
|
||||
return http.put(`/account/invest/holdings/${id}`, payload)
|
||||
},
|
||||
removeHolding(id) {
|
||||
return http.delete(`/account/invest/holdings/${id}`)
|
||||
},
|
||||
holdingTrades(holdingId) {
|
||||
return http.get(`/account/invest/holdings/${holdingId}/trades`)
|
||||
},
|
||||
addTrade(holdingId, payload) {
|
||||
return http.post(`/account/invest/holdings/${holdingId}/trades`, payload)
|
||||
},
|
||||
removeTrade(id) {
|
||||
return http.delete(`/account/invest/trades/${id}`)
|
||||
},
|
||||
|
||||
// 분류(카테고리) — 사용자별
|
||||
categories() {
|
||||
return http.get('/account/categories')
|
||||
},
|
||||
createCategory(payload) {
|
||||
return http.post('/account/categories', payload)
|
||||
},
|
||||
updateCategory(id, payload) {
|
||||
return http.put(`/account/categories/${id}`, payload)
|
||||
},
|
||||
removeCategory(id) {
|
||||
return http.delete(`/account/categories/${id}`)
|
||||
},
|
||||
importCategories() {
|
||||
return http.post('/account/categories/import')
|
||||
},
|
||||
|
||||
// 사용자별 가계부 태그
|
||||
tags() {
|
||||
return http.get('/account/tags')
|
||||
},
|
||||
createTag(payload) {
|
||||
return http.post('/account/tags', payload)
|
||||
},
|
||||
updateTag(id, payload) {
|
||||
return http.put(`/account/tags/${id}`, payload)
|
||||
},
|
||||
removeTag(id) {
|
||||
return http.delete(`/account/tags/${id}`)
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import http from './http'
|
||||
|
||||
// 관리자 태그 관리 API (/api/admin)
|
||||
export const adminApi = {
|
||||
categories() {
|
||||
return http.get('/admin/tag-categories')
|
||||
},
|
||||
createCategory(payload) {
|
||||
return http.post('/admin/tag-categories', payload)
|
||||
},
|
||||
updateCategory(id, payload) {
|
||||
return http.put(`/admin/tag-categories/${id}`, payload)
|
||||
},
|
||||
removeCategory(id) {
|
||||
return http.delete(`/admin/tag-categories/${id}`)
|
||||
},
|
||||
createTag(payload) {
|
||||
return http.post('/admin/tags', payload)
|
||||
},
|
||||
updateTag(id, payload) {
|
||||
return http.put(`/admin/tags/${id}`, payload)
|
||||
},
|
||||
removeTag(id) {
|
||||
return http.delete(`/admin/tags/${id}`)
|
||||
},
|
||||
getBoardSetting() {
|
||||
return http.get('/admin/board-setting')
|
||||
},
|
||||
setBoardSetting(tagCategoryId) {
|
||||
return http.put('/admin/board-setting', { tagCategoryId })
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import http from './http'
|
||||
|
||||
// 백엔드 /api/auth 엔드포인트와 매핑
|
||||
export const authApi = {
|
||||
signup(payload) {
|
||||
return http.post('/auth/signup', payload)
|
||||
},
|
||||
login(payload) {
|
||||
return http.post('/auth/login', payload)
|
||||
},
|
||||
logout() {
|
||||
return http.post('/auth/logout')
|
||||
},
|
||||
me() {
|
||||
return http.get('/auth/me')
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import http from './http'
|
||||
|
||||
// 백엔드 /api/board 엔드포인트와 매핑
|
||||
export const boardApi = {
|
||||
list({ page = 1, size = 10, tag = '', keyword = '', searchType = '' } = {}) {
|
||||
return http.get('/board/posts', {
|
||||
params: {
|
||||
page,
|
||||
size,
|
||||
tag: tag || undefined,
|
||||
keyword: keyword || undefined,
|
||||
searchType: keyword ? searchType || 'title' : undefined,
|
||||
},
|
||||
})
|
||||
},
|
||||
get(id) {
|
||||
return http.get(`/board/posts/${id}`)
|
||||
},
|
||||
create(payload) {
|
||||
return http.post('/board/posts', payload)
|
||||
},
|
||||
update(id, payload) {
|
||||
return http.put(`/board/posts/${id}`, payload)
|
||||
},
|
||||
remove(id) {
|
||||
return http.delete(`/board/posts/${id}`)
|
||||
},
|
||||
tags() {
|
||||
return http.get('/board/tags')
|
||||
},
|
||||
tagGroups() {
|
||||
return http.get('/board/tag-groups')
|
||||
},
|
||||
addComment(id, content) {
|
||||
return http.post(`/board/posts/${id}/comments`, { content })
|
||||
},
|
||||
removeComment(commentId) {
|
||||
return http.delete(`/board/comments/${commentId}`)
|
||||
},
|
||||
block(id, reason) {
|
||||
return http.post(`/board/posts/${id}/block`, { reason })
|
||||
},
|
||||
unblock(id) {
|
||||
return http.post(`/board/posts/${id}/unblock`)
|
||||
},
|
||||
}
|
||||
+4
-2
@@ -27,8 +27,10 @@ http.interceptors.response.use(
|
||||
(error) => {
|
||||
const status = error.response?.status
|
||||
if (status === 401) {
|
||||
// 인증 만료 처리 등
|
||||
console.warn('인증이 필요합니다.')
|
||||
// 세션 만료/무효: 클라이언트 저장값 정리 후 로그인 팝업 트리거
|
||||
localStorage.removeItem('token')
|
||||
localStorage.removeItem('auth_user')
|
||||
window.dispatchEvent(new CustomEvent('auth:unauthorized'))
|
||||
}
|
||||
return Promise.reject(error)
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user