Skip to content

jun-bank/.github

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Jun Bank

MSA 환경에서 은행 도메인의 트랜잭션 관리와 데이터 일관성을 학습하기 위한 프로젝트입니다.


왜 이 프로젝트를 시작했나?

결제 시스템을 개발할 때마다 실제 은행 API나 PG사 연동이 필요합니다.
테스트를 위해 매번 복잡한 계약 절차를 거치거나, 샌드박스 환경의 제약에 부딪히게 됩니다.

이 프로젝트는 가상의 은행 시스템을 직접 구축하여:

  • 결제 시스템 개발 시 자유롭게 테스트할 수 있는 환경 제공
  • 은행 도메인의 인프라 구조 이해
  • MSA 환경에서 트랜잭션 관리 방법 학습
  • 데이터 일관성동시성 제어 문제 해결 경험

기술 스택

구분 기술 버전
Language Java 21
Framework Spring Boot 4.0.0
Cloud Spring Cloud 2025.1.0
Database PostgreSQL + pgvector 17+
Messaging Apache Kafka (KRaft) 3.8.0
Query QueryDSL 5.1.0 jakarta
Container Docker, Docker Compose -
Monitoring Prometheus, Grafana, Zipkin -

아키텍처

전체 구조

                           ┌─────────────────┐
                           │     Nginx       │
                           │    (L7 LB)      │
                           └────────┬────────┘
                                    │
                    ┌───────────────┴───────────────┐
                    ▼                               ▼
            ┌───────────────┐               ┌───────────────┐
            │ Gateway Server│               │ Gateway Server│
            │   -1 :8080    │               │   -2 :8089    │
            └───────┬───────┘               └───────┬───────┘
                    │                               │
                    └───────────────┬───────────────┘
                                    │
        ┌───────────────────────────┼───────────────────────────┐
        │                           │                           │
        ▼                           ▼                           ▼
┌───────────────┐           ┌───────────────┐           ┌───────────────┐
│ User Service  │           │ Auth Server   │           │Account Service│
│    :8087      │           │    :8086      │           │    :8081      │
└───────────────┘           └───────────────┘           └───────────────┘
        │                           │                           │
        ▼                           ▼                           ▼
┌───────────────┐           ┌───────────────┐           ┌───────────────┐
│Transaction Svc│           │Transfer Service│          │ Card Service  │
│    :8082      │           │    :8083      │           │    :8084      │
└───────────────┘           └───────────────┘           └───────────────┘
        │                           │                           │
        └───────────────────────────┼───────────────────────────┘
                                    │
                                    ▼
                            ┌───────────────┐
                            │Ledger Service │
                            │    :8085      │
                            └───────────────┘

인프라 서버

┌─────────────────┐     ┌─────────────────┐
│ Eureka Server-1 │◄───►│ Eureka Server-2 │
│     :8761       │     │     :8762       │
└─────────────────┘     └─────────────────┘
         ▲
         │
┌─────────────────┐
│  Config Server  │
│     :8888       │
└─────────────────┘

서비스 목록

인프라 서버 (3개)

서비스 포트 설명
eureka-server 8761, 8762 서비스 디스커버리 (이중화)
config-server 8888 중앙 설정 관리
gateway-server 8080, 8089 API Gateway (이중화)

비즈니스 서비스 (7개)

서비스 포트 설명 핵심 학습
user-service 8087 사용자 관리 기본 CRUD, 이벤트 발행
auth-server 8086 인증/인가 JWT, Spring Security
account-service 8081 계좌 관리 낙관적/비관적 락, 동시성
transaction-service 8082 입출금 처리 멱등성 (Idempotency Key)
transfer-service 8083 계좌 이체 SAGA 패턴, Outbox 패턴
card-service 8084 카드 결제 Resilience4j, 한도 관리
ledger-service 8085 원장 기록 불변 데이터, 복식부기

핵심 학습 주제

1. 분산 트랜잭션 (Transfer Service)

단일 DB에서는 ACID가 보장되지만, MSA에서는 서비스마다 DB가 분리됩니다.

  • SAGA 패턴: 보상 트랜잭션을 통한 Eventually Consistent
  • Outbox 패턴: DB 트랜잭션과 이벤트 발행의 원자성 보장

2. 동시성 제어 (Account Service)

같은 계좌에 동시에 여러 거래가 발생하면 잔액이 꼬일 수 있습니다.

  • 낙관적 락: @Version 필드를 통한 충돌 감지
  • 비관적 락: @Lock(PESSIMISTIC_WRITE) DB 레벨 잠금

3. 멱등성 (Transaction Service)

네트워크 장애로 같은 요청이 중복 전송될 수 있습니다.

  • Idempotency Key: X-Idempotency-Key 헤더로 중복 요청 방지

4. 장애 대응 (Card Service)

일부 서비스가 죽어도 전체 시스템은 동작해야 합니다.

  • Circuit Breaker: 장애 전파 차단
  • Retry + Backoff: 일시적 장애 복구
  • Rate Limiter: 과부하 방지

프로젝트 구조

공통 라이브러리

common-lib/                      # 공통 모듈 (Maven Central 배포)
├── event/                       # IntegrationEvent (멱등성/순서보장/재시도/TTL)
├── util/                        # UuidUtils (도메인별 ID 생성)
└── exception/                   # 공통 예외

의존성 추가

implementation 'io.github.jun-bank:common-lib:0.0.1'

서비스별 아키텍처 (헥사고날 + 도메인 중심 하이브리드)

service/
├── global/                      # 전역 설정 레이어
│   ├── config/                  # JPA, Kafka, Security, Feign, Swagger, Async
│   ├── infrastructure/          # BaseEntity, AuditorAware
│   ├── security/                # UserPrincipal, HeaderAuthenticationFilter
│   ├── feign/                   # FeignErrorDecoder, FeignRequestInterceptor
│   └── aop/                     # LoggingAspect
└── domain/
    └── {domain}/                # 도메인 단위
        ├── domain/              # 순수 도메인 (Entity, VO, Enum)
        ├── application/         # 유스케이스 + Port + DTO
        ├── infrastructure/      # Adapter (Out) - Repository, Kafka
        └── presentation/        # Adapter (In) - Controller

Global 레이어 공통 구성 (110개 파일)

패키지 파일 설명
config/ JpaConfig JPA Auditing 활성화
QueryDslConfig JPAQueryFactory 빈 등록
KafkaProducerConfig 멱등성 Producer (Spring Kafka 4.0)
KafkaConsumerConfig 수동 ACK Consumer
SecurityConfig 헤더 기반 인증, Stateless
FeignConfig 로깅, 에러 디코더
SwaggerConfig OpenAPI 문서화
AsyncConfig ThreadPoolTaskExecutor
infrastructure/ BaseEntity Audit + Soft Delete
AuditorAwareImpl JPA Auditing 사용자 정보
security/ UserPrincipal UserDetails 구현체
HeaderAuthenticationFilter Gateway 헤더 → SecurityContext
SecurityContextUtil 현재 사용자 조회 유틸리티
feign/ FeignErrorDecoder HTTP 에러 → BusinessException
FeignRequestInterceptor 인증 헤더 전파
aop/ LoggingAspect 요청/응답 로깅

인증 아키텍처

Client → Gateway → JWT 검증 → 헤더 주입 → 내부 서비스
                                │
                                ▼
                    X-User-Id, X-User-Role, X-User-Email
  • Gateway: JWT 토큰 검증 후 사용자 정보를 헤더로 전달
  • 내부 서비스: 헤더만 신뢰, JWT 검증 없음
  • HeaderAuthenticationFilter: 헤더 → SecurityContext 변환

이벤트 기반 통신

Kafka 토픽

토픽 Producer Consumer 설명
user.created User Auth 회원가입 → 계정 생성
user.deleted User All 회원탈퇴 → 연관 데이터 정리
account.balance.changed Account Ledger 잔액 변경 기록
transfer.debit.requested Transfer Account SAGA 출금 요청
transfer.credit.requested Transfer Account SAGA 입금 요청
transfer.completed Transfer Ledger 이체 완료 기록

IntegrationEvent 구조

public record IntegrationEvent(
        String eventId,           // 멱등성 보장
        String eventType,         // 이벤트 타입
        int sequenceNumber,       // 순서 보장
        LocalDateTime timestamp,  // 발생 시각
        LocalDateTime expiresAt,  // TTL
        int retryCount,           // 재시도 횟수
        Object payload            // 실제 데이터
) {}

빠른 시작

사전 요구사항

  • Java 21
  • Docker & Docker Compose
  • Gradle 8.x

실행 순서

# 1. 인프라 실행 (Kafka, PostgreSQL, Zipkin)
cd infrastructure
docker-compose up -d

# 2. Eureka Server 실행 (서비스 디스커버리)
cd eureka-server
./gradlew bootRun

# 3. Config Server 실행 (설정 서버)
cd config-server
./gradlew bootRun

# 4. Gateway Server 실행
cd gateway-server
./gradlew bootRun

# 5. 비즈니스 서비스 실행
cd user-service && ./gradlew bootRun &
cd auth-server && ./gradlew bootRun &
cd account-service && ./gradlew bootRun &
# ... 나머지 서비스

구현 진행 상황

✅ 완료

  • 프로젝트 초기 구조 설정 (10개 서비스)
  • common-lib 구현 (IntegrationEvent, UuidUtils, 113개 테스트)
  • 인프라 서버 기본 설정 (Eureka, Config, Gateway)
  • 비즈니스 서비스 Global 레이어 (110개 파일)
    • JPA, Kafka, Security, Feign, Swagger, Async 설정
    • BaseEntity (Soft Delete), JPA Auditing
    • 헤더 기반 인증 필터
    • 로깅 AOP
  • 비즈니스 서비스 Domain 레이어 (66개 파일)
    • 7개 서비스 전체 도메인 모델 구현 완료
    • Exception, Enum, VO, Aggregate Root 포함
    • 각 서비스별 핵심 학습 주제 반영

🔜 예정

  • Auth Server JWT 구현
  • Gateway JWT 검증 필터
  • Application Layer (UseCase, Port, Service)
  • Infrastructure Layer (Entity, Repository, Kafka)
  • Presentation Layer (Controller, DTO)
  • SAGA 패턴 구현 (Transfer Service)
  • Resilience4j 적용 (Card Service)
  • 통합 테스트
  • Docker Compose 전체 구성

도메인 레이어 구현 현황 (66개 파일)

서비스별 도메인 모델

서비스 파일 수 Aggregate Root 주요 VO 핵심 Enum
user-service 7 User UserId, Email, PhoneNumber UserStatus
auth-server 12 AuthUser, RefreshToken, LoginHistory AuthUserId, Password, RefreshTokenId UserRole, AuthUserStatus
account-service 8 Account AccountId, AccountNumber, Money AccountType, AccountStatus
transaction-service 9 Transaction, IdempotencyRecord TransactionId, IdempotencyKey, Money TransactionType, TransactionStatus
transfer-service 10 Transfer, OutboxEvent TransferId, OutboxEventId, Money TransferStatus, SagaStatus, OutboxStatus
card-service 11 Card, Payment CardId, CardNumber, PaymentId, Money CardType, CardStatus, PaymentStatus
ledger-service 9 LedgerEntry, AuditLog LedgerEntryId, AuditLogId, Money EntryType, TransactionCategory

도메인 모델 상세

User Service (7개 파일)

domain/user/domain/
├── exception/
│   ├── UserErrorCode.java          # 에러 코드 (INVALID_EMAIL, USER_NOT_FOUND 등)
│   └── UserException.java          # 도메인 예외 (팩토리 메서드 패턴)
└── model/
    ├── User.java                   # Aggregate Root (회원 정보 관리)
    ├── UserStatus.java             # 상태 Enum (ACTIVE, INACTIVE, SUSPENDED, WITHDRAWN)
    └── vo/
        ├── UserId.java             # USR-xxxxxxxx (8자리 영숫자)
        ├── Email.java              # 이메일 형식 검증, 정규화
        └── PhoneNumber.java        # 한국 전화번호 형식 (010-xxxx-xxxx)

Auth Server (12개 파일)

domain/auth/domain/
├── exception/
│   ├── AuthErrorCode.java          # 인증 에러 코드
│   └── AuthException.java          # 인증 예외
└── model/
    ├── AuthUser.java               # Aggregate Root (인증 사용자)
    ├── RefreshToken.java           # Refresh Token 관리
    ├── LoginHistory.java           # 로그인 이력 (감사 로그)
    ├── UserRole.java               # 역할 Enum (USER, ADMIN, SYSTEM)
    ├── AuthUserStatus.java         # 인증 상태 Enum
    └── vo/
        ├── AuthUserId.java         # AUTH-xxxxxxxx
        ├── Email.java              # 이메일 VO
        ├── Password.java           # BCrypt 해시, 강도 검증
        ├── RefreshTokenId.java     # RTK-xxxxxxxx
        └── LoginHistoryId.java     # LHI-xxxxxxxx

Account Service (8개 파일)

domain/account/domain/
├── exception/
│   ├── AccountErrorCode.java       # 계좌 에러 코드
│   └── AccountException.java       # 계좌 예외
└── model/
    ├── Account.java                # Aggregate Root (계좌, 낙관적/비관적 락)
    ├── AccountType.java            # 유형 Enum (CHECKING, SAVINGS, DEPOSIT)
    ├── AccountStatus.java          # 상태 Enum (ACTIVE, DORMANT, FROZEN, CLOSED)
    └── vo/
        ├── AccountId.java          # ACC-xxxxxxxx
        ├── AccountNumber.java      # 은행코드-상품-일련-검증 (110-xxxx-xxxx-xx)
        └── Money.java              # BigDecimal 래퍼, 통화 연산

Transaction Service (9개 파일)

domain/transaction/domain/
├── exception/
│   ├── TransactionErrorCode.java   # 거래 에러 코드 (멱등성 포함)
│   └── TransactionException.java   # 거래 예외
└── model/
    ├── Transaction.java            # Aggregate Root (입출금 거래)
    ├── IdempotencyRecord.java      # 멱등성 레코드 (IN_PROGRESS/COMPLETED/FAILED)
    ├── TransactionType.java        # 유형 Enum (DEPOSIT, WITHDRAWAL, TRANSFER_IN 등)
    ├── TransactionStatus.java      # 상태 Enum (PENDING, SUCCESS, FAILED, CANCELLED)
    └── vo/
        ├── TransactionId.java      # TXN-xxxxxxxx
        ├── IdempotencyKey.java     # 클라이언트 제공 키 (8~128자, 24시간 TTL)
        └── Money.java              # 금액 VO

Transfer Service (10개 파일)

domain/transfer/domain/
├── exception/
│   ├── TransferErrorCode.java      # 이체 에러 코드 (SAGA 관련)
│   └── TransferException.java      # 이체 예외
└── model/
    ├── Transfer.java               # Aggregate Root (이체, SAGA 상태 관리)
    ├── OutboxEvent.java            # Outbox 패턴 이벤트 (트랜잭션 원자성)
    ├── TransferStatus.java         # 상태 Enum (PENDING → COMPLETED/FAILED/CANCELLED)
    ├── SagaStatus.java             # SAGA 단계 (DEBIT_PENDING → CREDIT_PENDING → COMPLETED)
    ├── OutboxStatus.java           # Outbox 상태 (PENDING, PUBLISHED, FAILED)
    └── vo/
        ├── TransferId.java         # TRF-xxxxxxxx
        ├── OutboxEventId.java      # OBX-xxxxxxxx
        └── Money.java              # 금액 VO

Card Service (11개 파일)

domain/card/domain/
├── exception/
│   ├── CardErrorCode.java          # 카드 에러 코드 (한도 초과 등)
│   └── CardException.java          # 카드 예외
└── model/
    ├── Card.java                   # Aggregate Root (카드, 한도 관리)
    ├── Payment.java                # 결제 엔티티 (Card의 하위 엔티티)
    ├── CardType.java               # 유형 Enum (DEBIT, CREDIT, PREPAID)
    ├── CardStatus.java             # 상태 Enum (ACTIVE, INACTIVE, EXPIRED, BLOCKED)
    ├── PaymentStatus.java          # 결제 상태 Enum (PENDING, APPROVED, DECLINED 등)
    └── vo/
        ├── CardId.java             # CRD-xxxxxxxx
        ├── CardNumber.java         # 16자리 카드번호 (마스킹 지원)
        ├── PaymentId.java          # PAY-xxxxxxxx
        └── Money.java              # 금액 VO

Ledger Service (9개 파일)

domain/ledger/domain/
├── exception/
│   ├── LedgerErrorCode.java        # 원장 에러 코드
│   └── LedgerException.java        # 원장 예외
└── model/
    ├── LedgerEntry.java            # Aggregate Root (원장 기록, Append-only)
    ├── AuditLog.java               # 감사 로그 (불변)
    ├── EntryType.java              # 유형 Enum (DEBIT, CREDIT)
    ├── TransactionCategory.java    # 카테고리 Enum (DEPOSIT, WITHDRAWAL, TRANSFER 등)
    └── vo/
        ├── LedgerEntryId.java      # LED-xxxxxxxx
        ├── AuditLogId.java         # AUD-xxxxxxxx
        └── Money.java              # 금액 VO

도메인 설계 원칙

1. Value Object 패턴

모든 VO는 record로 구현하여 불변성을 보장하고, 생성자에서 자가 검증을 수행합니다.

public record AccountId(String value) {
    public static final String PREFIX = "ACC";
    
    public AccountId {
        if (!isValid(value)) {
            throw AccountException.invalidAccountIdFormat(value);
        }
    }
    
    public static AccountId generate() {
        return new AccountId(PREFIX + "-" + UuidUtils.generateShortId());
    }
}

2. Aggregate Root 패턴

Aggregate 내부 상태는 반드시 Root를 통해서만 변경되며, 비즈니스 메서드가 도메인 행위를 캡슐화합니다.

public class Account {
    public void withdraw(Money amount) {
        validateActive();
        validateSufficientBalance(amount);
        this.balance = this.balance.subtract(amount);
    }
    
    private void validateSufficientBalance(Money amount) {
        if (this.balance.isLessThan(amount)) {
            throw AccountException.insufficientBalance(this.balance, amount);
        }
    }
}

3. Enum 정책 패턴

상태 Enum이 비즈니스 규칙과 상태 전이 정책을 직접 관리합니다.

public enum TransferStatus {
    PENDING("대기중", false),
    COMPLETED("완료", true),
    FAILED("실패", true),
    CANCELLED("취소", true);
    
    public boolean canTransitionTo(TransferStatus target) {
        return switch (this) {
            case PENDING -> target != PENDING;
            case COMPLETED, FAILED, CANCELLED -> false;
        };
    }
}

4. Exception 체계

ErrorCode Enum과 팩토리 메서드 패턴으로 명확하고 일관된 예외 처리를 구현합니다.

public class UserException extends BusinessException {
    public static UserException userNotFound(String userId) {
        return new UserException(
            UserErrorCode.USER_NOT_FOUND,
            Map.of("userId", userId)
        );
    }
    
    public static UserException emailAlreadyExists(String email) {
        return new UserException(
            UserErrorCode.EMAIL_ALREADY_EXISTS,
            Map.of("email", email)
        );
    }
}

관련 저장소

저장소 설명
config-repo 설정 파일 저장소
infrastructure Docker Compose, 인프라 설정

라이선스

MIT License

About

프로젝트 설명

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors