1. 배경 및 문제 의식 (Background)
1.1 기존 방식: 메모리 합산 (Memory Aggregation)
초기에는 시스템 지갑(System Wallet)에 대한 DB 락 경합을 피하기 위해, 정산 루프 내에서 수수료를 메모리 변수(totalFee)에 합산한 뒤 마지막에 단 1회의 UPDATE로 처리함.
- Logic:
List 순회 → totalFee += fee → 루프 종료 후 Wallet.balance += totalFee
- 장점: 시스템 지갑 Row에 락이 걸리는 횟수 최소화 (성능 우수).
- 치명적 단점 (Risk): 데이터 휘발성. 배치 수행 중 서버가 다운되거나 예외가 발생하면, 판매자에게 정산금은 입금되었으나 수수료 데이터는 메모리에서 증발하여 금전적 손실 발생 가능성 존재.
1.2 기술적 도입 배경 (Why Multi-Thread?)
현재 프로젝트의 데이터 규모는 싱글 스레드로도 처리가 가능한 수준입니다. 하지만 실제 현업의 대규모 정산 시스템은 수십~수백만 건의 트랜잭션을 짧은 시간 내에 처리해야 하므로 **병렬 처리(Parallel Processing)**가 필수적입니다.
이에 따라 다음과 같은 두 가지 명확한 목적을 가지고 의도적으로 멀티 스레드 아키텍처를 도입했습니다.
- 확장성 확보: 데이터가 급증하더라도 로직 변경 없이 스레드 풀(Thread Pool) 조정만으로 처리량을 선형적으로 늘릴 수 있는 구조를 구축.
- 기술적 도전: 멀티 스레드 환경에서 필연적으로 발생하는 DB 락 경합(Lock Contention)과 데드락(Deadlock) 이슈를 직접 마주하고, 이를 기술적으로 해결하는 경험을 통해 동시성 제어 역량을 확보.
1.3 목표 (Objective)
- 데이터 정합성 보장: 서버 장애 시에도 수수료 내역이 절대 유실되지 않아야 한다.
- 동시성 처리 능력 확보: 데이터 양이 늘어날 것을 대비해 멀티 스레드(Multi-thread) 환경에서도 성능 저하 없이 동작해야 한다.
2. 기술적 도전과 해결 과정 (Troubleshooting)
Challenge 1: 정합성과 성능의 트레이드오프
문제 상황:
데이터 유실을 막으려면 정산 건마다 DB에 수수료를 저장해야 하는데, 매번 시스템 지갑을 UPDATE 하면 Row Lock 경합으로 성능이 급격히 저하됨 (병목 현상).
해결책: Transactional Outbox Pattern 도입 (INSERT 위주 설계)