소셜 핀테크 — 원장 파생 지표 · 또래 비교 · 미션
FinMate — 청년 금융 온보딩
- 기간
- 2026.04 – 2026.07
- 역할
- 풀스택 — 앱의 프론트·백엔드 단독
- 참여 인력
- 4명 — 기획 2 / 풀스택 1(본인) / 데이터 1 · 가가제작소, 하나금융그룹 × 금융감독원 2026 청년 금융인재
React 19TypeScript strictViteTailwind CSS v4Motion v12React Router 7html-to-imageJava 21 · Spring Boot 3.5PostgreSQL 16Flyway · Testcontainersfal.ai (FLUX)
GitHub에서 코드 보기 ↗서비스
20대가 금융을 미루지 않고 오늘 작은 행동 하나로 시작하게 만드는 모바일 서비스입니다.
20대는 금융관리의 필요성은 알지만 '무엇부터 해야 할지 몰라서', '어렵고 귀찮아서' 시작을 미룹니다. 필요한 건 더 많은 정보가 아니라 마찰 없는 첫 걸음입니다.
그래서 또래의 금융 행동을 구경하게 해 동기를 만들고, 지금 할 수 있는 작은 미션으로 연결하고, 하루가 끝나면 그날의 소비를 그림 한 장으로 남깁니다.
- 피드
- 분석
- 마이
- 미션
- 기록


- 데모 UI
- React 19 · TypeScript strict · Tailwind v4 · Motion v12 · 커스텀 SVG 차트
- 직접 띄워보기
cd finmate-app && npm install && npm run dev→ localhost:5173 (모바일 390×844 · 화면은 finmate-app, 서버는 finmate-api)
e2e가 화면에서 재현하는 것
- 차트 라이브러리를 쓰지 않고 SVG와 Motion으로 직접 그렸다 — 물잔 수위는 사인파 두 장의 수평 루프다
- 화면의 모든 수치는 거래 원장에서 파생된다. 예산 챌린지의 일·주·월 판정도 전부 거래에서 계산한다
위 화면은 로컬 데모 실행 결과입니다. 운영 중인 서비스가 아닙니다.
요약
- 또래 비교가 매 요청 원장 88만 행을 다시 세던 것을 사람×월 사전 집계로 접어 p50 32.5ms → 0.72ms
- 고치기 전에 먼저 재서 개인 화면은 p95 0.96ms로 문제가 아님을 확인하고, 인덱스(50MB·24.0ms) 대신 사전 집계(1.9MB·0.72ms)를 선택
- 외부 이미지 생성 API가 3~6초 걸리는 동안 행 잠금이 풀리던 것을 상태 값으로 선점하게 바꿔 두 인스턴스가 같은 작업을 가져가지 못하게 함
- 합성 데이터셋 2,000명 887,002행을 JDBC 배치로 적재(34,490행/초)하고 원장·프로필 정합성을 통합 테스트로 고정
01청년 금융 · 인구 집계
또래 비교가 매번 88만 행을 세던 것을 미리 접어 32.5ms → 0.72ms
문제 원인
- 또래 비교는 소득대가 같은 사람 전부를 가로질러 집계하므로 한 달치가 105,484행
- 조건이 기간뿐이라 (persona_id, occurred_on) 인덱스의 선행 컬럼이 없어 Parallel Seq Scan
- count(DISTINCT persona_id)가 정렬을 강요해 work_mem을 넘기고 디스크 정렬 2,496kB 발생
해결 과정
- 고치기 전에 먼저 측정 — 개인 화면도 함께 재보니 p95 0.96ms로 문제가 아니었고, 예상과 달리 인구 집계만 비쌌음
- 싼 것부터 검증 — 쿼리 수정으로 디스크 정렬을 없애 28.0ms, 커버링 인덱스는 50MB를 쓰고 24.0ms에 그쳐 채택하지 않음
- 사람×월 사전 집계를 도입하되 원장을 유일한 진실로 두어 언제든 통째로 재생성 가능하게 설계
결과
- p50 32.5ms → 0.72ms, 읽는 버퍼 23,326 → 206 (2,000명 · 원장 887,002행 기준)
- 50명이 동시에 화면을 넘길 때 또래 비교 p95 25.39ms — 순차로 잰 0.72ms와는 다른 숫자다
- 사전 집계는 14,000행 1.9MB로 원장의 1.1%, 두 방식의 결과가 소득대 6개 그룹 전부 일치
구현 기능
위 문제 해결 외에, 서비스가 돌아가기 위해 구현한 것들입니다.
- 하루의 거래에서 그날의 주인공(소비·저축·투자)을 뽑아 AI 그림일기 한 장으로 기록 — 하루 한 장 멱등성, 3회 재시도, 응답 없는 작업 회수
- 예산 챌린지 판정을 저장하지 않고 매번 원장에서 세고, 포인트는 잔액이 아니라 원장으로 쌓아 이중 지급 차단 — 또래 비교는 금액을 구간으로만 내보내고 20명 미만 그룹은 만들지 않는다
- 화면의 모든 수치를 거래 원장에서 파생하는 순수 셀렉터로 통일하고, 차트는 라이브러리 없이 SVG로 직접 구현해 번들에 차트 의존성 0개