Fitdo 개발과정에서 Spring Batch의 ItemWriter를 커스텀하는 과정에서 배운 내용을 정리합니다.
Fitdo에서는 매일 정오(12시)에 사용자의 전날 기록을 기준으로 점수를 계산하고, 이를 저장하는 작업을 수행하고 있습니다.
Spring Batch는 대량의 데이터를 처리하는 데 최적화된 프레임워크입니다.
작업 단위인 Job은 여러 단계(Step)로 나뉘며, 각 Step은 아래와 같은 세 가지 구성 요소로 이루어져 있습니다.
Spring Batch는 데이터를 효율적으로 처리하기 위해 chunk 단위로 동작합니다.
예를 들어, Reader가 데이터를 10개씩 읽으면 Processor는 이를 하나씩 처리하고, Writer는 10개의 데이터를 모아 한 번에 저장합니다.
Fitdo에서는 계산된 점수를 Redis의 Sorted Set 형태로 저장해야 했습니다.
하지만 Spring Batch에서 제공하는 기본 Writer인 RedisItemWriter는 key-value 구조만 지원하고 있어 Sorted Set에는 적합하지 않았습니다.
기본 RedisItemWriter는 다음과 같이 동작합니다:
스프링에서 지원하는 key-value writer 구현체
public class RedisItemWriter<K, T> extends KeyValueItemWriter<K, T> {
private RedisTemplate<K, T> redisTemplate;
public RedisItemWriter() {
}
protected void writeKeyValue(K key, T value) {
if (this.delete) {
this.redisTemplate.delete(key);
} else {
this.redisTemplate.opsForValue().set(key, value);
}
}
protected void init() {
Assert.notNull(this.redisTemplate, "RedisTemplate must not be null");
}
public void setRedisTemplate(RedisTemplate<K, T> redisTemplate) {
this.redisTemplate = redisTemplate;
}
}
위 코드에서 보이듯, 기본적으로 key-value 데이터 저장만 지원하고 있습니다.
Fitdo의 경우 점수를 Sorted Set에 저장해야 했기 때문에, 이 구현으로는 부족했습니다.
Sorted Set을 지원하기 위해 ItemWriter를 직접 구현했습니다.
Spring Batch의 ItemWriter 인터페이스를 상속받아 Redis의 zAdd 명령을 수행하도록 커스텀한 Writer를 만들었습니다.
🔗 sorted set에 배치 인서트를 수행하는 커스텀 Writer 클래스
public class RedisSortedSetItemWriter implements ItemWriter<UserScoreRow> {
private final RedisTemplate<String, String> redisTemplate;
public RedisSortedSetItemWriter(RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public void write(Chunk<? extends UserScoreRow> chunk) throws Exception {
for (UserScoreRow item : chunk.getItems()) {
redisTemplate.zAdd("userScores".getBytes(), item.getScore(),
String.valueOf(item.getUserId()).getBytes());
}
}