티스토리 뷰


배치 애플리케이션의 성능 문제는 대개 특정 시점에 갑자기 발생하기보다, 서비스 운영 기간이 길어질수록 점진적으로 드러나는 경우가 많습니다. 초기에는 정상적으로 동작하던 작업이 데이터 규모 증가와 함께 실행 시간이 길어지고, 어느 순간부터 CPU 사용률이 지속적으로 높아지거나 메모리 사용량이 안정적으로 회수되지 않는 상황이 반복됩니다. 이후 GC 빈도가 증가하고, 배치 수행 시간이 예측하기 어려운 상태가 되면서 구조적인 개선 필요성이 제기됩니다.

 

이 시점에서 많은 팀이 Spring Batch 도입을 검토하게 됩니다. 다만 Spring Batch를 단순히 성능 개선 도구로 이해하면 기대와 다른 결과를 얻을 수 있습니다. Spring Batch는 처리 속도를 직접 높여주는 라이브러리가 아니라, 대용량 데이터를 안정적으로 처리할 수 있도록 구조를 정리하는 프레임워크에 가깝습니다. 이 글에서는 Spring Batch 도입이 CPU 처리량과 메모리 사용 패턴에 어떤 구조적 변화를 만드는지 공식 문서를 기반으로 정리해보고자 합니다.

 


 

배치 처리에서 반복적으로 나타나는 CPU와 메모리 병목

 

일반적인 배치 프로그램은 다음과 같은 흐름으로 구현되는 경우가 많습니다. 데이터베이스에서 대량의 데이터를 조회하고, 이를 애플리케이션 메모리에 적재한 뒤 반복문을 통해 처리하고 저장하는 방식입니다. 초기에는 단순하고 구현이 쉽지만 데이터 양이 증가하면 문제가 발생합니다.

 

우선 CPU 사용 패턴을 보면, 단일 스레드 기반 처리 구조에서는 CPU 코어를 충분히 활용하지 못하는 경우가 많습니다. 반대로 성능 개선을 위해 무작정 멀티 스레드를 도입하면, 스레드 간 컨텍스트 스위칭 비용과 동기화 비용이 증가하면서 실제 처리량은 기대만큼 늘어나지 않는 경우도 자주 발생합니다.

 

메모리 측면에서는 더 직접적인 문제가 나타납니다. 많은 데이터를 한 번에 조회하여 메모리에 올리면, 처리 완료 이전까지 해당 객체들이 힙 영역에 계속 남아 있게 됩니다. 객체의 생명주기가 길어지면 GC 비용이 증가하고, 결국 Old 영역까지 객체가 승격되면서 Full GC가 빈번하게 발생하는 구조로 이어집니다.

 

이러한 문제는 특정 코드의 문제라기보다, 처리 구조 자체에서 비롯되는 경우가 많습니다.

 


 

단순 루프 기반 처리 구조의 구조적 한계

 

배치 작업이 하나의 긴 루프 구조로 구현되어 있는 경우, 처리 단위가 명확하게 나뉘지 않습니다. 데이터 조회, 처리, 저장이 하나의 흐름에 포함되면서 트랜잭션 경계 역시 명확하지 않게 됩니다. 이 구조에서는 이미 처리가 끝난 데이터도 동일한 생명주기를 가지는 경우가 많습니다.

 

Java의 메모리 관리 구조를 고려하면, 객체가 오래 살아남을수록 GC 비용은 증가합니다. 특히 대량의 객체가 동시에 생성되고 오랜 시간 참조가 유지되면, GC는 더 많은 영역을 스캔해야 하고, 이는 CPU 사용률 증가로 직결됩니다.

 

또한 병렬 처리를 도입하려고 하더라도 작업 단위가 분리되어 있지 않으면 작업 분배 자체가 복잡해지고, 병렬 처리에 따른 부작용만 커질 수 있습니다. 결국 성능 문제는 개별 로직이 아니라 처리 구조에서 비롯된다는 점을 확인하게 됩니다.

 


 

Spring Batch가 제공하는 처리 모델의 핵심 구조

 

Spring Batch는 이러한 문제를 해결하기 위해 설계된 배치 전용 프레임워크입니다. 공식 문서에서 핵심으로 설명되는 구조는 Chunk 기반 처리 모델입니다.

 

Chunk 기반 처리에서는 데이터를 일정 크기로 나누어 읽고, 처리하고, 저장하는 흐름을 반복합니다. 이때 Chunk 단위로 트랜잭션이 종료됩니다. 즉, 하나의 Chunk 처리가 완료되면 해당 작업에 사용된 객체는 더 이상 참조되지 않는 상태가 됩니다.

 

이 구조는 메모리 사용 패턴을 크게 바꿉니다. 한 번에 대량 데이터를 메모리에 올리는 대신, 제한된 크기의 데이터만 처리하고 다음 단계로 넘어가게 됩니다. 결과적으로 객체 생명주기가 짧아지고, GC는 상대적으로 가벼운 Young 영역 중심으로 동작하게 됩니다.

 

이는 메모리 사용량을 안정적으로 유지하는 데 중요한 역할을 합니다.

 


 

CPU 처리량 관점에서 바라본 구조적 변화

 

Spring Batch를 도입한다고 해서 CPU 사용률이 자동으로 개선되는 것은 아닙니다. 다만 병렬 처리 구조를 설계하기에 적합한 경계를 제공합니다.

 

Spring Batch에서는 Step 단위 병렬 실행이나 Partition 기반 분산 처리가 공식적으로 지원됩니다. 처리 단위가 명확하게 나뉘어 있기 때문에, CPU 코어 수에 맞춰 작업을 분배하는 설계가 가능해집니다.

 

Chunk 단위 처리는 각각 독립적인 작업 단위로 다뤄질 수 있기 때문에, 병렬 처리 시에도 작업 충돌 가능성이 줄어듭니다. 이 구조는 CPU 자원을 보다 효율적으로 활용할 수 있는 기반을 제공합니다.

 

다만 병렬 처리 설정이 항상 성능 개선으로 이어지는 것은 아닙니다. 데이터베이스 I/O 병목이나 네트워크 지연이 주요 원인인 경우, CPU 병렬 처리 확대는 기대만큼의 효과를 내지 못할 수 있습니다. 결국 CPU 병목이 실제로 존재하는지를 먼저 확인하고 구조를 적용하는 것이 중요합니다.

 


 

메모리 사용량 관점에서의 구조적 이점

 

Spring Batch는 실행 상태를 JobRepository에 저장합니다. 이는 단순한 재시작 기능 제공을 넘어, 애플리케이션 메모리에 상태를 오래 유지하지 않도록 하는 구조를 제공합니다.

 

Chunk 단위로 트랜잭션이 종료되면서 처리 완료 데이터에 대한 참조가 빠르게 해제되고, GC가 효율적으로 동작할 수 있는 환경이 만들어집니다. 특히 처리 시간이 길어질수록 메모리 사용량이 지속적으로 증가하는 문제를 완화하는 데 도움이 됩니다.

 

대규모 데이터를 처리하는 환경에서는 이 구조적 차이가 상당한 안정성 차이를 만들어냅니다. 배치 수행 시간이 길어질수록 메모리 사용량이 불안정해지는 패턴을 완화할 수 있기 때문입니다.

 


 

Spring Batch 도입이 항상 성능 개선으로 이어지지 않는 이유

 

Spring Batch를 도입했음에도 성능이 개선되지 않는 사례도 존재합니다. 이는 대부분 설정 또는 사용 방식의 문제로 이어집니다.

 

예를 들어 Chunk 크기를 지나치게 크게 설정하면, 한 번에 메모리에 올라가는 데이터 양이 증가하여 기존 구조와 크게 다르지 않은 패턴이 됩니다. 반대로 지나치게 작은 Chunk는 트랜잭션 오버헤드를 증가시켜 CPU 처리 효율을 떨어뜨릴 수 있습니다.

 

또한 Reader나 Writer 구현에서 전체 데이터를 내부적으로 캐싱하는 구조를 사용하면 Chunk 처리의 이점이 사라지게 됩니다. 이 경우 Spring Batch 구조를 사용하더라도 실제 메모리 사용 패턴은 기존 구조와 유사하게 나타납니다.

 

결국 구조적 이점을 살리기 위해서는 처리 단위와 객체 생명주기를 함께 고려하는 설계가 필요합니다.

 


 

구조적 선택으로서의 Spring Batch 도입 판단

 

Spring Batch 도입 여부를 판단할 때 단순히 성능 개선을 기대하기보다, 현재 구조가 가진 한계를 구조적으로 해소할 수 있는지 검토하는 것이 중요합니다.

 

CPU와 메모리 문제는 대부분 특정 코드 한 줄의 문제가 아니라, 처리 구조 전체에서 발생합니다. Spring Batch는 이 구조를 명확하게 분리하고 제어할 수 있도록 돕는 도구에 가깝습니다.

 

처리 단위를 나누고, 객체 생명주기를 짧게 유지하며, 병렬 처리 경계를 명확히 설계할 수 있다면 결과적으로 CPU 처리량과 메모리 사용 패턴은 안정적으로 변화하게 됩니다. 성능 개선은 이러한 구조 변화의 결과로 나타나는 경우가 많습니다.

 


 

마무리하며 정리해 본 개인적인 관찰

 

Spring Batch를 도입하면 성능이 개선된다는 표현은 지나치게 단순화된 설명일 수 있습니다. 실제로는 구조를 어떻게 설계하고 적용하느냐에 따라 결과가 달라집니다.

 

다만 배치 작업이 점점 복잡해지고 데이터 규모가 커지는 상황에서는 처리 구조를 체계적으로 정리할 필요가 있습니다. 그 과정에서 Spring Batch는 충분히 의미 있는 선택지가 될 수 있습니다.

 

최근 배치 구조를 다시 검토하면서 느끼게 된 점은, 성능 문제는 대부분 처리 구조와 데이터 흐름을 이해하는 과정에서 해결의 실마리를 찾게 된다는 사실입니다. Spring Batch 역시 이러한 구조를 이해하고 정리하는 과정에서 자연스럽게 선택하게 되는 도구에 가깝다고 생각하게 되었습니다.

 

결국 중요한 것은 특정 기술 자체가 아니라, 현재 시스템 구조를 어떻게 이해하고 개선해 나갈 것인가에 대한 고민이라는 점을 다시 한 번 정리하게 됩니다.