소개

페이 서비스과 유사한 도메인 지식을 쌓기 위해, 계좌 관리 및 결제 트랜잭션을 다루는 작은 금융 서비스를 설계하고 구현하였습니다.

본 프로젝트에서는 다중 계좌 관리 및 외부 결제 연동 시 발생하는 트랜잭션 처리 문제를 해결하는 데 집중했습니다.

사용 기술 스택

Java21, Spring boot, JPA, MySQL, Redis

서버 구상도

image.png

구현 사항

1. 계좌 생성 및 충전

기능

고려한 것과 선택한 이유

  1. 메인 계좌 → 적금 계좌로 돈이 이동할 때 트랜젝션

    1. 비관적 락을 사용하여 메인 계좌, 적금 계좌 조회 시 X lock을 건다.
      • Pros : DB 단에서 강력한 동시성 제어가 가능하고 트랜젝션과 같이 사용 가능하다
      • Cons : 여러 자원에 락이 걸면 트랜잭션 간 락 획득 순서 제어가 어려워 데드락 발생 위험이 높다. 다중 Master DB에서 전체에 X lock이 걸리지 않기 때문에 글로벌락이 필요하다.
    2. Redis 분산락을 사용하여 메인 계좌, 적금 계좌에 락을 걸고 비지니스 로직을 수행한다.
      • Pros : 여러 애플리케이션 서버 및 다중 DB에서도 보장 가능하고 DB 락 보다 빠르게 처리 가능
      • Cons : 트랜젝션과 별도로 동작하기 때문에 추가로 설정을 해주어야한다. TTL 설정이 없으면 데드락에 걸릴 수 있다. Redis Failover 대비책 필요

    → 선택한 방법 : Redis 분산락 | Why? : Redis는 메모리 기반 저장으로 높은 처리량을 제공하며, 분산 서비스 환경에서도 대응 가능. 단일 Row가 아닌 비지니스 로직 단위로 락을 설계해 유연하게 사용 가능. Redis Failover에 대비해 낙관적 락을 추가로 적용할 수 있다.

  2. 개별 일일 인출 한도 관리

    1. DB에 인출 내역을 저장하고 계좌 조회 시
      • Pros : 결제 내역 조회 시 사용하는 테이블을 활용할 수 있다. DB 격리 수준에 맞게 조회가능하다.
      • Cons : Account table 외에도 JOIN 또는 추가 질의를 통해 결제 내역을 가져와야해서 캐싱보다 오래 걸린다.
    2. 캐시에 { key : 사용자:날짜 , value : 사용자 일일 타행 인출 금액 합 } 을 저장하고 TTL을 1day로 지정한다.
      • Pros : 캐시의 빠른 검색 속도. 일일 한도는 하루 동안만 필요한 데이터이기 때문에 TTL 설정으로 메모리 관리
      • Cons : 분산 환경에서는 로컬 캐시 대신 글로벌 캐시(ex. Redis) 를 사용해야한다. DB 저장 외에도 추가적인 공간을 사용

    → 선택한 방법 : 캐시 저장 | Why? : 1번에서 분산락으로 메인 계좌 사용에 락이 걸려있어, 별도의 동시성을 고려하지 않아도 괜찮고, 캐시 조회가 빠르며 하루 뒤에 데이터가 사라지기 때문에 데이터가 무한히 늘어나지 않는다.

  3. 타행 계좌에서 인출 시 에러가 발생했을 때

    1. 재시도 처리
      • Pros : 최종 처리 결과를 사용자에게 바로 전달할 수 있음.
      • Cons : 여러 번 재시도하면서 전체 요청 시간 증가로 락을 잡고있는 시간이 오래 걸림
    2. 이벤트 기반 비동기로 수행
      • Pros : 인출에 성공/실패에 대한 결과를 알림을 통해 전달한다면 전체 요청의 블로킹을 줄이고, 메인 계좌 락 최소화
      • Cons : 잔액 부족 등의 사유로 인출이 실패할 수도 있기 때문에 인출에 무조건 성공한다는 보장이 없어, 사용자에게 결과를 별도로 알려줄 방법이 필요하다.

    → 선택한 방법 : 재시도 처리 | Why? : 알림 시스템을 구축한다면 비동기 메세징을 활용해 락을 최소화 할 수 있지만, 이번 구현에서는 알림 시스템을 별도로 구현하지 않았기 때문에 동기식으로 재시도만 진행

  4. 타행 계좌 인출 후, 서버 장애가 발생했을 때 트랜젝션 처리 or 복구

    1. 타행 계좌 인출 전에 요청을 PENDING으로 기록한 Entity 저장 (기존 트랜젝션과 물리 트랜젝션 분리해 커밋)
    2. 타행 계좌 인출 요청 후, 입금이 완료되면 COMPLETE 으로 상태 변경
    3. 장애 발생 후 서버가 복구되면 (a) 상태인 거래를 찾아 복구 작업 수행

동시성 실험

동시성 처리 전