티스토리 뷰
API 서버에서 @Scheduled 배치 처리가 성능 문제로 이어지는 지점과 Spring Batch 도입 판단 기준
ebson 2026. 1. 27. 07:51Spring 기반 API 서버를 운영하다 보면 일정 주기로 수행해야 하는 작업이 자연스럽게 등장합니다. 오래된 데이터 정리, 통계 집계, 외부 시스템 동기화, 상태 갱신과 같은 작업들은 대부분 실시간 요청 처리와는 다른 흐름으로 수행됩니다. 이런 요구가 처음 등장할 때 가장 손쉽게 선택되는 방법이 바로 Spring Framework에서 제공하는 @Scheduled 어노테이션 기반 스케줄 작업입니다.
별도의 인프라를 구성하지 않아도 되고, 기존 애플리케이션 내부에서 간단하게 작업을 등록할 수 있다는 점에서 @Scheduled 방식은 매우 실용적인 출발점이 됩니다. 그러나 API 서버가 성장하고 트래픽과 데이터 규모가 커지면서, 스케줄 작업이 단순한 보조 기능이 아니라 중요한 데이터 처리 흐름으로 발전하는 순간부터 구조적 고민이 시작됩니다.
이 글에서는 @Scheduled 기반 배치 처리가 어떤 전제 위에서 동작하는지 공식 문서 관점에서 정리하고, API 서버 환경에서 어떤 성능 및 구조적 문제가 발생할 수 있는지 살펴봅니다. 그리고 이러한 고민이 왜 자연스럽게 Spring Batch 도입 검토로 이어지게 되는지 정리해 보고자 합니다.
@Scheduled 기반 작업이 동작하는 방식
Spring Framework 공식 문서에 따르면 @Scheduled 어노테이션은 애플리케이션 컨텍스트 내에서 스케줄된 작업을 등록하고, 내부적으로 TaskScheduler 또는 ScheduledExecutorService를 이용해 해당 작업을 실행합니다. 기본 설정에서는 단일 스레드 스케줄러가 사용되며, 필요에 따라 스레드 풀을 설정하여 병렬 실행을 구성할 수 있습니다.
중요한 점은 이 작업들이 별도의 프로세스나 외부 시스템에서 실행되는 것이 아니라, API 서버 애플리케이션과 동일한 JVM 내부에서 동작한다는 사실입니다. 즉, 웹 요청을 처리하는 스레드, 데이터베이스 커넥션, 메모리 자원과 같은 핵심 자원을 스케줄 작업과 공유하게 됩니다.
초기에는 이 구조가 단순하고 관리하기 쉽다는 장점으로 작용합니다. 스케줄 작업이 짧고 가볍고, 실패 시 영향 범위가 크지 않다면 충분히 합리적인 선택이 됩니다. 그러나 작업의 성격이 점차 배치 처리 형태로 확장되기 시작하면 상황이 달라집니다.
API 요청 처리와 배치 작업이 충돌하는 지점
API 서버는 기본적으로 외부 요청에 대한 빠르고 안정적인 응답을 목표로 설계됩니다. 반면 배치 작업은 상대적으로 긴 시간 동안 데이터를 처리하거나 대량의 레코드를 갱신하는 경우가 많습니다.
문제는 @Scheduled 기반 작업이 이 두 가지 성격의 작업을 동일한 실행 환경에 묶어 둔다는 점입니다. 예를 들어 대량 데이터를 조회하거나 업데이트하는 스케줄 작업이 실행되는 동안 CPU 사용률이 상승하거나 데이터베이스 커넥션을 장시간 점유하게 되면, 동시에 들어오는 API 요청 처리에도 간접적인 영향을 미치게 됩니다.
실무 환경에서는 특정 시간대에만 응답 속도가 느려지는 현상이 나타나기도 하는데, 이 원인을 추적해 보면 스케줄 작업이 실행되는 시점과 겹치는 경우가 적지 않습니다. 이러한 문제는 단순히 스레드 풀 크기를 늘리는 방식으로 해결되지 않는 경우가 많습니다. 결국 동일한 자원을 공유하고 있기 때문에 경쟁 구조 자체가 유지되기 때문입니다.
이 시점에서 스케줄 작업은 더 이상 단순한 보조 작업이 아니라, 서비스 품질에 영향을 미치는 요소로 인식되기 시작합니다.
실행 보장과 장애 복구의 문제
또 하나 고민하게 되는 부분은 실행 보장과 장애 복구 문제입니다. Spring Framework의 스케줄 기능은 작업을 특정 시점에 실행하는 역할을 담당하지만, 작업의 성공 여부나 실패 이후의 복구 과정을 구조적으로 관리하지는 않습니다.
예를 들어 작업 실행 중 애플리케이션이 재시작되거나 장애가 발생했을 경우, 어느 단계까지 처리되었는지를 추적하고 다시 이어서 처리하는 기능은 기본적으로 제공되지 않습니다. 이 기능을 구현하려면 별도의 상태 관리 로직과 데이터 저장 구조를 직접 설계해야 합니다.
처음에는 간단한 로그 기록 정도로 충분하다고 생각할 수 있지만, 처리해야 할 데이터 양이 증가하고 작업 실패가 서비스에 영향을 주기 시작하면 상황이 복잡해집니다. 실패 구간을 다시 처리해야 하거나 일부 데이터만 재처리해야 하는 요구가 등장하면서 코드가 점점 복잡해지게 됩니다.
이 지점에서 많은 개발자들이 느끼는 것은, 스케줄 작업 자체보다 실패 처리 로직이 더 큰 부담이 된다는 점입니다.
트랜잭션 처리와 데이터 일관성 고민
배치 작업은 대량 데이터를 처리하는 경우가 많기 때문에 트랜잭션 경계 설정 역시 중요한 고민이 됩니다. 하나의 큰 트랜잭션으로 처리할 경우 롤백 비용이 커지고 데이터베이스 부하가 증가할 수 있으며, 반대로 트랜잭션을 너무 잘게 나누면 실패 시 일관성을 유지하기 어려워집니다.
Spring의 일반적인 트랜잭션 모델은 서비스 로직 중심으로 설계되어 있기 때문에, 배치 처리 특유의 단계적 처리나 체크포인트 기반 복구 전략을 그대로 적용하기는 쉽지 않습니다. 결국 이러한 부분 역시 별도의 설계와 구현이 필요해지며, 코드 복잡도는 점차 증가하게 됩니다.
이러한 고민이 Spring Batch 도입 검토로 이어지는 이유
Spring Batch는 Spring 생태계에서 제공하는 공식 배치 처리 프레임워크로, 대량 데이터 처리와 안정적인 실행 관리에 초점을 맞추고 설계되었습니다. Job과 Step 구조, 실행 이력 관리, 재시작 기능, 체크포인트 기반 처리 등은 배치 작업에서 반복적으로 등장하는 요구사항을 프레임워크 차원에서 해결하도록 구성되어 있습니다.
중요한 점은 Spring Batch가 단순히 더 복잡한 기술이라는 것이 아니라, 배치 처리에 필요한 책임을 명확히 분리해 주는 구조를 제공한다는 사실입니다. API 서버 내부에 점점 많은 배치 로직이 추가되면서 발생하는 구조적 부담을 줄이고, 실행 상태 관리와 장애 복구를 체계적으로 다룰 수 있게 됩니다.
따라서 Spring Batch 도입은 단순히 기술을 교체하는 문제가 아니라, 배치 처리의 중요도가 높아지면서 자연스럽게 등장하는 구조적 선택이라고 보는 편이 현실에 더 가깝습니다.
언제까지 @Scheduled 방식이 충분한가
모든 스케줄 작업이 Spring Batch로 옮겨져야 하는 것은 아닙니다. 실행 시간이 짧고, 실패 시 영향 범위가 작으며, 단순 반복 작업에 가까운 경우라면 @Scheduled 방식은 여전히 합리적인 선택입니다.
문제는 작업이 점점 중요해지고, 실패 시 복구가 필요하며, 실행 시간과 데이터 규모가 증가하는 시점입니다. 이때부터는 스케줄 작업이 API 서버의 보조 기능이 아니라 핵심 처리 흐름의 일부가 되기 시작합니다.
이러한 변화를 인지하고 구조를 점검하는 시점이 바로 Spring Batch 도입을 검토하게 되는 자연스러운 출발점이라고 생각하게 됩니다.
정리하며
처음 @Scheduled를 도입할 때는 단순히 편리한 해결책으로 느껴지지만, 서비스가 성장하면서 배치 처리 역시 점점 중요한 영역이 됩니다. 그리고 그 과정에서 발생하는 성능 문제나 구조적 복잡성은 구현의 문제가 아니라, 도구가 전제하고 있는 책임 범위를 넘어섰기 때문이라는 사실을 나중에서야 깨닫게 되는 경우가 많습니다.
결국 중요한 것은 특정 기술이 더 좋다는 결론을 내리는 것이 아니라, 현재 시스템이 요구하는 책임의 무게가 어디에 위치해 있는지를 판단하는 일이라고 생각합니다. 저 역시 여러 프로젝트를 경험하면서, 단순한 스케줄 작업이 점차 중요한 데이터 처리 흐름으로 발전하는 과정을 반복해서 보게 되었고, 그때마다 구조를 다시 고민하게 되었습니다.
앞으로도 새로운 요구가 등장할 때마다 지금의 선택이 언제까지 유효할지 계속 점검하게 될 것 같습니다. 이런 고민과 정리를 공유하는 것이 같은 문제를 고민하는 개발자에게 조금이라도 도움이 되기를 기대합니다.
'WORK-RELATED' 카테고리의 다른 글
| Spring Batch 도입이 CPU 처리량과 메모리 사용에 미치는 구조적 영향 (0) | 2026.01.27 |
|---|---|
| JPA EntityListener에서 ConcurrentModificationException이 발생하는 이유와 권장 가이드 기반 해결 전략 (0) | 2026.01.23 |
| [JAVA] JVM 리소스 로깅 유틸리티 클래스 구현 및 활용 (0) | 2025.12.26 |
| [Spring Batch] 스프링배치 멀티스레딩 환경에서 스레드 안정성 (1) | 2025.12.09 |
| [Spring Batch] 병렬처리 구현과 실무 사례 (1) | 2025.08.29 |
- Total
- Today
- Yesterday
- Cache Avalanche
- Redis 성능 개선
- Initialization-on-Demand Holder Idiom
- 동시성처리
- 스레드 생명주기
- 캐시 장애
- Cache Penetration
- Eager Initialization
- 캐시와 인덱스
- 트랜잭션 관리
- 백엔드 아키텍처
- Redis vs DB
- Cache Aside
- Hot Key 문제
- 백엔드 성능 튜닝
- 백엔드 성능 설계
- 캐시 성능 비교
- Redis 캐시 전략
- Enum 기반 싱글톤
- Double-Checked Locking
- TTL 설계
- Java Performance
- InterruptedException
- spring batch 5
- 백엔드 성능
- Spring Batch
- DB 트랜잭션
- 트래픽 처리
- DB 인덱스 성능
- mybatis
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |

