[DB처리] 2. JdbcBatchItemWriter

y001·2026년 3월 15일

Spring Batch Guide

목록 보기
17/19
post-thumbnail

1. 시작하면서

Spring Batch에서 대량 데이터를 처리할 때 중요한 것은 데이터를 어떻게 읽느냐뿐 아니라 어떻게 저장하느냐이다. 배치 작업은 수십만 건 이상의 데이터를 처리하는 경우가 많기 때문에 단순히 INSERTUPDATE를 반복 실행하는 방식으로는 성능을 확보하기 어렵다. SQL이 실행될 때마다 데이터베이스와 애플리케이션 사이에 네트워크 왕복이 발생하고, 데이터베이스는 각각의 쿼리를 독립적으로 파싱하고 실행 계획을 수립해야 하기 때문이다.

JdbcBatchItemWriter는 이러한 문제를 해결하기 위해 JDBC의 batch update 기능을 활용한다. 여러 SQL 실행을 하나의 batch로 묶어 데이터베이스에 전달함으로써 네트워크 호출 횟수를 줄이고 데이터베이스가 여러 쿼리를 하나의 작업 단위로 처리하도록 만든다. 여기에 Spring Batch의 chunk 기반 처리 모델이 결합되면서 대량 데이터 처리 환경에서도 안정적인 처리 성능을 확보할 수 있다.

2. 내부 구조

JdbcBatchItemWriter는 JDBC의 PreparedStatement와 batch update 메커니즘을 기반으로 동작한다. Step이 실행되면 Writer는 Reader와 Processor를 통해 전달된 객체들을 SQL 파라미터로 변환하고 PreparedStatement에 바인딩한 뒤, 여러 SQL 실행을 하나의 batch로 묶어 데이터베이스에 전달한다.

이 과정에서 Writer는 다음과 같은 구성 요소를 사용한다.

구성 요소설명
DataSource데이터베이스 커넥션을 제공한다. Writer는 DataSource를 통해 JDBC Connection을 획득한다.
SQL실행할 INSERT 또는 UPDATE 쿼리이다. Writer는 이 SQL을 PreparedStatement 형태로 생성한다.
ItemSqlParameterSourceProvider객체의 필드를 SQL 파라미터로 변환한다. Named parameter 기반 SQL에서 주로 사용된다.
ItemPreparedStatementSetterPreparedStatement에 값을 직접 바인딩할 때 사용된다.
assertUpdatesSQL 실행 결과가 예상된 업데이트 개수와 일치하는지 검증할지 여부를 설정한다.

JdbcBatchItemWriter의 핵심은 여러 SQL 실행을 JDBC batch update로 묶어 실행한다는 점이다. 일반적인 JDBC update는 SQL이 실행될 때마다 데이터베이스에 요청이 전달된다.

UPDATE orders ...
UPDATE orders ...
UPDATE orders ...

이 방식은 SQL 실행 횟수만큼 네트워크 왕복이 발생하며 데이터베이스는 각 쿼리를 독립적으로 처리한다.

반면 JDBC batch update는 여러 SQL을 하나의 실행 단위로 묶는다.

UPDATE orders ...
UPDATE orders ...
UPDATE orders ...
executeBatch()

이 방식에서는 PreparedStatement에 여러 SQL 파라미터가 누적된 뒤 executeBatch() 호출 시점에 데이터베이스로 전달된다. 결과적으로 네트워크 호출 횟수가 감소하고 데이터베이스는 여러 SQL을 하나의 처리 단위로 실행할 수 있게 된다.

또한 일부 JDBC 드라이버는 batch update를 더욱 효율적으로 처리하기 위해 내부 최적화를 수행한다. 예를 들어 MySQL 드라이버에서 rewriteBatchedStatements=true 옵션이 활성화되면 여러 INSERT 문을 하나의 multi-value INSERT로 변환해 실행한다. 이 방식은 SQL 파싱 비용을 줄이고 네트워크 전송량을 줄여 대량 데이터 처리 성능을 크게 개선한다.

3. Chunk 처리

Spring Batch는 데이터를 chunk 단위로 처리하는 구조를 가진다. Step이 실행되면 Reader가 데이터를 읽고 Processor가 데이터를 가공한 뒤 Writer는 일정 개수의 데이터를 모아 한 번에 저장한다.

예를 들어 chunkSize가 1000이라면 Step의 실행 흐름은 다음과 같이 진행된다.

read 1000
process 1000
write 1000

Writer가 실행되는 시점에는 이미 1000개의 데이터가 메모리에 모여 있으며 JdbcBatchItemWriter는 이 데이터를 반복적으로 SQL 파라미터에 바인딩한 뒤 JDBC batch update로 실행한다.

실제 내부 동작은 다음과 같은 흐름을 따른다.

PreparedStatement 생성

객체1 → 파라미터 바인딩 → addBatch()
객체2 → 파라미터 바인딩 → addBatch()
객체3 → 파라미터 바인딩 → addBatch()
...

executeBatch()

이 과정에서 Spring Batch의 chunkSize는 JDBC batch update의 실행 단위가 된다. 즉 chunkSize가 1000이라면 한 번의 executeBatch() 호출로 최대 1000개의 SQL이 실행된다.

chunkSize가 클수록 네트워크 호출 횟수는 줄어들지만 트랜잭션 범위가 커지고 메모리 사용량도 증가한다. 반대로 chunkSize가 너무 작으면 batch 효과가 감소하고 SQL 실행 횟수가 증가하게 된다. 이러한 이유로 실무에서는 보통 500 ~ 2000 사이의 값을 사용하는 경우가 많다.

4. 예제

다음은 주문 상태를 업데이트하는 JdbcBatchItemWriter 설정 예제이다.

@Bean
public JdbcBatchItemWriter<Order> orderWriter(DataSource dataSource) {

    JdbcBatchItemWriter<Order> writer = new JdbcBatchItemWriter<>();

    writer.setDataSource(dataSource);

    writer.setSql("""
        UPDATE orders
        SET status = :status
        WHERE id = :id
    """);

    writer.setItemSqlParameterSourceProvider(
        new BeanPropertyItemSqlParameterSourceProvider<>()
    );

    return writer;
}

이 설정에서 중요한 역할을 하는 컴포넌트는 ItemSqlParameterSourceProvider이다. 이 컴포넌트는 객체의 필드를 SQL 파라미터로 변환하는 역할을 수행한다.

예를 들어 Order 객체가 다음과 같은 구조라면

class Order {
    Long id;
    String status;
}

Spring Batch는 객체 필드를 SQL 파라미터와 자동으로 매핑한다.

:id → order.id
:status → order.status

Writer가 실행될 때 Step의 chunkSize가 1000이라면 Reader와 Processor가 1000개의 데이터를 처리한 뒤 Writer가 batch update를 실행한다.

read 1000
process 1000
write 1000
executeBatch()

이 구조 덕분에 SQL 실행 횟수가 크게 줄어들고 데이터베이스와 애플리케이션 사이의 네트워크 비용도 감소한다. JdbcBatchItemWriter는 이러한 방식으로 대량 데이터 처리 환경에서도 안정적인 처리 성능을 제공한다.

0개의 댓글