11d1655261
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7.5 KiB
7.5 KiB
돈돼지 가계부 — 백엔드 (sb_bt)
Spring Boot 기반의 가계부·자산·게시판 REST API 서버. (구 Slim Budget)
기술 스택
- Spring Boot 3.5 / Java 17
- MyBatis (XML 매퍼) + MariaDB
- Redis (세션 저장 — DB 이중 저장으로 유실 대비)
- spring-security-crypto (BCrypt 비밀번호 해시)
- Lombok, Bean Validation
패키지 구조
com.sb.web
├── account # 가계부 (controller / service / mapper / domain / dto)
│ └ invest·budget·category·recurring·quick·ocr·fx·backup 포함
├── auth # 인증 (로그인/세션/구글로그인, AuthInterceptor, 포인트)
├── billing # 인앱결제·구독(Google Play Billing 검증, RTDN)
├── board # 게시판 (글/댓글/추천/공지/이미지)
├── user # 사용자
├── admin # 관리자 (기본분류·게시판/가입 설정·회원 관리)
└── common # 공통 (예외, 응답, 헬스체크, 설정)
스키마 자동 초기화
spring.sql.init 로 기동 시 적용. 기본 mode: never, 환경변수 SQL_INIT_MODE=always 로 켜면 실행(멱등 — CREATE TABLE IF NOT EXISTS, ALTER TABLE ... ADD COLUMN IF NOT EXISTS):
schema-locations: db/member.sql, db/board.sql, db/account.sql, db/dev-seed.sql
continue-on-error: true
주요 테이블:
- 회원/인증:
member,auth_session(세션 DB 백업),point_history,app_setting(가입 허용 등),iap_purchase(인앱결제) - 가계부:
wallet,account_entry,account_category(parent_id대/소분류),account_tag,account_entry_tag,budget,budget_income,recurring,quick_entry,invest_holding,invest_trade - 게시판:
post,comment,post_vote,comment_vote,post_tag,tag,tag_category,tag_category_board,board_image,report - 기타:
default_category(기본 분류 템플릿)
인증 / 보안
- 로그인/구글로그인 시 토큰 발급, 이후
Authorization: Bearer <token>로 인증 - 구글 로그인:
POST /api/auth/google로 ID 토큰 수신 → tokeninfo 검증(aud=웹 클라이언트 ID) 후 세션 발급. 검증된 동일 이메일이면 기존 계정에member.google_id로 연결 AuthInterceptor가 현재 사용자(SessionUser)를 요청 속성(CURRENT_MEMBER)으로 주입- 세션 이중 저장: Redis 유실·재시작 시
auth_session(DB)에서 복원·재수화 - 무차별 대입 방지: 실패 시도만 카운트 — 로그인 IP당 10회/10분, 비밀번호 재인증 회원당 5회/10분 초과 시 429
- 봇 가입 차단: 허니팟 + IP 레이트리밋(Redis)
- 모든 가계부 데이터는
member_id(소유자)로 격리 — 본인 데이터만 조회/수정 - DB/Redis 자격증명·
ACCOUNT_CRYPTO_KEY(계좌번호 AES-256-GCM)·GOOGLE_CLIENT_ID등은.env(gitignore),.env.example템플릿 제공
REST API 개요
인증 · 사용자 · 관리자
/api/auth—login·signup·logout·google·me(GET/DELETE) /password·verify-password·profile·profile-image/points·point-history/signup-enabled·google-client-id/api/users— 사용자 CRUD/api/admin—signup-setting(가입 허용 토글),default-categories(기본 분류),members(회원 목록·role·plan·status변경·삭제), 태그 카테고리/api/billing—products·subscription·google/verify·cancel·resume·restore·google/rtdn(정기 알림)/api/board— 글/댓글 CRUD,posts/{id}/vote(추천),notice/unnotice(공지),block/unblock(열람제한),my/posts·my/comments,tag-groups,recommenders/api/board/images(업로드) ·/api/images/{id}(공개 조회) — 게시판 이미지(DB 저장, 절대 URL 반환)
가계부 /api/account
| 메서드·경로 | 설명 |
|---|---|
GET/POST/PUT/DELETE /entries |
내역 CRUD. 조회 필터: year,month,type,category,walletId,keyword,tagId |
POST /entries/notification POST /entries/parse |
카드/은행 알림 자동인식 등록 · 자연어 파싱 |
GET /entries/pending/count POST /entries/{id}/confirm |
확인 필요(pending) 건수 · 확정 |
GET /summary GET /stats |
월 요약 / 기간 버킷(일·주·월·년) 수입·지출 |
GET /category-stats |
분류별 합계 (파이차트, 대/소분류) |
GET/POST/PUT/DELETE /wallets (+/reorder) |
계좌(BANK·CASH·CARD·LOAN·INVEST) |
GET /networth GET /networth/trend |
순자산 / 순자산 추이(unit=WEEK&weeks=N 주간 지원) |
GET /wallets/{id}/entries POST /repayment |
계좌별 내역 / 상환·납부(원금=이체·이자=지출·연회비=지출) |
GET/POST/PUT/DELETE /tags (+/reorder) |
사용자별 가계부 태그 |
/categories (+/reorder,/import) |
분류 CRUD, 순서변경, 기존 내역에서 가져오기 |
/budgets (+/income,/copy,/status,/period) |
월별 예산 CRUD, 수입, 전월 복사, 진행률, 기간별 |
/recurrings (+/run) |
고정지출 규칙, 밀린 회차 자동 생성 |
/quick-entries |
자주 쓰는 내역 |
/invest/** |
투자 포트폴리오(아래) |
/ocr/receipt |
영수증 OCR (Google Vision) |
POST /restore |
백업 데이터 복구 (엑셀·JSON) |
환율:
GET /api/fx/rate— 외화 입력 시 환율 자동조회(원화 환산).
잔액 · 순자산 모델
- 부호 모델: 자산(+) / 부채(−). 현금(CASH)은 은행(BANK)과 동일하게 자산 합산
- 지갑 잔액 = 개시잔액 + 수입 − 지출 − 이체출금 + 이체입금 (
WalletMapper.findBalances) - 순자산 = Σ자산 − Σ부채. 투자계좌는 예수금 + 주식 평가금액으로 반영
- 순자산 추이: 현금흐름 누적(이체는 상쇄) + 투자 손익은 현재 시점에 가산. 주간(월요일 기준) 집계 지원
고정지출 (recurring)
- 빈도(DAILY/WEEKLY/MONTHLY/YEARLY) + 일/요일/월 지정
last_run_date커서로 중복 없이 밀린 회차를 따라잡아account_entry생성. 자동 생성분은 '확인 필요' 상태로 만들어 실제 처리 확인 후 확정
투자 포트폴리오 (/api/account/invest) — 종목 단위
| 경로 | 설명 |
|---|---|
GET/POST/PUT/DELETE /holdings |
보유 종목(종목명·코드·현재가). 집계: 수량·평단·평가금액·평가손익·수익률·실현손익 |
GET/POST /holdings/{id}/trades, PUT/DELETE /trades/{id} |
매수/매도 이력 (수정 시 시점별 시뮬레이션으로 보유수량 음수 방지) |
POST /holdings/refresh-prices, POST /refresh-prices |
종목코드로 현재가 자동 갱신(네이버 금융) |
- 매매이력(시간순)으로 이동평균 평단·실현손익·평가손익 산출. 매도수량이 보유수량 초과 시 400
- 투자계좌 가치 = 예수금(현금 ± 매매) + 주식평가, 순자산/추이에 연동
- 원화·정수 수량 기준 (해외주식 소수점/환율·자동 시세는 범위 외)
- ℹ️ 백엔드는 종목 단위 API를 유지하나, 현재 프론트 UI 는 투자금+평가액만 입력하는 단순형으로 노출
개발/실행
cp .env.example .env # DB/Redis 자격증명 입력 (SQL_INIT_MODE=always 로 스키마 자동생성)
./gradlew bootRun # http://localhost:8080
./gradlew test # 단위 + @WebMvcTest 통합 테스트
배포
main푸시 → Gitea Actions 가 CI(빌드·테스트) + Deploy(jar 전송·systemd 재시작) 실행. 테스트 실패 시 배포 중단- 상세: deploy/README.md