중고물품 거래 플랫폼을 개발하면서, 마케팅 측면에서 유저가 이메일 수신을 허용하도록 유도하기 위해 생일인 유저에게 생일 쿠폰을 자동으로 발송하는 기능을 구현했다.
이 기능을 어떤 방식으로 구현할지 고민하던 중,
확장성과 유지보수성을 고려하여 Spring Batch를 도입하기로 결정했다.
Spring Batch란?
Spring Batch는 대용량 데이터를 효율적으로 처리하기 위한 Java 기반 배치 프레임워크다.
주로 청크(Chunk) 단위로 데이터를 나누어 처리함으로써, 상황에 따라 메모리를 유연하게 사용할 수 있다는 장점이 있다.
Spring Batch의 청크 처리 구조는 다음과 같은 구성요소로 이루어진다.
1. Scheduler → 배치 실행을 트리거(JobLauncher 실행) 한다.
2. JobLauncher → 스케줄러로부터 호출되어 Job을 실행한다.
3. Job → 여러 개의 Step으로 구성되며, 각 Step의 실행 순서를 관리한다.
4. Step → 독립적인 작업 단위로, 각 Step 내부에서 Reader, Processor, Writer를 통해 데이터가 처리된다.
5. JobRepository → Step의 실행 상태와 이력을 관리하는 저장소 역할을 한다.
6. TransactionManager → 청크 단위의 커밋과 롤백을 관리하여 안정적인 배치 처리를 보장한다.
Scheduler와 Batch의 차이
프로젝트를 진행하며 많은 사람들이 Scheduler와 Batch를 혼동하는 경우가 있었다.
이를 간단히 비유하자면,
Scheduler는 “알람 시계”이고,
Batch는 “알람이 울린 후 수행하는 일”이다.
즉, Scheduler는 Job을 실행시키는 역할, Batch는 실제 데이터를 처리하는 역할을 담당한다.
Scheduler의 구현 방식
Scheduler는 일반적으로 두 가지 방식으로 구현된다.
단일 서버 환경(Monolithic) 에서는 어떤 방식으로 구현하여도 문제 없지만,
Quartz 방식은 트리거가 발생한 기록을 DB 에 저장하여 다중서버 환경(MSA)에서 중복실행을 방지할 수 있다는 차이점이 있다.
Batch에서 데이터 읽기 (Reader)
Scheduler를 통해 Batch가 실행되면, 데이터 처리를 위해 DB에서 데이터를 조회해야 한다.
Spring Batch에서는 데이터를 읽는 방식에 따라 세 가지 Reader가 자주 사용된다.
1. CursorItemReader
Cursor 방식은 DB와 연결을 유지하며 하나씩 가져오는 방식으로,
메모리에 하나의 데이터만 적재되기 때문에 메모리 사용량이 적다.
하지만 하나의 DB 커넥션을 오랜 시간 점유하기 때문에, 멀티스레드 처리에는 부적합하다.
2. PagingItemReader
Paging 방식은 일정한 데이터 개수만큼 조회하여 메모리에 적재한 뒤,
DB 연결을 끊고 해당 데이터를 모두 처리하면 다음 페이지를 다시 조회하는 방식이다.
이 방식은 DB 커넥션을 오래 점유하지 않기 때문에 멀티스레드 환경에서도 안전하게 동작한다.
3. List Reader
List 방식은 데이터를 한 번에 모두 메모리에 로드한 뒤, 리스트 형태로 순차적으로 처리하는 방식이다.
따라서 메모리 사용량이 많아 대용량 데이터 처리에는 부적합하며, 일반적으로 소량의 데이터 처리나 테스트용으로 사용된다.
Batch에서 데이터 저장 (Writer)
데이터를 읽는 방식만큼 중요한 것이 데이터 저장 방식이다.
Spring Batch는 데이터를 청크(Chunk) 단위로 저장한다.
Chunk는 DB에 값을 저장하기 위해 사용되는 논리적 단위로
하나의 처리가 끝났다면 DB에 바로 저장되는 방식이 아닌 Chunk에 적재한 후,
Chunk가 가득 찼다면 Chunk 단위로 DB에 저장하는 방식이다.
Chunk 하나에 하나의 트랜잭션이 적용되기 때문에 DB부하 감소 및 처리성능이 향상된다.
단, 청크 단위로 커밋 및 롤백이 이루어지기 때문에 에러 발생 시 해당 청크 전체가 롤백된다는 점에 유의해야 한다.
구현 중 발생한 트러블 및 트러블슈팅
TransientDataAccessResourceException
문제 Spring batch 사용 시 Read 방식을 List로 구현하였을 때에는 정상적으로 동작이 수행되었다이후, read방식을 paging 으로 변경 후 quartz가 실행되었을 때, TransientDataAccessResourceException 발생하였다. org
developerhun.tistory.com
'Java' 카테고리의 다른 글
HttpSession(서버 세션) (0) | 2025.04.03 |
---|---|
Servlet, JSP (0) | 2025.04.02 |
Comparable VS Comparator (0) | 2025.03.02 |
[Java] Stream (0) | 2023.06.21 |
오버로딩(Overloading)과 오버라이딩(Overriding) (0) | 2023.06.09 |