우당탕탕

Spring Batch 처음 써보면서 비용 비교와 숫자 처리에서 막혔던 점들 본문

Tech/Spring

Spring Batch 처음 써보면서 비용 비교와 숫자 처리에서 막혔던 점들

모찌모찝 2026. 6. 18. 13:28

직장 동료가 Spring Batch를 써서 대용량 데이터를 처리하는 걸 보고 저도 도전해봤는데요. 생각보다 간단할 줄 알았는데 비용 비교 같은 숫자 처리에서 여러 시행착오가 있었어요. 특히 가격이나 요금, 한도 같은 숫자 값을 다룰 때 미묘한 오차나 비교 문제 때문에 코드가 꼬이는 경우가 많더라고요.

이 글에서는 제가 직접 Spring Batch를 세팅하고, 숫자 비교에서 발생한 문제와 그 해결법을 상세하게 정리했어요. 덕분에 여러분도 저처럼 헤매지 않고 비용/가격 관련 배치 작업을 빠르게 완성할 수 있을 거예요.

개발 환경 / 버전 정보

제가 사용한 환경은 다음과 같아요. Spring Batch 5.0.0 버전부터 바뀐 부분도 있었고, Java 17 기반이라 최신 기능도 써봤습니다.

  • Java 17
  • Spring Boot 3.2.0
  • Spring Batch 5.0.0
  • MySQL 8.0 (배치 메타데이터 저장용)

제가 쓴 핵심 배치 작업 방법

사실 이 부분이 제일 중요했어요. 배치 작업은 보통 Reader → Processor → Writer 구조를 따르는데, 여기서 가격 비교나 비용 계산을 어떻게 정확하게 할지 고민이 많았거든요.

public class PriceProcessor implements ItemProcessor<Order, ProcessedOrder> {
    @Override
    public ProcessedOrder process(Order item) throws Exception {
        // 비용과 가격 차이 비교 예시
        BigDecimal basePrice = item.getBasePrice();
        BigDecimal discountPrice = item.getDiscountPrice();
        
        // 가격 차이 구하기
        BigDecimal diff = discountPrice.subtract(basePrice);
        
        // 0과 비교할 때는 compareTo 사용
        if(diff.compareTo(BigDecimal.ZERO) < 0) {
            // 할인 전 가격이 더 클 때 처리
            item.setNote("할인 적용됨");
        } else {
            item.setNote("할인 없음");
        }

        return new ProcessedOrder(item);
    }
}

제가 제일 먼저 고민한 게 이 부분이었는데, double이나 float 대신 꼭 BigDecimal을 써야 한다는 거였어요. 소수점 오차 없이 가격 차이를 정확히 계산하려면 BigDecimal이 필수더라고요. compareTo로 비교하는 것도 처음에는 익숙하지 않았지만, 이게 가장 안정적이라는 걸 깨달았죠.

비용 비교 작업에서 3가지 차이점 비교 기준

그런데 여기서 많이들 헷갈려하는 게 숫자 비교 기준이에요. 저는 가격 비교를 세 가지 기준으로 나눠서 확인했는데요.

  • 1. 가격 차이(diff): 할인 가격과 기본 가격 차이(할인 적용 여부 판단)
  • 2. 비용 한도 초과 여부: 한도와 비용 비교하여 초과 시 경고 처리
  • 3. 요금 비교(조건별 요금 적용): 조건에 맞는 요금이 올바르게 선택됐는지 검증

아래 표는 이 세 기준에서 각각 어떻게 숫자가 비교되고, 제가 적용한 비교 방법을 한눈에 보여주는 표예요.

비교 기준 예시 값 (원) 비교 방법 차이 난 값 코드에서 주의할 점
가격 차이(diff) 기본 10000, 할인 8500 discountPrice.subtract(basePrice).compareTo(BigDecimal.ZERO) -1500 BigDecimal 사용 필수, compareTo로 0과 비교
비용 한도 초과 여부 비용 120000, 한도 100000 cost.compareTo(limit) > 0 20000 초과 초과 시 로그 또는 경고 표시
요금 비교 (조건별) 기본요금 5000, 프리미엄 8000 조건에 따라 요금 선택 및 equals() 비교 - equals() 로 값 일치 여부 확인 필수

여기서 가장 많이 헷갈렸던 부분

처음에 double이나 float으로 비용을 비교했다가 아주 미세한 오차 때문에 조건이 깨져서 한참 삽질했어요. 예컨대 100.00과 99.99999999가 같다고 착각해서 할인 처리가 제대로 안 되는 거죠.

// 이건 안 좋은 예
double base = 10000.00;
double discount = 8499.999999999;
if(discount < base) {
    // 실제로는 True지만 소수점 오차로 인한 문제 발생
}

그래서 BigDecimal로 바꾸고 비교도 compareTo를 썼더니 훨씬 안정적으로 동작했어요.

저는 이렇게 처리했어요: 배치 설정과 숫자 비교 팁

Spring Batch 설정은 의외로 간단했어요. @EnableBatchProcessing만 붙이고 기본 Job, Step 구성하면 기본적인 메타데이터 DB 저장부터 재시작 지원까지 자동으로 되더라고요.

@Configuration
@EnableBatchProcessing
public class BatchConfig {
    @Bean
    public Job priceCheckJob(JobBuilderFactory jobBuilderFactory,
                             StepBuilderFactory stepBuilderFactory) {
        return jobBuilderFactory.get("priceCheckJob")
                .start(priceCheckStep(stepBuilderFactory))
                .build();
    }

    @Bean
    public Step priceCheckStep(StepBuilderFactory stepBuilderFactory) {
        return stepBuilderFactory.get("priceCheckStep")
                .<Order, ProcessedOrder>chunk(100)
                .reader(orderItemReader())
                .processor(priceProcessor())
                .writer(orderItemWriter())
                .build();
    }

    // Reader, Processor, Writer 빈은 생략
}

각 Reader, Processor, Writer에서도 비용 비교는 위에서 설명한 BigDecimal 방식으로 처리했고, 스텝 단위로 chunk 사이즈를 맞춰서 안정적이게 배치 수행이 가능했죠.

여기서 삽질했던 부분들

이 에러가 왜 나는지 한참 찾았는데, 바로 DB 트랜잭션과 커밋 설정이 엉킨 탓이었어요. 배치가 중간에 실패했을 때 재시작하려면 반드시 트랜잭션 경계를 명확히 지정해야 하거든요.

org.springframework.transaction.CannotCreateTransactionException:
Failed to open transaction; nested exception is org.hibernate.TransactionException: ...

이 부분은 spring.datasource.auto-commit=false 설정과 배치 트랜잭션 관리가 꼬인 덕분이었는데, application.properties에서 명시적으로 auto-commit을 false로 설정하고 배치 Step마다 트랜잭션 범위를 조정하니 해결됐어요.

심화: 비용 비교 시 주의할 점과 최적화 팁

사실 여기서 더 중요한 건 숫자 처리 로직 외에 배치 성능과 유지보수인데요. 가격이나 비용이 자주 바뀌는 비즈니스 로직이라면 배치 내에서 조건별 요금 정책을 별도의 서비스나 룰로 분리하는 게 좋아요.

그리고 비용 비교가 복잡해지면 수많은 if-else가 나오기 쉬운데, 저는 Java 17부터 도입된 switch 식을 적극 활용해서 가독성과 유지보수성을 높였어요.

BigDecimal costBracket = ...;
String category = switch (costBracket.intValue()) {
    case 0 -> "무료";
    case 1, 2 -> "저가";
    case 3, 4, 5 -> "중가";
    default -> "고가";
};

이런 식으로 숫자 범위에 따라 카테고리를 나누면 비용 비교 로직이 훨씬 명확해지고, 향후 정책이 바뀔 때도 코드 수정이 줄어들어요.

자주 물어보시는 것들

Q. double 대신 BigDecimal을 써야 하는 이유가 뭔가요?

A. double은 내부적으로 이진 부동소수점 방식이라 0.1 같은 십진수를 정확히 표현하지 못해서 미묘한 오차가 생깁니다. 가격, 요금 같이 정확한 비교가 필요한 숫자에선 오차가 치명적이라 BigDecimal을 써서 이런 문제를 막는 거예요.

Q. 배치 chunk 사이즈는 얼마로 해야 하나요?

A. 저는 100으로 했는데, 데이터 크기와 DB 처리 속도에 따라 조절하세요. 너무 작으면 처리 효율이 떨어지고, 너무 크면 메모리 부담이 커요.

Q. 비용 비교 후 로그 출력은 어떻게 하나요?

A. 배치 로그에서는 보통 SLF4J 로거를 쓰고, 조건별로 경고나 정보 로그를 남기면 좋아요. 비용 한도 초과 시 WARN 레벨로 출력하면 추후 모니터링에도 도움 됩니다.

이렇게 직접 Spring Batch를 써보면서 겪은 비용/가격 숫자 비교 이슈부터 트랜잭션 문제, 최적화 팁까지 꽤 많은 걸 경험했는데요. 제 경험이 여러분 배치 작업에 작은 도움이 되길 바라면서, 이 글 하나면 비용 비교에 관한 고민은 대부분 해결되실 거예요. 다음에는 멀티스레드 배치 처리 경험도 공유해볼게요.

Comments