Spring Batch Guide 시리즈는 이동욱 개발자님의 Spring Batch 가이드를 보고 학습한 내용을 정리한 글입니다.
많은 내용이 원 글과 유사할 수 있습니다. 이 점 양해바랍니다 🙏🏻
프로젝트 개발 환경은 아래와 같습니다.
IntelliJ IDEA 2020.3.1
Spring Boot 2.4.1
Spring Batch 4.3.1
Java 8
Gradle
먼저 IntelliJ를 사용하여 Spring Boot 프로젝트를 생성합니다. New Project를 눌러서 프로젝트를 생성합니다.
Spring Initializer를 선택하고 Project SDK 버전을 1.8(Java 8)로 설정합니다.
본인이 사용할 Group, Artifact를 지정합니다. 그리고 Type은 Gradle, Java version은 8을 선택합니다.
Spring 의존성에서는 Lombok, Spring Data JPA, H2, MySQL, Spring Batch를 추가하겠습니다.
프로젝트가 생성되면 build.gradle
은 다음과 같습니다.
plugins {
id 'org.springframework.boot' version '2.4.1'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group = 'me.lxxjn0'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-batch'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.batch:spring-batch-test'
}
test {
useJUnitPlatform()
}
프로젝트의 구조는 아래와 같습니다. SpringBatchGuideApplication
클래스 안에 main
메서드가 위치하고 있습니다.
Batch Job을 만들기 전에 SpringBatchGuideApplication
클래스에 Spring Batch 기능 활성화 어노테이션인 @EnableBatchProcessing
어노테이션을 추가합니다.
해당 어노테이션을 선언하면 Spring Batch의 다양한 기능을 사용할 수 있게 됩니다.
(선언하지 않으면 Spring Batch 기능을 사용할 수 없다고 하니 꼭! 선언하도록 합시다 😆)
설정이 완료되면 job
패키지를 생성하고, 하위에 SimpleJobConfiguration
클래스를 생성합니다. 그리고 생성한 파일 안에 simpleJob이란 이름의 간단한 Spring Batch 코드를 작성합니다.
@Slf4j // log 사용을 위한 lombok 어노테이션
@RequiredArgsConstructor // Constructor DI를 위한 lombok 어노테이션
@Configuration
public class SimpleJobConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job simpleJob() {
return jobBuilderFactory.get("simpleJob")
.start(simpleStep1())
.build();
}
@Bean
public Step simpleStep1() {
return stepBuilderFactory.get("simpleStep1")
.tasklet((contribution, chunkContext) -> {
log.info(">>>>> This is Step1");
return RepeatStatus.FINISHED;
})
.build();
}
}
@Configuration
@Configuration
으로 등록해서 사용합니다.jobBuilderFactory.get("simpleJob")
simpleJob
이란 이름의 Batch Job을 생성합니다.
job의 이름은 별도로 지정하지 않고 이렇게 Builder를 통해 지정합니다.
stepBuilderFactory.get("simpleStep1")
simpleStep1
이란 이름의 Batch Step을 생성합니다.
jobBuilderFactory.get("simpleJob")
와 마찬가지로 Builder를 통해 이름을 지정합니다.
tasklet((contribution, chunkContext) -> {})
Step 안에서 수행될 기능들을 명시합니다.
Tasklet은 Step안에서 단일로 수행될 커스텀한 기능들을 선언할 때 사용합니다.
여기서는 Batch가 수행되면 log.info(">>>>> This is Step1")
가 출력되도록 합니다.
Batch Job을 생성하는 simpleJob은 simpleStep1을 품고 있습니다.
Spring Batch에서 Job은 하나의 배치 작업 단위를 말합니다. Job 안에서는 이처럼 여러 Step이 존재하고, Step 안에는 Tasklet 혹은 Reader & Processor & Writer 묶음이 존재합니다.
Step이 가질 수 있는 단위가 애매할 수도 있습니다. Step에서 Tasklet 하나와 Reader & Processor & Writer 한 묶음은 같은 레벨입니다.
그래서 Reader & Processor가 끝나고 Tasklet으로 마무리 짓는 것과 같은 Step은 만들 수 없다는 것을 꼭 명심해야 합니다.
Tasklet은 어찌보면 Spring MVC의
@Component
,@Bean
과 비슷한 역할이라고 보셔도 될 것 같습니다. 명확한 역할은 없지만 개발자가 지정한 커스텀한 기능을 위한 단위로 보시면 됩니다.
@Component
와@Bean
이 Spring MVC에서 개발자가 커스텀하게 생성한 Bean을 등록해주는 역할이라는 관점에서 비슷한 것 같습니다.
이제 Spring Batch 어플리케이션을 실행해보겠습니다. main
메서드를 실행하면 Batch가 실행됩니다.
실행해보면 아래처럼 log.info(">>>>> This is Step1")
가 잘 수행되어 로그가 찍힌 것을 확인할 수 있습니다.
위의 과정을 따라하고 단순히 Spring Batch는 어플리케이션 코드만 작성하면 된다고 생각하면 안됩니다.
Spring Batch에서는 메타 데이터 테이블이 필요합니다.
메타 데이터란 데이터를 설명하는 데이터라고 보면 됩니다.
Spring Batch의 메타 데이터는 다음과 같은 내용을 담고 있습니다.
등등 Batch 어플리케이션을 운영하기 위한 메타 데이터가 여러 테이블에 나눠져 있습니다.
위의 테이블이 있어야만 Spring Batch가 정상 작동합니다.
기본적으로 H2 DB를 사용할 경우엔 해당 테이블을 Spring Boot가 실행될 때 자동으로 생성해줍니다. 그러나 MySQL이나 Oracle과 같은 DB를 사용할 때는 해당 테이블을 개발자가 직접 생성해야만 합니다.
위 테이블들의 스키마는 사실 이미 Spring Batch에 존재하고 있고, 이를 복사해서 그대로 create table을 하면 됩니다.
MySQL을 설치해야 하는데 저는 Docker를 사용해서 실습을 진행하도록 하겠습니다.
먼저 아래 명령어를 통해 MySQL 5.7 버전을 다운받습니다.
docker pull mysql:5.7
정상적으로 잘 설치되었는지 images 명령어를 통해 확인합니다.
docker images
또는 Docker 어플리케이션을 통해 확인할 수도 있습니다.
MySQL의 데이터를 마운트할 폴더를 지정하고 Docker를 실행합니다. 이때 한글이 깨지지 않기 위해 추가적인 옵션을 넣어서 실행합니다.
docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=1234 --name spring-batch-guide-mysql -v /Users/lxxjn0/Dev/docker/spring-batch-guide-mysql:/var/lib/mysql mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
-d
-p
컨테이너의 포트를 호스트의 포트와 바인딩해 연결할 수 있습니다.
-p [host(외부)의 port]:[container(내부)의 port]
입니다.
--name
-v
데이터 볼륨을 설정합니다. 호스트와 공유할 디렉토리를 설정하여 파일을 컨테이너에 저장하지 않고 호스트에 바로 저장합니다. 호스트 디렉토리 뒤에 :ro
, :rw
를 붙여서 읽기, 쓰기 설정을 할 수 있으며 기본값은 :rw
입니다.
-v [host directory]:[container directory]
입니다.
--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
utf8mb4
)로 바꿔줍니다.그러면 이처럼 컨테이너가 올라간 것을 볼 수 있습니다.
또는 위의 방법말고 docker-compose.yml
파일로 만들어서 실행할 수도 있습니다.
(이 방법을 사용하면 Docker의 옵션을 좀 더 명확히 볼 수 있는 장점이 있습니다)
version: "3" # 파일 규격 버전
services: # 이 항목 밑에 실행하려는 컨테이너들을 정의
db: # 서비스 명
image: mysql:5.7 # 사용할 이미지
container_name: spring-batch-guide-mysql # 컨테이너 이름 설정
ports:
- "3306:3306" # 접근 포트 설정 (host port:container port)
environment: # -e 옵션
MYSQL_ROOT_PASSWORD: "1234" # MySQL 패스워드 설정 옵션
command: # 명령어 실행
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
volumes:
- /User/lxxjn0/docker/spring-batch-guide-mysql:/var/lib/mysql # -v 옵션
위의 docker-compose.yml
파일을 실행시키려면 해당 파일이 위치한 디렉토리에서 해당 명령어를 실행합니다.
docker-compose up -d
현재 실행 중인 Docker 컨테이너 목록을 확인할 수 있습니다.
docker ps -a
MySQL 이미지가 정상적으로 올라와 있는 것을 확인했다면, MySQL 컨테이너 bash 쉘에 접속합니다.
docker exec -it spring-batch-guide-mysql bash
MySQL 컨테이너를 실행할 때 지정한 정보를 입력해서 MySQL에 접속합니다.
mysql -u root -p
1234 # 패스워드 입력
마지막으로 실습에서 사용할 Database를 추가합니다.
CREATE DATABASE spring_batch default CHARACTER SET UTF8;
이처럼 MySQL을 설치 완료했다면 보다 편하게 사용하기 위해 IntelliJ와 연동을 해보도록 하겠습니다.
IntelliJ의 우측에 datasource 탭을 클릭하고 + 버튼을 클릭해서 Data Source에 MySQL을 선택합니다.
그리고 앞에서 MySQL를 설치할 때 설정한 값들을 사용해서 연동을 합니다.
Test Connection 버튼을 클릭해서 정상적으로 연결이 되는 지까지 확인합니다.
연결을 완료하면 이처럼 Docker에 설치한 MySQL 서버가 연동되었음을 확인할 수 있습니다.
application.yml
파일에 MySQL을 사용할 수 있도록 datasource설정을 추가합니다.
spring:
profiles:
active: local
---
spring:
config:
activate:
on-profile: local
datasource:
hikari:
jdbc-url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: sa
password:
driver-class-name: org.h2.Driver
---
spring:
config:
activate:
on-profile: mysql
datasource:
hikari:
jdbc-url: jdbc:mysql://localhost:3306/spring_batch
username: root
password: 1234
driver-class-name: com.mysql.cj.jdbc.Driver
spring.datasource.hikari
jdbc-url
연결할 MySQL 주소를 지정합니다.
localhost의 3306 포트로 연결되어서 spring_batch
라는 database에 접근합니다.
username
, password
driver-class-name
각각의 DBMS에 맞춰 JDBC 드라이버를 지정해야 합니다.
MySQL의 경우는 com.mysql.cj.jdbc.Driver
로 지정하였습니다.
기존에는 com.mysql.jdbc.Driver
로 지정하였는데 현재는 해당 이름이 deprecate되고 com.mysql.cj.jdbc.Driver
라는 이름을 사용하고 있습니다.
spring.config.activate.on-profile
해당 profile일 때 해당 설정을 사용하겠다는 것을 의미합니다.
기존에는 spring.profile
을 사용해서 설정하였는데 현재 spring.profile
속성은 Spring Boot 2.4.x 버전부터 deprecate된 것으로 보입니다.
아직 MySQL에 메타 테이블을 생성하지 않았기 때문에 실행을 해도 Batch가 실패할 것입니다.
우선 Spring Batch의 profile을 mysql로 설정하여 실행해보도록 하겠습니다.
profile을 설정할 수 있는 방법은 두 가지가 있습니다. 먼저 실행 환경에서 profile을 설정하는 방법입니다.
상단의 실행 환경 버튼에서 Edit Configurations를 클릭합니다.
그리고 기존의 환경을 복사해서 새로운 환경을 생성합니다.
이름을 구분하기 쉽게 설정해준 후 Active profiles에 mysql을 입력합니다.
그리고 해당 환경으로 실행하면 profile이 mysql인 상태로 실행되는 것을 확인할 수 있습니다.
두 번째 방법으로는 main
메서드를 가지고 있는 Application 클래스에 @Profile
어노테이션을 지정하는 방법입니다.
@Profile
어노테이션의 value
값으로 지정할 profile의 이름을 입력해주면 됩니다(@Profile(value = "mysql")
과 같이 value를 명시해서 입력해주지 않아도 됩니다. default 속성으로 value를 받습니다).
그러나 매번 코드에 변경이 필요하기 때문에 이번에는 간단한 첫 번째 방법을 사용하여 어플리케이션을 실행하도록 하겠습니다.
하지만 어플리케이션을 실행시키면 결과는 당연히 실패! 메타 데이터 테이블인 batch_job_instance
가 존재하지 않는다는 에러가 발생하면서 실패했음을 확인할 수 있었습니다.
이를 해결하기 위해서 앞에서 언급했던 메타 데이터 테이블 스키마인 scheam-mysql.sql
파일을 검색해서 MySQL에 동일한 스키마를 실행해줍니다.
스키마를 실행하면 메타 데이터 테이블이 생성됨을 확인할 수 있습니다.
그럼 다시 한번 Spring Batch를 실행해보도록 하겠습니다.
이전에 H2 Database의 로컬 환경과 마찬가지로 정상적으로 실행됨을 확인할 수 있습니다!
이번에는 간단하게 Spring Batch 어플리케이션을 작성해보았고, MySQL을 연동할 때 메타 데이터 테이블에 대한 중요성을 학습할 수 있었습니다.