Skip to content

feat : PRD A1~A3 반영 (해커톤 레포 이관) 및 리뷰 후속 정리 - #41

Merged
zwo-n merged 10 commits into
mainfrom
yuyeol3/feat/prd-a1-a3
Nov 2, 2026
Merged

zwo-n merged 10 commits into
mainfrom
yuyeol3/feat/prd-a1-a3

Conversation

@yuyeol3

@yuyeol3 yuyeol3 commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

변경점 👍

해커톤 모노레포에서 머지된 PRD A1~A3 작업(PNU-2026-AI-Hackathon/pnuai-c-07-finfin2#42)을 Fin-BE로 옮기고, 원본 PR 리뷰에서 남은 항목까지 이 PR에서 함께 처리했습니다. 후속 PR로 올렸던 #42는 이 PR에 합쳤습니다.

1. PRD A1~A3 이관 (27c0717)

  • 원본 PR의 backend/, database/ 변경을 Fin-BE 경로로 옮겼습니다. 커밋 작성자는 원저자로 유지했습니다.
  • 내용: 저축기간 기준 대분류 라우팅(1·3개월은 단기예치, 그 외는 목돈만들기), 세후 실수령액, 파킹통장 탭
  • 텍스트 충돌은 SearchRequestPolicy의 import 한 줄뿐이었습니다. #33에서 추가된 List는 쓰이지 않아 Set만 남겼습니다.
  • 모노레포에만 해당하는 변경(.gitignore, api/)은 제외했습니다.

2. 저축기간 옵션 마이그레이션 번호 V7 → V9 (7f525c2)

  • #35가 V7 자리를 비워 두고 V8로 먼저 머지됐습니다.
  • V7 그대로 들어오면 V8까지 적용된 개발 DB에서 Flyway 검증이 실패해 서버가 뜨지 않습니다.
    • Detected resolved migration not applied to database: 7. (outOfOrder=false 기본값)
    • 테스트는 매번 빈 DB에 V1부터 적용해서 이 문제가 드러나지 않습니다.
  • V9로 옮기면 V8이 적용된 DB와 빈 DB 모두 순서대로 적용됩니다. V7 번호는 비워 둡니다.
  • V8은 ALTER만 하므로 빈 DB에서 category_option ID는 V7일 때와 같습니다.

3. 파킹 카드 필드를 V8 컬럼과 연결 (f12559b)

  • #35에서 컬럼만 추가되고 엔티티에는 매핑되지 않은 상태였습니다.
  • ProductProperty에 maxRateApplicableMinAmount, maxRateApplicableMaxAmount, interestPaymentMethod를 매핑했습니다.
  • ParkingProductDto 응답 필드 변경 (Fin-FE에는 아직 쓰는 곳이 없습니다)
필드 변경 설명
applicableLimit 삭제 적금 전용 컬럼인 max_monthly_limit을 읽고 있었음
maxRateApplicableMinAmount / MaxAmount 추가 최고금리가 적용되는 잔액 범위. 하한은 초과, 상한은 이하
maxDepositAmount 추가 적용 범위 정보가 없을 때 대신 표시할 최대 예치한도
interestPaymentMethod 변경 "매일" 고정값 대신 공시 원문

4. 정렬 테스트 정리 (b5888aa)

  • 기존 ShortTermSortingTest는 SearchService의 private 정렬 로직을 테스트 안에 복사해 두고 그 복사본을 검증했습니다. 탭B 은행 정렬의 reversed() 버그가 다시 생겨도 통과하는 상태였습니다.
  • 정렬 메서드를 package-private static으로 열고(collapseToBestPerProduct와 같은 방식), SearchServiceSortingTest에서 실제 메서드를 호출합니다.
  • 호출처가 없던 sortedByAchievableRate와 그 복사본을 검증하던 탭D 테스트 3개를 삭제했습니다. 실제 파킹 정렬은 ParkingProductServiceTest가 검증합니다.
  • LongTermSortingIntegrationTest를 추가해 searchUnified 결과로 탭B 정렬을 검증합니다.
    • 은행: 실수령액 내림차순
    • 정부: 환산수익률이 낮아도 기여금 총액이 크면 먼저

5. 코드 품질 정리 (ecc4e99, 06b9942, e794334)

  • 호출처 없는 코드 삭제
    • MatchScoreService.isGovBankConditionExcluded, effectiveAmount 2개
    • SearchRequestDto.isShortTerm/isLongTerm(keywords), ProductCategoryEnum.fromKeyword, KeywordValueEnum.isShortTerm/isLongTerm
    • 실제 대분류 결정은 fromSaveTrm(getSaveTrm(keywords)) 경로만 쓰므로 CategoryRoutingTest도 이 경로를 검증하도록 바꿨습니다.
  • 인접 기간 배점 테스트 보강
    • 기존: 12개월 선택만, "0 초과 20 미만"으로만 확인 (isAdjacentPeriod 분기 29개 중 23개 미검증)
    • 변경: 1·3·6·12·24·36개월 각각 바로 앞뒤 구간은 정확 매칭의 절반, 그 외는 0점인지 파라미터화 테스트 19개로 검증
  • MatchScoreService의 V2·TOP3 가중치 재분배 로직이 27줄 그대로 중복돼 있어 redistributeInactive로 합쳤습니다. 리팩터링 전에 TOP3 재분배 특성화 테스트 2개를 먼저 추가했습니다.
  • 결과(원본 코드 대비): PMD 이슈 20건 → 18건, 중복 블록 2개 → 1개(main에 원래 있던 계산기 중복)

버그 해결 💊

금액 하한 검증을 분리된 예치 한도 컬럼 기준으로 수정 (0c42380)

  • 원본 PR은 예금과 적금이 min_monthly_limit을 같이 쓰던 시점 기준으로 작성됐습니다. #32에서 예금 한도가 min/max_deposit_amount로 분리된 뒤로 하한 검증이 잘못 동작했습니다.
    • 단기 예금: min_monthly_limit이 null이라 하한 검사가 사실상 꺼져 있었음
    • 단기 적금: 월 최소 납입액을 예치 총액과 비교 (단위 불일치)
    • 파킹: min_deposit_amount 대신 min_monthly_limit과 비교
  • 수정 후에는 상품 유형에 맞는 금액끼리 비교합니다. 금액은 원본 PR의 normalizedMonthlyDeposit / normalizedDepositPrincipal로 정규화합니다.
상품 비교 금액 하한 컬럼
예금 예치 원금 (단기: 예치액, 목돈: 월저축액×기간) min_deposit_amount
적금 월 납입액 (단기: 예치액÷기간, 목돈: 월저축액) min_monthly_limit
파킹 예치액 min_deposit_amount

단기예치 상품 수에서 파킹통장 중복 집계 수정 (70e0237)

  • 파킹통장을 옵션 단위(parkingProducts.size())로 세고 있어서 옵션이 여러 개인 상품이 중복 집계됐습니다.
  • 예적금처럼 상품 ID로 distinct해서 셉니다.
  • 지금 수집 데이터는 KFB 파킹이 상품당 옵션 1개라 실제 값은 달라지지 않습니다.

테스트 💻

  • ./gradlew test(367개), ./gradlew integrationTest(125개) 모두 통과합니다.
  • 구현 전 실패 확인
    • EligibilityBoundaryIntegrationTest 예치금 하한 경계 케이스 5개 중 3개 (단기 예금, 단기 적금, 목돈 예금)
    • 파킹 DTO 테스트, 상품 수 테스트 (기대 4, 실제 5)
  • 버그를 일부러 넣어서 테스트가 잡는지 확인
    • 6개월 인접 구간을 틀리게 바꾸면 인접 기간 테스트 2개 실패
    • reversed() 버그나 "정부상품을 환산수익률 우선으로 정렬"을 넣으면 SearchServiceSortingTest 2개, LongTermSortingIntegrationTest 2개 실패
  • 엔티티 매핑은 ddl-auto: validate로 V8 스키마와 일치하는 것을 확인했습니다.
  • V8까지 적용된 로컬 DB 복사본에서 bootRun
    • V7일 때는 위 Flyway 에러로 기동 실패
    • V9일 때는 Current version: 8 → v9로 적용된 뒤 정상 기동

스크린샷 🖼

V8까지 적용된 로컬 DB 복사본으로 서버를 띄우고(V9 적용) Swagger에서 POST /search/products를 호출했습니다. 비로그인 상태라 실수령액 탭(B, C)은 비활성입니다.

단기예치 (3개월, 예치액 1천만 원)

{
  "options": [
    { "categoryId": 3, "optionId": 38 }
  ],
  "detailedOptions": {
    "birthdate": "2000-01-01",
    "depositAmount": 10000000
  }
}
  • category가 SHORT_TERM으로 라우팅되고 파킹통장 탭(D)이 활성화됩니다.
  • 파킹 카드에 V8 컬럼 값이 채워집니다.
    • 광주은행 매일이자Wa파킹통장: 잔액 30만 원 이하에서 최고금리 5.1% (maxRateApplicableMaxAmount: 300000)
    • SC제일은행 스마트박스통장: 적용 범위 정보 없음, 이자지급방식 월지급
short-term-parking

목돈만들기 (1년, 월 50만 원)

{
  "options": [
    { "categoryId": 3, "optionId": 40 },
    { "categoryId": 6, "optionId": 32 }
  ],
  "detailedOptions": {
    "birthdate": "2000-01-01",
    "monthlySavingsGoal": 500000
  }
}
  • category가 LONG_TERM으로 라우팅되고 적합도순 탭(A)에 정부·은행 상품이 나옵니다.
long-term-routing

비고 ✏

궁금한 점 있으시면 편하게 질문 주세요!

zwo-n and others added 10 commits October 18, 2026 19:25
PNU-2026-AI-Hackathon/pnuai-c-07-finfin2#42 (33a2b2c)의 backend/, database/ 변경을 Fin-BE 경로로 옮겼다.
- backend/ → 루트, database/db/migration → src/main/resources/db/migration
- 충돌 해결: SearchRequestPolicy import (#33의 미사용 List 대신 Set)
- 모노레포 전용 변경(.gitignore, api/)은 제외
#32에서 예금 한도가 min/max_deposit_amount로 분리되었는데 PRD A1~A3 이관분은 min_monthly_limit만 비교하고 있었다.
- 예금: 예치 원금(단기 예치액, 목돈 월저축액×기간) vs min_deposit_amount
- 적금: 월 납입액(단기 예치액÷기간, 목돈 월저축액) vs min_monthly_limit
- 파킹: 예치액 vs min_deposit_amount (V8 기준 예금·파킹 공용)
#35의 V8이 먼저 적용된 DB에서 Flyway가 out-of-order 검증으로
기동에 실패하는 문제를 수정한다.
- ProductProperty에 V8 컬럼(max_rate_applicable_min/max_amount, interest_payment_method) 매핑
- 최고금리 적용 한도: 적금 전용 max_monthly_limit 대신 적용 범위 하한/상한으로 응답
- 적용 범위가 없을 때 표시할 최대 예치한도(max_deposit_amount) 함께 응답
- 이자지급방식: "매일" 고정값 대신 공시 원문 사용
- SearchService 정렬 메서드를 package-private static으로 열고 SearchServiceSortingTest에서 직접 호출
  (복사한 로직을 검증하던 ShortTermSortingTest 대체, collapseToBestPerProduct와 같은 방식)
- 호출처 없는 sortedByAchievableRate와 그 복사본 테스트(탭D) 제거. 파킹 정렬은 ParkingProductServiceTest가 검증
- LongTermSortingIntegrationTest: searchUnified 결과로 탭B 은행(실수령액순)·정부(기여금총액순) 정렬 검증
- MatchScoreService.isGovBankConditionExcluded (PMD UnusedPrivateMethod)
- SearchRequestDto/DetailedOptionsDto.effectiveAmount, SearchRequestDto.isShortTerm/isLongTerm(keywords)
- ProductCategoryEnum.fromKeyword, KeywordValueEnum.isShortTerm/isLongTerm
실제 대분류 결정은 SearchService.determineCategory(fromSaveTrm(getSaveTrm(keywords)))만 쓰므로
CategoryRoutingTest도 그 경로로 검증하도록 바꿨다.
기존 테스트는 12개월 선택(24·10개월 상품)만, 그것도 "0 초과 20 미만"으로만 확인했다.
1·3·6·12·24·36개월 각각에 대해 바로 앞뒤 구간은 정확 매칭의 정확히 절반, 그 외는 0점인지 검증한다.
distributeWeights(V2)와 distributeTop3Weights가 같은 재분배 로직(미선택 축 가중치를 활성 축에 비례 배분)을
복사해 쓰고 있었다(CPD 27줄 중복). redistributeInactive로 합쳤다.
TOP3 재분배는 직접 검증하는 테스트가 없어 리팩터링 전에 특성화 테스트를 추가했다.
@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Backend Code Health Report

Commit: b192d2b

Metric Current Base Change
Line Coverage 89.7% 88.8% +0.9 pp ✅
Branch Coverage 73.4% 73.2% +0.2 pp ✅
Code Health 92.8 (A) 92.8 (A) +0.0 ✅
Duplicated Lines 0.4% 0.4% -0.1 pp ✅
High-complexity Methods 13 10 +3 ⚠️
PMD Issues 18 13 +5 ⚠️

Attention

  • src/main/java/apptive/fin/search/service/ProductDetailService.java:113 — Cognitive Complexity 27 / Cyclomatic Complexity 25
  • src/main/java/apptive/fin/myfin/service/MyFinService.java:216 — Cognitive Complexity 19 / Cyclomatic Complexity 10
  • src/main/java/apptive/fin/search/service/SearchService.java:176 — Cognitive Complexity 18
  • Duplicated block (16 lines): src/main/java/apptive/fin/calculator/service/DepositRateCalculator.java:46 ↔ src/main/java/apptive/fin/calculator/service/SavingRateCalculator.java:60

Informational report only — these metrics do not block merging.

@yuyeol3
yuyeol3 marked this pull request as draft October 26, 2026 09:06
@yuyeol3 yuyeol3 changed the title feat : PRD A1~A3 반영 (해커톤 레포 이관) feat : PRD A1~A3 반영 (해커톤 레포 이관) 및 리뷰 후속 정리 Nov 1, 2026
@yuyeol3
yuyeol3 marked this pull request as ready for review November 1, 2026 11:24
@yuyeol3
yuyeol3 requested a review from zwo-n November 1, 2026 11:24
@zwo-n
zwo-n merged commit 4541839 into main Nov 2, 2026
5 checks passed
@zwo-n
zwo-n deleted the yuyeol3/feat/prd-a1-a3 branch November 2, 2026 02:22
@zwo-n

zwo-n commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

수고하셨습니다!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants