외화자산관리시스템개발
외화 자산 관리 시스템
1. 기능정의
1-1. Must Have (핵심 MVP)
1) 사용자별 외화 지갑 (통화별 잔액 관리)
- 개발 포인트
- 사용자 계정 1명 기준 (멀티 유저 구조는 설계만)
- 통화는 ISO 코드 기반 (USD, JPY, EUR 등)
- 세부 기능
- 통화 지갑 생성
- 환전 기능
- 통화별 잔액 조회
- 잔액 증감 처리
- 수동 입금 / 출금
- 거래 사유 메모
- 잔액 검증
- 출금 시 잔액 부족 방지
- 데이터
- Wallet
- WalletTransaction
2) 한국수출입은행 환율정보 외부 API 연동
- 개발 포인트
- 외부 API → 내부 DTO 변환
- 캐싱 필수 (API 남용 방지)
- 세부 기능
- 오늘 기준 환율 조회
- 통화별 매매기준율 저장
- API 실패 시 예외 처리
- 최근 스냅샷 있으면 fallback
3) 기준 통화(KRW/USD) 자산 환산
- 개발 포인트
- 계산 로직 정확성 + 소수점 처리
- 세부 기능
- 기준통화 선택 (KRW / USD)
- 전체 자산 합계 계산
- 통화별 환산 금액 반환
1-2. Should Have
1) 환율 스냅샷 저장 (히스토리)
- 개발 포인트
- “하루 1회” 기준으로 단순화
- 세부 기능
- 일자별 환율 스냅샷 저장
- 통화별 환율 조회 (날짜 기준)
- 스냅샷 중복 저장 방지
- 데이터
- ExchangeRateSnapshot
- 기준일
- 통화
- 환율
2) 환율 변동에 따른 손익(P/L) 추적
- 개발 포인트
- 실현 손익 ❌
- 평가 손익(valuation P/L) 만 계산
- 세부 기능
- 기준일 대비 현재 자산 가치 차이 계산
- 통화별 P/L
- 전체 P/L 합계
- 증감률(%) 계산
1-3. Could-Have
1) AI 요약 리포트 (LLM API 기반)
- 입력 데이터
- 최근 7일 or 14일 환율 스냅샷
- 통화별 변동률
- 사용자 보유 비중
- 기능 분해
- 환율 변화 요약 : “최근 7일간 USD/KRW는 +1.8% 상승”
- 변동 원인 템플릿 요약 : (고정 문구 + 데이터 삽입) (예: 글로벌 금리, 달러 강세 등)
- 사용자 자산 영향 설명 : “현재 포트폴리오에 긍정적/중립적”
- 예측처럼 보이지만 예측 아님 (오를 것이다가 아닌 변동성이 유지되고 있다 는 정도로 표현)
2. 개발 일정표
- 기간: 2026/01/26(월) ~ 2026/02/23(월) 4주
- 평일: 월·화·수·목 → 2시간
- 주말: 토요일,일요일 → 10시간
주차별 목표
| 주차 | 기간 | 주차 목표 |
|---|---|---|
| 1주차 | 1/26 ~ 1/31 | 기획·설계·환경 세팅 완료 |
| 2주차 | 2/2 ~ 2/7 | Must Have 전부 구현 → 외화 지갑 + 환율 + 자산 환산 |
| 3주차 | 2/9 ~ 2/14 | Should Have 구현 → 환율 히스토리 + 손익(P/L) |
| 4주차 | 2/16 ~ 2/23 | Could Have + 완성도 → AI 요약 리포트 + 포트폴리오 |
1주차 : 기획·설계·환경 세팅 완료
| 날짜 | 요일 | 시간 | 일정 |
|---|---|---|---|
| 1/26 | 월 | 2h | 프로젝트 목표 정의 기능 범위 확정(Must/Should/Could) |
| 1/27 | 화 | 2h | 전체 아키텍처 설계 (레이어 구조 / 책임 분리) |
| 1/28 | 수 | 2h | ERD 설계(핵심 엔티티 도출) |
| 1/29 | 목 | 2h | DB 스키마 확정 인덱스·유니크키 정의 |
| 1/31 | 토 | 10h | 프로젝트 세팅 • Spring Boot 생성 • 패키지 구조/환경 분리 • 공통 모듈(exception, logging) |
2주차 : Must Have 구현
| 날짜 | 요일 | 시간 | 일정 |
|---|---|---|---|
| 2/2 | 월 | 2h | ERD 설계 보완 |
| 2/3 | 화 | 2h | API 설계 |
| 2/4 | 수 | 2h | 유저 api 개발 |
| 2/5 | 목 | 2h | 지갑서비스 api 개발 |
| 2/7 | 토 | 10h | 멘토링 및 보완사항 반영 |
3주차 : Must Have, Should Have 구현
| 날짜 | 요일 | 시간 | 일정 |
|---|---|---|---|
| 2/9 | 월 | 2h | 한국 수출입은행 환율정보 API 연동 및 개발 |
| 2/10 | 화 | 2h | 한국 수출입은행 환율정보 API 연동 및 개발 |
| 2/11 | 수 | 2h | 평가 손익 api 개발 |
| 2/12 | 목 | 2h | kafka , 쿠버네티스 관련 개념이해 및 공부 |
| 2/14 | 토 | 10h | 멘토링 및 보완사항 반영 Kafka 연동 및 경험 |
4주차 : Could Have + 완성도
| 날짜 | 요일 | 시간 | 일정 |
|---|---|---|---|
| 2/16 | 월 | 2h | AI 리포트 데이터 집계 |
| 2/17 | 화 | 2h | AI 요약 프롬프트 설계 |
| 2/18 | 수 | 2h | LLM API 연동 |
| 2/19 | 목 | 2h | AI 요약 리포트 API |
| 2/21 | 토 | 10h | 리팩토링 에러/로그 정리 |
| 2/23 | 월 | 2h | README 완성 데모 시나리오 정리 |
3. 기술스택
- Java 17
- Spring Boot
- Spring Data JPA (Hibernate)
- MySQL 8
- Flyway (DB 마이그레이션)
- Validation (Jakarta Validation) : 금액>0, 통화코드, 날짜 범위 같은 입력 검증을 어노테이션으로 깔끔하게 처리.
- OpenAPI(Swagger) : API 명세 자동화
- JUnit5 + Spring Boot Test : P/L 계산, 출금 잔액 검증 같은 핵심 로직
- Docker + Compose
- Kafka
4. ERD 구조
users
- 서비스 사용자 기본 정보 테이블
- 멀티유저 구조를 위한 루트 엔티티로, 지갑·리포트 등 모든 사용자 소유 데이터의 기준이 됨
- MVP 단계에서는 단일 사용자만 있어도 확장에 문제없음
| 컬럼명 | 타입 | NULL | Key | 설명 | 예시 | 검증 규칙 |
|---|---|---|---|---|---|---|
| id | BIGINT UNSIGNED | N | PK | 사용자 식별자 | 1 | 자동 증가, 양수 |
| VARCHAR(255) | N | UQ | 로그인/식별용 이메일 | user@domain.com |
유니크, 공백 불가 | |
| name | VARCHAR(100) | Y | 사용자 이름(표시용) | 홍길동 |
길이 제한 | |
| created_date | DATETIME | N | 최초등록 일시 | 2026-01-26 21:10:00 |
자동 세팅 | |
| updated_date | DATETIME | N | 수정 일시 | 2026-01-27 09:10:00 |
업데이트 자동 |
wallets
- 사용자별 통화 지갑(통화별 잔액) 정보를 저장
- 사용자당 통화코드(USD, JPY 등) 기준으로 지갑은 1개만 생성 가능
- 현재 잔액을 빠르게 조회하기 위한 잔액 스냅샷 역할을 겸함
| 컬럼명 | 타입 | NULL | Key | 설명 | 예시 | 검증 규칙 |
|---|---|---|---|---|---|---|
| id | BIGINT UNSIGNED | N | PK | 지갑 식별자 | 10 | 자동 증가 |
| user_id | BIGINT UNSIGNED | N | FK | 소유 사용자 id | 1 | 존재하는 users.id |
| currency_code | CHAR(3) | N | UQ | 통화 코드 | USD |
ISO 4217, 3자리 대문자 |
| balance | DECIMAL(18,6) | N | 현재 잔액 | 1000.000000 |
0 이상 권장, 음수 허용 여부 정책 필요 | |
| created_date | DATETIME | N | 최초등록 일시 | 2026-01-28 21:00:00 |
자동 | |
| updated_date | DATETIME | N | 수정 일시 | 2026-01-29 21:00:00 |
자동 |
wallet 외화지갑 거래 구조
- wallet_events: “사용자가 한 번 실행한 행위(입금/출금/환전)” = 이력 1건(헤더)
- wallet_transactions: 그 행위 때문에 실제로 각 지갑에 찍히는 “원장 라인” = 상세 이력 1~N건
- 입금: 지갑 1개만 변함 → transactions 1건
- 출금: 지갑 1개만 변함 → transactions 1건
- 환전: 지갑 2개가 동시에 변함 → transactions 2건 (from 통화 지갑 DEBIT 1건 + to 통화 지갑 CREDIT 1건)
wallet_events
- 지갑 변동의 “행위/사건”을 기록하는 테이블
- 환전, 입금, 출금 등의 행위를 업무단위 한 건으로 표현하기 위함
| 컬럼명 | 타입 | NULL | Key | 설명 | 예시 | 검증/제약 |
|---|---|---|---|---|---|---|
| id | BIGINT UNSIGNED | N | PK | 이벤트 ID | 5001 | AUTO_INCREMENT |
| user_id | BIGINT UNSIGNED | N | FK/IDX | 사용자 ID | 1 | users.id 존재 |
| type | ENUM(‘DEPOSIT’,’WITHDRAW’,’EXCHANGE’) | N | IDX | 이벤트 유형 | EXCHANGE | enum 제한 |
| external_source | VARCHAR(50) | Y | 외부 출처(은행/수동 등) | BANK | 길이 제한 | |
| memo | VARCHAR(255) | Y | 이벤트 메모(요약) | KRW→USD 환전 | 길이 제한 | |
| idempotency_key | VARCHAR(64) | Y | UQ | 중복요청 방지 키(권장) | req_20260207_001 | 유니크 |
| occurred_date | DATETIME | N | IDX | 발생 시각 | 2026-02-07 12:00:00 | 필수 |
| created_date | DATETIME | N | 생성 시각 | 2026-02-07 12:00:01 | DEFAULT | |
| updated_date | DATETIME | N | 수정 시각 | 2026-02-07 12:00:01 | ON UPDATE |
wallet_transactions
- 외화 지갑의 입금/출금/환전 상세 내역을 기록하는 원장 테이블
- 모든 잔액 변경의 근거 데이터로, 추적·검증·감사에 핵심적인 역할
- 각 거래마다
balance_after를 저장해 거래 후 잔액 상태를 명확히 유지
| 컬럼명 | 타입 | NULL | Key | 설명 | 예시 | 검증/제약 |
|---|---|---|---|---|---|---|
| id | BIGINT UNSIGNED | N | PK | 거래(라인) ID | 90001 | AUTO_INCREMENT |
| event_id | BIGINT UNSIGNED | N | FK/IDX | 소속 이벤트 ID | 5001 | wallet_events.id 존재 |
| wallet_id | BIGINT UNSIGNED | N | FK/IDX | 대상 지갑 ID | 10 | wallets.id 존재 |
| direction | ENUM(‘CREDIT’,’DEBIT’) | N | 증감 방향 | DEBIT | enum 제한 | |
| amount | DECIMAL(18,6) | N | 증감 금액 | 200.000000 | > 0 | |
| balance_after | DECIMAL(18,6) | N | 거래 후 잔액 | 1200.000000 | 계산 결과와 일치 | |
| created_date | DATETIME | N | IDX | 생성 시각 | 2026-02-07 12:00:01 | DEFAULT |
| updated_date | DATETIME | N | 수정 시각 | 2026-02-07 12:00:01 | ON UPDATE |
exchange_rate_snapshots
- 환율을 하루 단위로 저장하기 위한 환율 스냅샷 메타 정보(헤더)
- 기준일, 기준통화, 환율 타입 조합으로 스냅샷을 구분하고 중복 저장을 방지
- 외부 환율 API를 실제로 조회한 시각을 기록해 데이터 신뢰도를 관리
| 컬럼명 | 타입 | NULL | Key | 설명 | 예시 | 검증 규칙 |
|---|---|---|---|---|---|---|
| id | BIGINT UNSIGNED | N | PK | 스냅샷 id | 1 | 자동 증가 |
| base_date | DATE | N | UQ | 기준일 | 2026-02-10 |
날짜 필수 |
| base_currency | ENUM(‘KRW’,’USD’) | N | UQ | 기준 통화 | KRW |
KRW/USD만 허용 |
| rate_type | ENUM(‘BASE’,’TT_BUY’,’TT_SELL’) | N | UQ | 환율 타입 | BASE |
MVP는 BASE만 사용 권장 |
| source | VARCHAR(100) | N | 제공처 | KEXIM |
기본값 | |
| created_date | DATETIME | N | 최초등록 일시 | 2026-02-10 09:01:02 |
자동 |
exchange_rate_snapshots_detail
- 특정 환율 스냅샷에 포함된 통화별 환율 상세 데이터
- 예: 기준통화 KRW 기준으로 USD, JPY, EUR 등의 환율 값 저장
- 환율 히스토리 조회 및 평가 손익(P/L) 계산의 핵심 데이터 소스
| 컬럼명 | 타입 | NULL | Key | 설명 | 예시 | 검증 규칙 |
|---|---|---|---|---|---|---|
| id | BIGINT UNSIGNED | N | PK | 아이템 id | 10 | 자동 증가 |
| snapshot_id | BIGINT UNSIGNED | N | FK/UQ | 스냅샷 id | 1 | 존재하는 snapshots.id |
| quote_currency | CHAR(3) | N | UQ | 대상 통화 | USD |
ISO 4217 3자리 |
| rate | DECIMAL(18,6) | N | 환율 | 1300.120000 |
0 초과 | |
| created_date | DATETIME | N | 최초등록 일시 | 2026-01-28 21:00:00 |
자동 |
ai_reports (Optional)
- AI가 생성한 환율 변동 요약 리포트를 저장하는 테이블
- 최근 N일간 환율 변화와 사용자 자산 영향에 대한 설명을 기록
- 계산된 변동률·지표를 함께 저장해 리포트 재사용 및 비교가 가능
| 컬럼명 | 타입 | NULL | Key | 설명 | 예시 | 검증 규칙 |
|---|---|---|---|---|---|---|
| id | BIGINT UNSIGNED | N | PK | 리포트 id | 1 | 자동 증가 |
| user_id | BIGINT UNSIGNED | N | FK | 사용자 id | 1 | users.id 존재 |
| base_currency | ENUM(‘KRW’,’USD’) | N | 기준 통화 | KRW |
제한 enum | |
| window_days | INT | N | 분석 구간(일) | 7 | 1~31 권장 | |
| from_date | DATE | N | IDX | 시작일 | 2026-02-01 |
to_date 이하 |
| to_date | DATE | N | IDX | 종료일 | 2026-02-07 |
from_date 이상 |
| content_md | LONGTEXT | N | 요약 본문(Markdown) | ### 요약 ... |
공백 불가 | |
| metrics_json | JSON | Y | 계산 지표(변동률 등) | { "usd_change": 1.2 } |
JSON 유효 | |
| model_name | VARCHAR(100) | Y | 사용 모델 | gpt-4.1-mini |
선택 | |
| created_date | DATETIME | N | 최초등록 일시 | 2026-02-17 22:10:01 |
자동 |
ERD

5. API 구조
services하위에 Must Have, Should Have , Could Have 등 기능이되는 api 를 나누어 개발libs/common-web/: 공통 예외처리를 라이브러리화 하여 services에서 사용libs/common-event/:- `libs/common-kafka/ :
my_wallet/
├─ settings.gradle
├─ build.gradle
├─ docker-compose.yml
│
├─ services/ # 실행 가능한 Spring Boot 애플리케이션
│ │
│ ├─ user-service/
│ │ ├─ build.gradle
│ │ └─ src/main/java/com/mywallet/user
│ │ ├─ UserServiceApplication.java
│ │ ├─ controller/ # HTTP API 진입점, service 호출
│ │ ├─ service/ # 유스케이스/비즈니스 로직 계층
│ │ ├─ domain/ # 도메인 모델(엔티티) - DB 테이블(users)과 매핑되는 JPA Entity
│ │ ├─ dto/ # API 전용 데이터 구조 (Request-입력, Response-응답)
│ │ ├─ exception/ # 서비스 전용 에러 코드만 보관 - 실제 예외 처리기(Handler)는 common-web에 있음
│ │ └─ repository/ # 데이터 접근 - JPA를 통한 CRUD
│ │
│ ├─ wallet-service/
│ │ ├─ build.gradle
│ │ └─ src/main/java/com/mywallet/wallet
│ │ ├─ WalletServiceApplication.java
│ │ ├─ controller/ # REST API
│ │ ├─ application/ # 유스케이스 (Command 중심)
│ │ ├─ domain/ # Wallet, WalletEvent, WalletTransaction
│ │ ├─ repository/
│ │ ├─ messaging/
│ │ │ ├─ outbox/ # Outbox 엔티티/퍼블리셔
│ │ │ └─ producer/ # Kafka 이벤트 발행
│ │ └─ config/
│ │
│ ├─ exchange-rate-service/
│ │ ├─ build.gradle
│ │ └─ src/main/java/com/mywallet/exchange
│ │ ├─ ExchangeRateServiceApplication.java
│ │ ├─ controller/
│ │ ├─ application/
│ │ ├─ domain/ # Snapshot, SnapshotDetail
│ │ ├─ repository/
│ │ ├─ external/ # KEXIM client
│ │ ├─ messaging/
│ │ │ └─ producer/
│ │ └─ config/
│ │
│ ├─ valuation-service/
│ │ ├─ build.gradle
│ │ └─ src/main/java/com/mywallet/valuation
│ │ ├─ ValuationServiceApplication.java
│ │ ├─ controller/
│ │ ├─ application/
│ │ ├─ domain/ # Portfolio, ValuationResult
│ │ ├─ repository/
│ │ ├─ messaging/
│ │ │ └─ consumer/ # wallet / snapshot 이벤트 구독
│ │ └─ config/
│ │
│ └─ report-service/
│ ├─ build.gradle
│ └─ src/main/java/com/mywallet/report
│ ├─ ReportServiceApplication.java
│ ├─ controller/
│ ├─ application/
│ ├─ domain/ # AiReport
│ ├─ repository/
│ ├─ messaging/
│ │ └─ consumer/
│ └─ config/
│
├─ libs/ # 공통 라이브러리
│ │
│ ├─ common-web/
│ │ ├─ build.gradle
│ │ └─ src/main/java/com/mywallet/common/web
│ │ ├─ exception/ # GlobalException, ErrorCode
│ │ ├─ response/ # ApiResponse<T>
│ │ └─ validation/ # @PositiveAmount, @CurrencyCode
│ │
│ ├─ common-event/ # ⭐ Kafka/HTTP 공통 "이벤트 계약"
│ │ ├─ build.gradle
│ │ └─ src/main/java/com/mywallet/common/event
│ │ ├─ wallet/
│ │ │ └─ WalletEventCreated.java
│ │ ├─ exchange/
│ │ │ └─ ExchangeRateSnapshotCreated.java
│ │ └─ BaseEvent.java # eventId, occurredAt, version
│ │
│ └─ common-kafka/
│ ├─ build.gradle
│ └─ src/main/java/com/mywallet/common/kafka
│ ├─ config/ # Producer/Consumer 공통 설정
│ ├─ serializer/
│ └─ producer/ # KafkaTemplate 헬퍼
│
├─ infra/
│ └─ k8s/ # ⭐ Kubernetes 매니페스트
│ ├─ base/
│ │ ├─ namespace.yaml
│ │ ├─ configmap.yaml
│ │ └─ secret.yaml
│ │
│ ├─ wallet-service/
│ │ ├─ deployment.yaml
│ │ ├─ service.yaml
│ │ └─ hpa.yaml
│ │
│ ├─ exchange-rate-service/
│ │ ├─ deployment.yaml
│ │ ├─ service.yaml
│ │ └─ cronjob-snapshot.yaml # 하루 1회 환율 스냅샷
│ │
│ ├─ valuation-service/
│ │ ├─ deployment.yaml
│ │ └─ service.yaml
│ │
│ └─ report-service/
│ ├─ deployment.yaml
│ └─ service.yaml
│
└─ README.md
외화 자산 관리 시스템
1. 기능정의
1-1. Must Have (핵심 MVP)
1) 사용자별 외화 지갑 (통화별 잔액 관리)
- 개발 포인트
- 사용자 계정 1명 기준 (멀티 유저 구조는 설계만)
- 통화는 ISO 코드 기반 (USD, JPY, EUR 등)
- 세부 기능
- 통화 지갑 생성
- 환전 기능
- 통화별 잔액 조회
- 잔액 증감 처리
- 수동 입금 / 출금
- 거래 사유 메모
- 잔액 검증
- 출금 시 잔액 부족 방지
- 데이터
- Wallet
- WalletTransaction
2) 한국수출입은행 환율정보 외부 API 연동
- 개발 포인트
- 외부 API → 내부 DTO 변환
- 캐싱 필수 (API 남용 방지)
- 세부 기능
- 오늘 기준 환율 조회
- 통화별 매매기준율 저장
- API 실패 시 예외 처리
- 최근 스냅샷 있으면 fallback
3) 기준 통화(KRW/USD) 자산 환산
- 개발 포인트
- 계산 로직 정확성 + 소수점 처리
- 세부 기능
- 기준통화 선택 (KRW / USD)
- 전체 자산 합계 계산
- 통화별 환산 금액 반환
1-2. Should Have
1) 환율 스냅샷 저장 (히스토리)
- 개발 포인트
- “하루 1회” 기준으로 단순화
- 세부 기능
- 일자별 환율 스냅샷 저장
- 통화별 환율 조회 (날짜 기준)
- 스냅샷 중복 저장 방지
- 데이터
- ExchangeRateSnapshot
- 기준일
- 통화
- 환율
2) 환율 변동에 따른 손익(P/L) 추적
- 개발 포인트
- 실현 손익 ❌
- 평가 손익(valuation P/L) 만 계산
- 세부 기능
- 기준일 대비 현재 자산 가치 차이 계산
- 통화별 P/L
- 전체 P/L 합계
- 증감률(%) 계산
1-3. Could-Have
1) AI 요약 리포트 (LLM API 기반)
- 입력 데이터
- 최근 7일 or 14일 환율 스냅샷
- 통화별 변동률
- 사용자 보유 비중
- 기능 분해
- 환율 변화 요약 : “최근 7일간 USD/KRW는 +1.8% 상승”
- 변동 원인 템플릿 요약 : (고정 문구 + 데이터 삽입) (예: 글로벌 금리, 달러 강세 등)
- 사용자 자산 영향 설명 : “현재 포트폴리오에 긍정적/중립적”
- 예측처럼 보이지만 예측 아님 (오를 것이다가 아닌 변동성이 유지되고 있다 는 정도로 표현)
2. 개발 일정표
- 기간: 2026/01/26(월) ~ 2026/02/23(월) 4주
- 평일: 월·화·수·목 → 2시간
- 주말: 토요일,일요일 → 10시간
주차별 목표
| 주차 | 기간 | 주차 목표 |
|---|---|---|
| 1주차 | 1/26 ~ 1/31 | 기획·설계·환경 세팅 완료 |
| 2주차 | 2/2 ~ 2/7 | Must Have 전부 구현 → 외화 지갑 + 환율 + 자산 환산 |
| 3주차 | 2/9 ~ 2/14 | Should Have 구현 → 환율 히스토리 + 손익(P/L) |
| 4주차 | 2/16 ~ 2/23 | Could Have + 완성도 → AI 요약 리포트 + 포트폴리오 |
1주차 : 기획·설계·환경 세팅 완료
| 날짜 | 요일 | 시간 | 일정 |
|---|---|---|---|
| 1/26 | 월 | 2h | 프로젝트 목표 정의 기능 범위 확정(Must/Should/Could) |
| 1/27 | 화 | 2h | 전체 아키텍처 설계 (레이어 구조 / 책임 분리) |
| 1/28 | 수 | 2h | ERD 설계(핵심 엔티티 도출) |
| 1/29 | 목 | 2h | DB 스키마 확정 인덱스·유니크키 정의 |
| 1/31 | 토 | 10h | 프로젝트 세팅 • Spring Boot 생성 • 패키지 구조/환경 분리 • 공통 모듈(exception, logging) |
2주차 : Must Have 구현
| 날짜 | 요일 | 시간 | 일정 |
|---|---|---|---|
| 2/2 | 월 | 2h | ERD 설계 보완 |
| 2/3 | 화 | 2h | API 설계 |
| 2/4 | 수 | 2h | 유저 api 개발 |
| 2/5 | 목 | 2h | 지갑서비스 api 개발 |
| 2/7 | 토 | 10h | 멘토링 및 보완사항 반영 |
3주차 : Must Have, Should Have 구현
| 날짜 | 요일 | 시간 | 일정 |
|---|---|---|---|
| 2/9 | 월 | 2h | 한국 수출입은행 환율정보 API 연동 및 개발 |
| 2/10 | 화 | 2h | 한국 수출입은행 환율정보 API 연동 및 개발 |
| 2/11 | 수 | 2h | 평가 손익 api 개발 |
| 2/12 | 목 | 2h | kafka , 쿠버네티스 관련 개념이해 및 공부 |
| 2/14 | 토 | 10h | 멘토링 및 보완사항 반영 Kafka 연동 및 경험 |
4주차 : Could Have + 완성도
| 날짜 | 요일 | 시간 | 일정 |
|---|---|---|---|
| 2/16 | 월 | 2h | AI 리포트 데이터 집계 |
| 2/17 | 화 | 2h | AI 요약 프롬프트 설계 |
| 2/18 | 수 | 2h | LLM API 연동 |
| 2/19 | 목 | 2h | AI 요약 리포트 API |
| 2/21 | 토 | 10h | 리팩토링 에러/로그 정리 |
| 2/23 | 월 | 2h | README 완성 데모 시나리오 정리 |
3. 기술스택
- Java 17
- Spring Boot
- Spring Data JPA (Hibernate)
- MySQL 8
- Flyway (DB 마이그레이션)
- Validation (Jakarta Validation) : 금액>0, 통화코드, 날짜 범위 같은 입력 검증을 어노테이션으로 깔끔하게 처리.
- OpenAPI(Swagger) : API 명세 자동화
- JUnit5 + Spring Boot Test : P/L 계산, 출금 잔액 검증 같은 핵심 로직
- Docker + Compose
- Kafka
4. ERD 구조
users
- 서비스 사용자 기본 정보 테이블
- 멀티유저 구조를 위한 루트 엔티티로, 지갑·리포트 등 모든 사용자 소유 데이터의 기준이 됨
- MVP 단계에서는 단일 사용자만 있어도 확장에 문제없음
| 컬럼명 | 타입 | NULL | Key | 설명 | 예시 | 검증 규칙 |
|---|---|---|---|---|---|---|
| id | BIGINT UNSIGNED | N | PK | 사용자 식별자 | 1 | 자동 증가, 양수 |
| VARCHAR(255) | N | UQ | 로그인/식별용 이메일 | user@domain.com |
유니크, 공백 불가 | |
| name | VARCHAR(100) | Y | 사용자 이름(표시용) | 홍길동 |
길이 제한 | |
| created_date | DATETIME | N | 최초등록 일시 | 2026-01-26 21:10:00 |
자동 세팅 | |
| updated_date | DATETIME | N | 수정 일시 | 2026-01-27 09:10:00 |
업데이트 자동 |
wallets
- 사용자별 통화 지갑(통화별 잔액) 정보를 저장
- 사용자당 통화코드(USD, JPY 등) 기준으로 지갑은 1개만 생성 가능
- 현재 잔액을 빠르게 조회하기 위한 잔액 스냅샷 역할을 겸함
| 컬럼명 | 타입 | NULL | Key | 설명 | 예시 | 검증 규칙 |
|---|---|---|---|---|---|---|
| id | BIGINT UNSIGNED | N | PK | 지갑 식별자 | 10 | 자동 증가 |
| user_id | BIGINT UNSIGNED | N | FK | 소유 사용자 id | 1 | 존재하는 users.id |
| currency_code | CHAR(3) | N | UQ | 통화 코드 | USD |
ISO 4217, 3자리 대문자 |
| balance | DECIMAL(18,6) | N | 현재 잔액 | 1000.000000 |
0 이상 권장, 음수 허용 여부 정책 필요 | |
| created_date | DATETIME | N | 최초등록 일시 | 2026-01-28 21:00:00 |
자동 | |
| updated_date | DATETIME | N | 수정 일시 | 2026-01-29 21:00:00 |
자동 |
wallet 외화지갑 거래 구조
- wallet_events: “사용자가 한 번 실행한 행위(입금/출금/환전)” = 이력 1건(헤더)
- wallet_transactions: 그 행위 때문에 실제로 각 지갑에 찍히는 “원장 라인” = 상세 이력 1~N건
- 입금: 지갑 1개만 변함 → transactions 1건
- 출금: 지갑 1개만 변함 → transactions 1건
- 환전: 지갑 2개가 동시에 변함 → transactions 2건 (from 통화 지갑 DEBIT 1건 + to 통화 지갑 CREDIT 1건)
wallet_events
- 지갑 변동의 “행위/사건”을 기록하는 테이블
- 환전, 입금, 출금 등의 행위를 업무단위 한 건으로 표현하기 위함
| 컬럼명 | 타입 | NULL | Key | 설명 | 예시 | 검증/제약 |
|---|---|---|---|---|---|---|
| id | BIGINT UNSIGNED | N | PK | 이벤트 ID | 5001 | AUTO_INCREMENT |
| user_id | BIGINT UNSIGNED | N | FK/IDX | 사용자 ID | 1 | users.id 존재 |
| type | ENUM(‘DEPOSIT’,’WITHDRAW’,’EXCHANGE’) | N | IDX | 이벤트 유형 | EXCHANGE | enum 제한 |
| external_source | VARCHAR(50) | Y | 외부 출처(은행/수동 등) | BANK | 길이 제한 | |
| memo | VARCHAR(255) | Y | 이벤트 메모(요약) | KRW→USD 환전 | 길이 제한 | |
| idempotency_key | VARCHAR(64) | Y | UQ | 중복요청 방지 키(권장) | req_20260207_001 | 유니크 |
| occurred_date | DATETIME | N | IDX | 발생 시각 | 2026-02-07 12:00:00 | 필수 |
| created_date | DATETIME | N | 생성 시각 | 2026-02-07 12:00:01 | DEFAULT | |
| updated_date | DATETIME | N | 수정 시각 | 2026-02-07 12:00:01 | ON UPDATE |
wallet_transactions
- 외화 지갑의 입금/출금/환전 상세 내역을 기록하는 원장 테이블
- 모든 잔액 변경의 근거 데이터로, 추적·검증·감사에 핵심적인 역할
- 각 거래마다
balance_after를 저장해 거래 후 잔액 상태를 명확히 유지
| 컬럼명 | 타입 | NULL | Key | 설명 | 예시 | 검증/제약 |
|---|---|---|---|---|---|---|
| id | BIGINT UNSIGNED | N | PK | 거래(라인) ID | 90001 | AUTO_INCREMENT |
| event_id | BIGINT UNSIGNED | N | FK/IDX | 소속 이벤트 ID | 5001 | wallet_events.id 존재 |
| wallet_id | BIGINT UNSIGNED | N | FK/IDX | 대상 지갑 ID | 10 | wallets.id 존재 |
| direction | ENUM(‘CREDIT’,’DEBIT’) | N | 증감 방향 | DEBIT | enum 제한 | |
| amount | DECIMAL(18,6) | N | 증감 금액 | 200.000000 | > 0 | |
| balance_after | DECIMAL(18,6) | N | 거래 후 잔액 | 1200.000000 | 계산 결과와 일치 | |
| created_date | DATETIME | N | IDX | 생성 시각 | 2026-02-07 12:00:01 | DEFAULT |
| updated_date | DATETIME | N | 수정 시각 | 2026-02-07 12:00:01 | ON UPDATE |
exchange_rate_snapshots
- 환율을 하루 단위로 저장하기 위한 환율 스냅샷 메타 정보(헤더)
- 기준일, 기준통화, 환율 타입 조합으로 스냅샷을 구분하고 중복 저장을 방지
- 외부 환율 API를 실제로 조회한 시각을 기록해 데이터 신뢰도를 관리
| 컬럼명 | 타입 | NULL | Key | 설명 | 예시 | 검증 규칙 |
|---|---|---|---|---|---|---|
| id | BIGINT UNSIGNED | N | PK | 스냅샷 id | 1 | 자동 증가 |
| base_date | DATE | N | UQ | 기준일 | 2026-02-10 |
날짜 필수 |
| base_currency | ENUM(‘KRW’,’USD’) | N | UQ | 기준 통화 | KRW |
KRW/USD만 허용 |
| rate_type | ENUM(‘BASE’,’TT_BUY’,’TT_SELL’) | N | UQ | 환율 타입 | BASE |
MVP는 BASE만 사용 권장 |
| source | VARCHAR(100) | N | 제공처 | KEXIM |
기본값 | |
| created_date | DATETIME | N | 최초등록 일시 | 2026-02-10 09:01:02 |
자동 |
exchange_rate_snapshots_detail
- 특정 환율 스냅샷에 포함된 통화별 환율 상세 데이터
- 예: 기준통화 KRW 기준으로 USD, JPY, EUR 등의 환율 값 저장
- 환율 히스토리 조회 및 평가 손익(P/L) 계산의 핵심 데이터 소스
| 컬럼명 | 타입 | NULL | Key | 설명 | 예시 | 검증 규칙 |
|---|---|---|---|---|---|---|
| id | BIGINT UNSIGNED | N | PK | 아이템 id | 10 | 자동 증가 |
| snapshot_id | BIGINT UNSIGNED | N | FK/UQ | 스냅샷 id | 1 | 존재하는 snapshots.id |
| quote_currency | CHAR(3) | N | UQ | 대상 통화 | USD |
ISO 4217 3자리 |
| rate | DECIMAL(18,6) | N | 환율 | 1300.120000 |
0 초과 | |
| created_date | DATETIME | N | 최초등록 일시 | 2026-01-28 21:00:00 |
자동 |
ai_reports (Optional)
- AI가 생성한 환율 변동 요약 리포트를 저장하는 테이블
- 최근 N일간 환율 변화와 사용자 자산 영향에 대한 설명을 기록
- 계산된 변동률·지표를 함께 저장해 리포트 재사용 및 비교가 가능
| 컬럼명 | 타입 | NULL | Key | 설명 | 예시 | 검증 규칙 |
|---|---|---|---|---|---|---|
| id | BIGINT UNSIGNED | N | PK | 리포트 id | 1 | 자동 증가 |
| user_id | BIGINT UNSIGNED | N | FK | 사용자 id | 1 | users.id 존재 |
| base_currency | ENUM(‘KRW’,’USD’) | N | 기준 통화 | KRW |
제한 enum | |
| window_days | INT | N | 분석 구간(일) | 7 | 1~31 권장 | |
| from_date | DATE | N | IDX | 시작일 | 2026-02-01 |
to_date 이하 |
| to_date | DATE | N | IDX | 종료일 | 2026-02-07 |
from_date 이상 |
| content_md | LONGTEXT | N | 요약 본문(Markdown) | ### 요약 ... |
공백 불가 | |
| metrics_json | JSON | Y | 계산 지표(변동률 등) | { "usd_change": 1.2 } |
JSON 유효 | |
| model_name | VARCHAR(100) | Y | 사용 모델 | gpt-4.1-mini |
선택 | |
| created_date | DATETIME | N | 최초등록 일시 | 2026-02-17 22:10:01 |
자동 |
ERD

5. API 구조
services하위에 Must Have, Should Have , Could Have 등 기능이되는 api 를 나누어 개발libs/common-web/: 공통 예외처리를 라이브러리화 하여 services에서 사용libs/common-event/:- `libs/common-kafka/ :
my_wallet/
├─ settings.gradle
├─ build.gradle
├─ docker-compose.yml
│
├─ services/ # 실행 가능한 Spring Boot 애플리케이션
│ │
│ ├─ user-service/
│ │ ├─ build.gradle
│ │ └─ src/main/java/com/mywallet/user
│ │ ├─ UserServiceApplication.java
│ │ ├─ controller/ # HTTP API 진입점, service 호출
│ │ ├─ service/ # 유스케이스/비즈니스 로직 계층
│ │ ├─ domain/ # 도메인 모델(엔티티) - DB 테이블(users)과 매핑되는 JPA Entity
│ │ ├─ dto/ # API 전용 데이터 구조 (Request-입력, Response-응답)
│ │ ├─ exception/ # 서비스 전용 에러 코드만 보관 - 실제 예외 처리기(Handler)는 common-web에 있음
│ │ └─ repository/ # 데이터 접근 - JPA를 통한 CRUD
│ │
│ ├─ wallet-service/
│ │ ├─ build.gradle
│ │ └─ src/main/java/com/mywallet/wallet
│ │ ├─ WalletServiceApplication.java
│ │ ├─ controller/ # REST API
│ │ ├─ application/ # 유스케이스 (Command 중심)
│ │ ├─ domain/ # Wallet, WalletEvent, WalletTransaction
│ │ ├─ repository/
│ │ ├─ messaging/
│ │ │ ├─ outbox/ # Outbox 엔티티/퍼블리셔
│ │ │ └─ producer/ # Kafka 이벤트 발행
│ │ └─ config/
│ │
│ ├─ exchange-rate-service/
│ │ ├─ build.gradle
│ │ └─ src/main/java/com/mywallet/exchange
│ │ ├─ ExchangeRateServiceApplication.java
│ │ ├─ controller/
│ │ ├─ application/
│ │ ├─ domain/ # Snapshot, SnapshotDetail
│ │ ├─ repository/
│ │ ├─ external/ # KEXIM client
│ │ ├─ messaging/
│ │ │ └─ producer/
│ │ └─ config/
│ │
│ ├─ valuation-service/
│ │ ├─ build.gradle
│ │ └─ src/main/java/com/mywallet/valuation
│ │ ├─ ValuationServiceApplication.java
│ │ ├─ controller/
│ │ ├─ application/
│ │ ├─ domain/ # Portfolio, ValuationResult
│ │ ├─ repository/
│ │ ├─ messaging/
│ │ │ └─ consumer/ # wallet / snapshot 이벤트 구독
│ │ └─ config/
│ │
│ └─ report-service/
│ ├─ build.gradle
│ └─ src/main/java/com/mywallet/report
│ ├─ ReportServiceApplication.java
│ ├─ controller/
│ ├─ application/
│ ├─ domain/ # AiReport
│ ├─ repository/
│ ├─ messaging/
│ │ └─ consumer/
│ └─ config/
│
├─ libs/ # 공통 라이브러리
│ │
│ ├─ common-web/
│ │ ├─ build.gradle
│ │ └─ src/main/java/com/mywallet/common/web
│ │ ├─ exception/ # GlobalException, ErrorCode
│ │ ├─ response/ # ApiResponse<T>
│ │ └─ validation/ # @PositiveAmount, @CurrencyCode
│ │
│ ├─ common-event/ # ⭐ Kafka/HTTP 공통 "이벤트 계약"
│ │ ├─ build.gradle
│ │ └─ src/main/java/com/mywallet/common/event
│ │ ├─ wallet/
│ │ │ └─ WalletEventCreated.java
│ │ ├─ exchange/
│ │ │ └─ ExchangeRateSnapshotCreated.java
│ │ └─ BaseEvent.java # eventId, occurredAt, version
│ │
│ └─ common-kafka/
│ ├─ build.gradle
│ └─ src/main/java/com/mywallet/common/kafka
│ ├─ config/ # Producer/Consumer 공통 설정
│ ├─ serializer/
│ └─ producer/ # KafkaTemplate 헬퍼
│
├─ infra/
│ └─ k8s/ # ⭐ Kubernetes 매니페스트
│ ├─ base/
│ │ ├─ namespace.yaml
│ │ ├─ configmap.yaml
│ │ └─ secret.yaml
│ │
│ ├─ wallet-service/
│ │ ├─ deployment.yaml
│ │ ├─ service.yaml
│ │ └─ hpa.yaml
│ │
│ ├─ exchange-rate-service/
│ │ ├─ deployment.yaml
│ │ ├─ service.yaml
│ │ └─ cronjob-snapshot.yaml # 하루 1회 환율 스냅샷
│ │
│ ├─ valuation-service/
│ │ ├─ deployment.yaml
│ │ └─ service.yaml
│ │
│ └─ report-service/
│ ├─ deployment.yaml
│ └─ service.yaml
│
└─ README.md