feat: 멤버십(무료/유료) 게이팅 — 백엔드

- member.plan(FREE/PREMIUM) 컬럼 추가 + SessionUser/MemberResponse 노출
- auth_session 백업에 plan 저장(세션 복원 시 유료 상태 유지)
- 관리자 회원 plan 변경 API (PUT /api/admin/members/{id}/plan)
- PremiumInterceptor 로 유료 전용 경로 차단(통계/예산/고정지출/태그/OCR/투자/카드알림/백업)
  · 관리자는 플랜과 무관하게 허용, 무료 회원은 403 PREMIUM_REQUIRED
- WebConfigTest: 유료 경로 등록 회귀 가드 추가

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-06-28 11:02:19 +09:00
co-authored by Claude Opus 4.8
parent 93e06c9475
commit 6256d3e63d
15 changed files with 122 additions and 5 deletions
@@ -2,6 +2,7 @@ package com.sb.web.common.config;
import com.sb.web.auth.web.AdminInterceptor;
import com.sb.web.auth.web.AuthInterceptor;
import com.sb.web.auth.web.PremiumInterceptor;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
@@ -33,7 +34,8 @@ class WebConfigTest {
when(registry.addInterceptor(any())).thenReturn(registration);
when(registration.addPathPatterns(any(String[].class))).thenReturn(registration);
new WebConfig(mock(AuthInterceptor.class), mock(AdminInterceptor.class)).addInterceptors(registry);
new WebConfig(mock(AuthInterceptor.class), mock(AdminInterceptor.class), mock(PremiumInterceptor.class))
.addInterceptors(registry);
ArgumentCaptor<String[]> captor = ArgumentCaptor.forClass(String[].class);
verify(registration, atLeastOnce()).addPathPatterns(captor.capture());
@@ -47,5 +49,12 @@ class WebConfigTest {
"/api/auth/password",
"/api/auth/verify-password",
"/api/auth/profile");
// 유료(PREMIUM) 전용 경로가 premiumInterceptor 에 등록되는지 회귀 가드
assertThat(allPatterns).contains(
"/api/account/stats",
"/api/account/budgets/**",
"/api/account/recurrings/**",
"/api/account/tags",
"/api/account/restore");
}
}