ItemWriter를 커스텀하여 Redis Sorted Set 저장해보자

coding_ticket·2025년 1월 9일
0
post-thumbnail

Fitdo 개발과정에서 Spring Batch의 ItemWriter를 커스텀하는 과정에서 배운 내용을 정리합니다.

Fitdo에서는 매일 정오(12시)에 사용자의 전날 기록을 기준으로 점수를 계산하고, 이를 저장하는 작업을 수행하고 있습니다.

Spring Batch는 대량의 데이터를 처리하는 데 최적화된 프레임워크입니다.
작업 단위인 Job은 여러 단계(Step)로 나뉘며, 각 Step은 아래와 같은 세 가지 구성 요소로 이루어져 있습니다.

  • Reader: 데이터를 읽어오는 단계
  • Processor: 데이터를 가공하거나 변환하는 단계
  • Writer: 처리된 데이터를 저장하는 단계

Spring Batch는 데이터를 효율적으로 처리하기 위해 chunk 단위로 동작합니다.
예를 들어, Reader가 데이터를 10개씩 읽으면 Processor는 이를 하나씩 처리하고, Writer는 10개의 데이터를 모아 한 번에 저장합니다.


Redis에 점수 저장하기: 기본 Writer의 한계

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을 지원하는 커스텀 Writer 구현

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());
    }
}

0개의 댓글