JAVA 스프링 배치 개념 및 php 로 만든 간단 예제

김석재·2025년 1월 11일
0

Spring Batch 는 로깅/추적, 트랜잭션 관리, 작업 처리 통계, 작업 재시작, 건너뛰기, 리소스 관리 등 대용량 레코드 처리 기능을 제공
또한 최적화 및 파티셔닝 기술을 통해 대용량, 고성능 배치 작업을 가능하게 하는 서비스나 기능을 제공
배치가 실패하여 작업을 재시작한다면 실패한 지점부터 실행하게됨

Spring Batch 용어

Job
배치 처리 과정을 하나의 단위로 만들어 놓은 객체.
Job은 특정 작업을 정의하고, 실행 단위로 구분할 수 있는 가장 큰 논리적 배치 작업의 집합이다.
하나의 Job은 여러 Step으로 구성될 수 있으며, 각각의 Step은 순차적으로 실행되거나 조건에 따라 실행될 수 있다.
Job은 주로 비즈니스 로직에 따라 데이터 처리의 흐름을 정의하는 데 사용된다.

JobInstance
Job의 실행 단위를 나타내는 객체.
Job을 실행시키면 하나의 JobInstance가 생성된다.
즉, 같은 Job이라도 실행할 때마다 새로운 JobInstance가 생성되며, JobParameters에 따라 구별된다.
JobInstance는 동일한 JobDefinition(Job의 논리적 정의)에 대해 실행된 결과를 나타내며, 재시작 가능한 배치 작업을 관리하는 데 중요한 역할을 한다.

JobParameters
JobInstance를 구분하기 위한 용도로, Job 실행 시 외부에서 받는 파라미터를 가진 객체.
JobParameters는 JobInstance를 고유하게 식별하기 위해 사용되며, 예를 들어 날짜, 시간, ID 등의 값을 포함할 수 있다.
JobParameters가 다르면 새로운 JobInstance가 생성되며, 동일한 JobParameters를 사용하면 이미 실행된 JobInstance가 재시작될 수 있다.

JobExecution
JobInstance의 실행 시도에 대한 정보를 담고 있는 객체.
JobExecution은 실행 상태, 시작 시간, 종료 시간, 생성 시간 등 실행 관련 메타데이터를 포함하며, Job의 성공 또는 실패 여부를 나타낸다.
JobExecution은 Job이 실행될 때마다 새로 생성되며, 하나의 JobInstance는 여러 개의 JobExecution을 가질 수 있다(재시작이 가능하기 때문).

Step
Job의 배치 처리 과정을 정의하고, 순차적인 단계를 캡슐화한 객체.
Step은 Job의 실제 일괄 처리를 제어하는 모든 정보를 담고 있으며, Reader, Processor, Writer의 조합으로 작업을 처리한다.
하나의 Job은 최소 하나 이상의 Step을 가져야 하며, 각 Step은 독립적으로 실행될 수 있다.
Step은 실행 순서와 의존성을 설정할 수 있으며, 비즈니스 로직을 모듈화하여 효율적으로 관리할 수 있다.

StepExecution
JobExecution과 동일하게 Step의 실행 시도를 나타내는 객체.
StepExecution은 실행 상태, 처리된 데이터 개수, 실패 여부 등의 실행 정보를 포함하며, Step이 실제로 실행될 때 생성된다.
Job이 여러 Step으로 구성된 경우, 이전 Step이 실패하면 다음 Step은 실행되지 않으며, 이 경우 해당 StepExecution은 생성되지 않는다.

ExecutionContext
Job에서 데이터를 공유할 수 있는 데이터 저장소 역할을 하는 객체.
ExecutionContext는 JobExecutionContext와 StepExecutionContext 두 종류로 나뉘며, 데이터를 각 실행 범위에서 공유하거나 보존하는 데 사용된다.

  • JobExecutionContext: JobExecution 범위에서 데이터를 공유하며, Commit 시점에 저장된다.
  • StepExecutionContext: StepExecution 범위에서 데이터를 공유하며, Step 실행 중간에도 저장될 수 있다.
ExecutionContext를 사용하면 Step 간 데이터를 공유할 수 있으며, Job이 실패했을 경우 마지막 실행 값을 복원하여 작업을 재개할 수 있다.

JobRepository
스프링 배치의 모든 배치 처리 정보를 저장하고 관리하는 매커니즘.
Job 실행 시 JobRepository는 JobExecution과 StepExecution 객체를 생성하고, 실행 정보와 상태를 저장하거나 조회하여 사용한다.
JobRepository는 데이터베이스에 메타데이터를 저장하며, 배치 작업의 상태를 지속적으로 관리하여 재시작 및 복구 기능을 지원한다.

JobLauncher
Job과 JobParameters를 사용하여 Job을 실행하는 객체.
JobLauncher는 Job 실행의 시작점을 제공하며, 배치 작업을 프로그래밍적으로 트리거하거나 관리할 수 있다.
주로 Spring Scheduler 또는 Rest API 호출과 결합하여 사용된다.

ItemReader
Step에서 데이터를 읽어오는 역할을 담당하는 인터페이스.
ItemReader는 데이터베이스, 파일, 큐 등 다양한 소스에서 데이터를 읽어오도록 구현될 수 있다.
Spring Batch는 다양한 기본 Reader 구현체를 제공하며, 커스텀 Reader를 작성하여 특정 데이터 소스를 처리할 수도 있다.

ItemWriter
Step에서 처리된 데이터를 쓰는 데 사용되는 인터페이스.
ItemWriter는 결과 데이터를 데이터베이스에 삽입하거나 업데이트하거나, 파일에 저장하거나, 메시지 큐로 전송하는 등 다양한 작업을 수행할 수 있다.
Spring Batch는 기본적으로 여러 Writer 구현체를 제공하며, 데이터를 Chunk 단위로 묶어서 처리한다.

ItemProcessor
ItemReader에서 읽어온 데이터를 처리하는 역할을 담당하는 인터페이스.
ItemProcessor는 데이터를 필터링하거나 변환하는 작업을 수행하며, 처리된 데이터를 ItemWriter로 전달한다.
ItemProcessor는 배치 작업에서 필수 요소는 아니지만, 데이터를 가공하거나 정제할 필요가 있을 때 유용하게 사용된다.

Tasklet
단일작업 수행하는 구성요소
작업의 비즈니스 로직을 처리하고 작업이 끝나면 RepeatStatus.FINISHED 또는 RepeatStatus.CONTINUABLE 을 반환
일반적으로 단순 초기화 작업 및 한번만 실행되고 종료되는 작업에 사용

PHP 로 만든 간단 예제

호출부

$data = [
    "data" => [
        "aa-01-0001", "aa-01-0002", "aa-01-0003", "aa-01-0004", "aa-01-0005",
        "aa-01-0006", "aa-01-0007", "aa-01-0008", "aa-01-0009", "aa-01-0010",
        "bb-01-0011", "bb-01-0012", "bb-01-0013", "bb-01-0014", "bb-01-0015",
        "bb-01-0016", "bb-01-0017", "bb-01-0018", "bb-01-0019", "bb-01-0020",
        "aa-01-0021", "aa-02-0022", "aa-03-0023", "aa-04-0024", "aa-05-0025",
        "aa-06-0026", "aa-07-0027", "aa-08-0028", "aa-09-0029", "aa-10-0030"
    ]
];

require_once '경로 어쩌고저쩌고/batch.php';
$batchProcessor = new Batch(10, $data);

$batchProcessor->Job();

배치가 실행되는 클래스

<?php

/*
서버에서 크론탭으로 배치 파일 호출할때
0 * * * * /myServer/bin/php /modules/chatbot/theme/default-desktop/adm/batch.php
*/
class batch
{
    private $_chunkSize;
    private $_data;
    private $_logFile = 'batch_log.txt';
    private $_currentTime;
    private $_refinedData = [];

    public function __construct($chunkSize, $data)
    {
        $this->_chunkSize = $chunkSize;
        $this->_data = $data;
        $this->_currentTime = date('Y-m-d H:i:s');
    }

    public function Job()
    {
        // 배치 Job 이 돌 때 로그 쌓음
        $this->_initJobStep();

        // 데이터 정제 Step
        $this->_dataSetStep();
    }

    private function _initJobStep()
    {
        file_put_contents($this->_logFile, "Batch executed at: $this->_currentTime\n", FILE_APPEND);
        file_put_contents($this->_logFile, "Request Data : $this->_data\n", FILE_APPEND);
    }

    private function _dataSetStep()
    {
        // itemReader 는 데이터가 생성자에서 들어왔기 때문에 추가하지 않았음

        // processor
        $this->_process();

        // writer
        $this->_write();
    }

    private function _process()
    {
        $chunks = array_chunk($this->_data['data'], $this->_chunkSize);

        foreach ($chunks as $index => $chunk) {
            $this->_processChunk($chunk);
        }
    }

    private function _processChunk($chunk)
    {
        foreach ($chunk as $item) {
            $this->_refinedData[] = $this->_refineData($item);
        }
    }

    private function _refineData($item)
    {
        $parts= explode('-', $item);

        return [
            'name' => $parts[0] ?? null,
            'type' => $parts[1] ?? null,
            'code' => $parts[2] ?? null,
        ];
    }

    private function _write()
    {
        $sql = "INSERT INTO test_batch 어쩌고저쩌고";
        //쿼리 실행으로 db 삽입
    }

}



0개의 댓글