BackEnd/Spring Batch

Spring Batch + MyBatis 예제 정리 (CSV → DB 처리)

Raconer 2025. 6. 2. 13:49
728x90

Spring Batch와 MyBatis를 활용하여 CSV 파일 데이터를 읽고, 가공 후 데이터베이스에 저장하는 배치 처리 예제입니다.

https://github.com/Raconer/SpringBatch


📁 프로젝트 구성

com.example.batch
├── config
│   └── BatchConfig.java                  # 배치 Job/Step 설정
├── entity
│   └── Person.java                       # 도메인 엔티티
├── listener
│   └── JobCompletionNotificationListener.java  # Job 실행 리스너
├── processor
│   └── PersonItemProcessor.java          # ItemProcessor 구현체
├── mappers
│   └── PersonMapper.java                 # MyBatis Mapper 인터페이스
├── log
│   └── CustomMyBatisLoggerImpl.java      # MyBatis SQL 로그 커스터마이징
├── resources
│   ├── application.yml                   # 환경 설정
│   ├── schema-all.sql                    # 테이블 생성 SQL
│   ├── sample-data.csv                   # CSV 입력 데이터
│   └── mybatis/mappers/PersonMapper.xml  # MyBatis SQL 매퍼
└── BatchApplication.java                 # 실행 메인 클래스

🔧 실행 환경

  • Java: 17
  • Spring Boot: 2.7.13
  • 빌드 도구: Gradle

📦 주요 의존성

// Batch
implementation("org.springframework.boot:spring-boot-starter-batch")

// DB - MySQL
runtimeOnly("com.mysql:mysql-connector-j")

// MyBatis
implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.2")

// H2 (Test용)
runtimeOnly("com.h2database:h2")

// Lombok
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")

// Test
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.batch:spring-batch-test")

🌐 환경 프로파일별 DB 설정

Spring Boot의 spring.profiles.active에 따라 다른 DB를 사용하도록 구성:

  • local → MySQL
  • test → H2 (In-Memory)

각 환경은 application.yml 또는 application-{profile}.yml로 분기 관리합니다.


💬 구성 요약

  • FlatFileItemReader: CSV → Person 객체 변환
  • ItemProcessor: 이름 필드 대문자 변환
  • MyBatisBatchItemWriter: XML Mapper를 통한 DB 저장
  • JobListener: 실행 로그 및 상태 추적
  • Custom Logger: SQL 가독성 향상

Spring Batch

💡 Spring Batch란?
개발자가 정의한 작업을 일괄 처리하는 데 최적화된 스프링 기반 프레임워크입니다.

📚 참고 자료


✅ 대표 사용 사례

  • 일매출 집계, 정산 처리
  • 보험급여 계산
  • 외부 시스템 수신 데이터 적재

📌 배치 애플리케이션이 필요한 경우

  1. 스케줄 기반 주기적 작업이 필요할 때
  2. 대량 데이터의 비동기 처리 필요할 때

단일 서버에서 모든 것을 처리하지 않고, 별도 배치 애플리케이션에서 분리 운영함으로써 성능 향상


🔒 배치 시스템이 갖춰야 할 조건

  • 대용량 처리 능력: 파일, DB 등 다양한 대량 데이터 처리
  • 자동화: 사용자 개입 없이 실행 가능
  • 견고성: 오류 발생 시에도 중단 없이 진행
  • 신뢰성: 실패 로그, 알림 제공
  • 성능: 병렬 처리, 스케줄링 고려

⚙️ 실행 순서 정리

Spring Batch 실행 순서는 다음과 같습니다:

0. 청크 처리

  • ex) 1500개 단위로 처리 → 병렬 처리 핵심 단위

1. Job

  • 하나의 배치 작업 단위
  • 내부에 여러 개의 Step 포함

2. Step

  • 작업 단위
    • Reader: 입력 (파일/DB 등)
    • Processor: 데이터 가공 (선택)
    • Writer: 출력 (DB 등 저장)

3. JobLauncher

  • 배치 실행 트리거 역할 (스케줄러, REST 등에서 호출)

4. JobRepository

  • 실행 이력 관리, 실패 복구를 위한 상태 저장소

5. JobExecution

  • 실행 결과 요약, 성공/실패 여부 추적 가능

💡 핵심 코드 요약 (자세히 설명)

✅ BatchConfig.java

@Configuration
@EnableBatchProcessing
public class BatchConfig {
    // Job: 배치 작업 단위 구성
    @Bean
    public Job importUserJob(...) {
        return jobBuilderFactory.get("importUserJob")
            .incrementer(new RunIdIncrementer()) // 실행마다 다른 JobInstance로 처리
            .listener(listener)                // 실행 전/후 리스너 등록
            .flow(step(writer))                // 실행할 Step 연결
            .end()
            .build();
    }

    // Step: Reader → Processor → Writer 실행 흐름 정의
    @Bean
    public Step step(MyBatisBatchItemWriter<Person> writer) {
        return stepBuilderFactory.get("Person Insertion Step")
            .<Person, Person>chunk(10)         // 10개 단위 청크 처리
            .reader(reader())
            .processor(processor())
            .writer(writer)
            .build();
    }
}

✅ FlatFileItemReader

@Bean
public FlatFileItemReader<Person> reader() {
    return new FlatFileItemReaderBuilder<Person>()
        .name("personItemReader")
        .resource(new ClassPathResource("sample-data.csv"))
        .delimited()                          // 구분자 기반 파싱
        .names("firstName", "lastName")     // CSV 컬럼 이름 지정
        .fieldSetMapper(new BeanWrapperFieldSetMapper<>() {{
            setTargetType(Person.class);     // 매핑할 객체 타입 지정
        }})
        .build();
}

✅ PersonItemProcessor.java

public class PersonItemProcessor implements ItemProcessor<Person, Person> {
    public Person process(Person person) {
        return new Person(
            person.getFirstName().toUpperCase(),  // 이름 대문자 변환
            person.getLastName().toUpperCase()
        );
    }
}

✅ PersonMapper.xml

<mapper namespace="com.example.batch.mappers.PersonMapper">
    <insert id="insert" parameterType="com.example.batch.entity.Person">
        INSERT INTO PEOPLE (first_name, last_name)
        VALUES (#{firstName}, #{lastName})
    </insert>
</mapper>

 

728x90