feat: 시세 자동조회·퇴직연금 평가액·게시판 카테고리·태그 정렬·계좌번호 암호화

- 투자: 종목코드로 현재가 시세 자동조회(StockQuoteService, 투자탭/포트폴리오 진입 시 갱신)
- 투자: 퇴직연금 등 평가액 직접입력형 계좌(currentValue 활성화, manualValuation)
- 게시판: community/saving/tips 카테고리 분리(post.category + 목록 필터)
- 태그: 순서 변경(account_tag.sort_order, /tags/reorder)
- 보안: 계좌번호 AES-256-GCM 암호화 저장/복호화(EncryptedStringTypeHandler, account_number VARCHAR(255))
  키는 서버 .env ACCOUNT_CRYPTO_KEY 로 주입(미설정 시 평문 통과)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-06-04 05:00:18 +09:00
co-authored by Claude Opus 4.8
parent 8fec057752
commit fb94df8112
24 changed files with 401 additions and 29 deletions
@@ -40,15 +40,16 @@ public class BoardService {
/* ===================== 게시글 ===================== */
public PageResponse<PostSummary> list(int page, int size, String tag, String keyword, String searchType) {
public PageResponse<PostSummary> list(int page, int size, String category, String tag, String keyword, String searchType) {
int p = Math.max(page, 1);
int s = Math.min(Math.max(size, 1), 100);
int offset = (p - 1) * s;
String st = normalizeSearchType(searchType);
String cat = normalizeCategory(category);
// 목록에는 태그를 표시하지 않으므로 게시글별 태그 조회를 생략한다.
List<PostSummary> content = postMapper.findPage(offset, s, blankToNull(tag), blankToNull(keyword), st);
long total = postMapper.countPage(blankToNull(tag), blankToNull(keyword), st);
List<PostSummary> content = postMapper.findPage(offset, s, cat, blankToNull(tag), blankToNull(keyword), st);
long total = postMapper.countPage(cat, blankToNull(tag), blankToNull(keyword), st);
return PageResponse.of(content, p, s, total);
}
@@ -56,6 +57,18 @@ public class BoardService {
return ("content".equals(t) || "comment".equals(t)) ? t : "title";
}
/** 목록 필터용: 빈값이면 null(전체), 알 수 없는 값은 community 로 보정 */
private String normalizeCategory(String c) {
if (c == null || c.isBlank()) return null;
return ("saving".equals(c) || "tips".equals(c) || "community".equals(c)) ? c : "community";
}
/** 저장용: 빈값/알 수 없는 값은 community 로 기본 설정 */
private String toCategory(String c) {
if (c == null || c.isBlank()) return "community";
return ("saving".equals(c) || "tips".equals(c)) ? c : "community";
}
/** 상세 조회 (열람 제한 글은 관리자만 전체 열람, 그 외엔 제한 응답) */
@Transactional
public PostDetail get(Long id, SessionUser viewer) {
@@ -95,6 +108,7 @@ public class BoardService {
.content(req.getContent())
.authorId(author.getId())
.authorName(author.getName())
.category(toCategory(req.getCategory()))
.build();
postMapper.insert(post);
applyTags(post.getId(), req.getTagIds());