페이지 불러오는 중
금융·통신·유통 등 엔터프라이즈 환경에서 월 수천만 건에 달하는 청구서 및 결재 내역 이메일을 대량 발송하는 핵심 발송 엔진의 Major 버전 업그레이드 및 커스텀 고도화 프로젝트입니다.
VARCHAR2, NUMBER, CLOB, BLOB, DATE 등을 MySQL의 VARCHAR, BIGINT/DECIMAL, TEXT, LONGBLOB, DATETIME으로 최적 맵핑.Sequence.NEXTVAL)를 MySQL의 AUTO_INCREMENT 또는 분산 ID 생성 전략으로 대체하고, PL/SQL 프로시저와 트리거의 비즈니스 로직을 애플리케이션 계층(Java Service)으로 흡수하여 DB 의존성 분리.단순 SQL 덤프 방식은 운영 중인 DB에 과도한 락(Table/Row Lock)과 I/O 부하를 유발하므로, 보안 인가 계정 기반의 커스텀 Java Batch 프로그램을 구축하여 이관을 수행했습니다.
[데이터 이관 파이프라인 단계]
1단계: 과거 분기/연도별 콜드(Cold) 데이터 사전 이관
2단계: 최근 기간별/월별 웜(Warm) 데이터 점진 동기화
3단계: 일자별/시간별 핫(Hot) 데이터 트래픽 최저점(새벽 시간대) 동기화
4단계: Major 버전 베타 검증 (데이터 정합성 및 누락 검증)
Chunk Size)와 Sleep 텀을 적용하여 점진적 Fetch 실행.기존 단일 1:1 메일 발송 구조를 탈피하고, 다중 서버(Server 1, 2, 3, 4...)가 협력하여 대량의 청구서를 병렬 처리하도록 Spring Integration + Spring Batch + Quartz 기반 분산 환경을 설계했습니다.
START/RUNNING으로 상태를 업데이트하는 속도보다 다른 노드의 트리거 확인 시점이 빨라 동일한 메일 발송 건이 중복 스케줄링되는 이슈 발생.// ⭕ 채널 격리 및 실행 직전 Status 이중 검증 로직
@Service
public class DistributedJobDispatcher {
@Autowired
private RedisTemplate<String, String> redisTemplate;
public void dispatchJob(String baseJobName, Long targetBatchId) {
// 1. Sharding 기반 유니크 Job Naming 생성으로 채널 격리
String jobChannelKey = String.format("%s_SHARD_%d", baseJobName, targetBatchId % TOTAL_SHARDS);
// 2. 분산 Lock을 통한 상태 선점 (중복 Trigger 방어)
Boolean acquired = redisTemplate.opsForValue()
.setIfAbsent("LOCK:" + jobChannelKey, "RUNNING", Duration.ofMinutes(5));
if (Boolean.TRUE.equals(acquired)) {
try {
// 이중 검증 후 Batch Job 실행
executeSpringBatchJob(jobChannelKey, targetBatchId);
} finally {
redisTemplate.delete("LOCK:" + jobChannelKey);
}
}
}
}
청구서 PDF/보안문서 등 5MB~10MB급 첨부파일이 수만 건 몰릴 때 발생하는 메모리 스파이크를 해결하기 위한 최적화 기법이다. (Java 15+의 ZGC를 쓸 수 없는 Java 1.8 환경에 맞춘 특화 튜닝)
[첨부파일 처리 최적화 전략]
1. Heap 메모리 직접 적재 ➔ Java Segment Block(버퍼 스트림) 분할 처리
2. Global / Static 참조 제거 ➔ 메서드 스코프 지역 참조로 변경하여 Young GC 대상화
3. OOM 방어 인터셉터 ➔ 임계치(예: Heap 85%) 도달 시 자동 큐 지연(Backpressure) 인가
byte[]나 String으로 Heap에 적재하지 않고, Segment 단위(Buffer Chunk)로 쪼개어 Mail Sender로 스트리밍 전송.static/global 캐시 변수를 전면 제거하여 메서드 종료 즉시 메모리가 수거되도록 리팩토링.InitiatingHeapOccupancyPercent)을 튜닝.