# Windows (Chocolatey 사용)
choco install openjdk17
# macOS (Homebrew 사용)
brew install openjdk@17
# Linux (Ubuntu)
sudo apt-get install openjdk-17-jdk
# 설치 확인
java -version
javac -version
# IntelliJ IDEA Community Edition (무료)
# https://www.jetbrains.com/idea/download/
# Eclipse IDE for Enterprise Java Developers
# https://www.eclipse.org/downloads/
# Gradle 설치 (권장)
# Windows
choco install gradle
# macOS
brew install gradle
# Linux
sudo apt-get install gradle
# 설치 확인
gradle -version
웹 브라우저에서 https://start.spring.io/ 접속
프로젝트 설정:
Project: Gradle Project
Language: Java
Spring Boot: 3.2.x (안정 버전)
Group: com.example
Artifact: batch-tutorial
Name: batch-tutorial
Description: Spring Batch Tutorial Project
Package name: com.example.batchtutorial
Packaging: Jar
Java: 17
Dependencies 추가:
GENERATE 클릭 후 다운로드
batch-tutorial/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/batchtutorial/
│ │ │ └── BatchTutorialApplication.java
│ │ └── resources/
│ │ └── application.properties
│ └── test/
│ └── java/
│ └── com/example/batchtutorial/
│ └── BatchTutorialApplicationTests.java
├── build.gradle
└── gradlew (Unix용 실행 스크립트)
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.0'
id 'io.spring.dependency-management' version '1.1.4'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
// Spring Batch 핵심
implementation 'org.springframework.boot:spring-boot-starter-batch'
// 데이터베이스
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
// 웹 (모니터링용)
implementation 'org.springframework.boot:spring-boot-starter-web'
// 개발 편의
developmentOnly 'org.springframework.boot:spring-boot-devtools'
// 로깅 및 유틸리티
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
// 테스트
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.batch:spring-batch-test'
}
tasks.named('test') {
useJUnitPlatform()
}
# 애플리케이션 기본 설정
server.port=8080
spring.application.name=batch-tutorial
# H2 데이터베이스 설정
spring.datasource.url=jdbc:h2:mem:batchdb
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
# H2 콘솔 활성화 (개발용)
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
# JPA/Hibernate 설정
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
# Spring Batch 설정
spring.batch.job.enabled=false
spring.batch.jdbc.initialize-schema=always
# 로깅 설정
logging.level.org.springframework.batch=DEBUG
logging.level.com.example.batchtutorial=DEBUG
package com.example.batchtutorial;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BatchTutorialApplication {
public static void main(String[] args) {
SpringApplication.run(BatchTutorialApplication.class, args);
}
}
package com.example.batchtutorial.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
@Slf4j
@Configuration
public class HelloWorldBatchConfig {
@Autowired
private JobRepository jobRepository;
@Autowired
private PlatformTransactionManager transactionManager;
/**
* Hello World Job 정의
*/
@Bean
public Job helloWorldJob(Step helloWorldStep) {
return new JobBuilder("helloWorldJob", jobRepository)
.start(helloWorldStep)
.build();
}
/**
* Hello World Step 정의 - Tasklet 방식
*/
@Bean
public Step helloWorldStep() {
return new StepBuilder("helloWorldStep", jobRepository)
.tasklet(helloWorldTasklet(), transactionManager)
.build();
}
/**
* 실제 작업을 수행하는 Tasklet
*/
@Bean
public Tasklet helloWorldTasklet() {
return (contribution, chunkContext) -> {
log.info("===========================================");
log.info("🎉 Hello, Spring Batch World!");
log.info("🚀 첫 번째 배치가 성공적으로 실행되었습니다!");
log.info("⏰ 실행 시간: {}", java.time.LocalDateTime.now());
log.info("===========================================");
return RepeatStatus.FINISHED;
};
}
}
package com.example.batchtutorial.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/batch")
public class BatchController {
@Autowired
private JobLauncher jobLauncher;
@Autowired
private Job helloWorldJob;
/**
* 배치 수동 실행 엔드포인트
*/
@PostMapping("/hello")
public String runHelloWorldBatch() {
try {
// 매번 다른 파라미터로 실행 (중복 실행 방지)
JobParameters jobParameters = new JobParametersBuilder()
.addLong("timestamp", System.currentTimeMillis())
.toJobParameters();
var jobExecution = jobLauncher.run(helloWorldJob, jobParameters);
return String.format(
"배치가 실행되었습니다! " +
"Job ID: %d, 상태: %s",
jobExecution.getId(),
jobExecution.getStatus()
);
} catch (Exception e) {
log.error("배치 실행 중 오류 발생", e);
return "배치 실행 실패: " + e.getMessage();
}
}
/**
* 간단한 상태 확인 페이지
*/
@GetMapping("/status")
public String getStatus() {
return "Spring Batch Tutorial 애플리케이션이 실행 중입니다! " +
"POST /batch/hello 로 배치를 실행하세요.";
}
}
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Spring Batch Tutorial</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
}
.container {
text-align: center;
background: #f5f5f5;
padding: 30px;
border-radius: 10px;
}
button {
background: #007bff;
color: white;
border: none;
padding: 15px 30px;
font-size: 16px;
border-radius: 5px;
cursor: pointer;
margin: 10px;
}
button:hover { background: #0056b3; }
.result {
margin-top: 20px;
padding: 15px;
background: white;
border-radius: 5px;
border-left: 4px solid #007bff;
}
</style>
</head>
<body>
<div class="container">
<h1>🚀 Spring Batch Tutorial</h1>
<p>첫 번째 Spring Batch 프로젝트에 오신 것을 환영합니다!</p>
<button onclick="runBatch()">📤 Hello World 배치 실행</button>
<button onclick="openH2Console()">🗄️ H2 데이터베이스 콘솔</button>
<div id="result" class="result" style="display: none;">
<h3>실행 결과:</h3>
<p id="resultText"></p>
</div>
</div>
<script>
function runBatch() {
const button = event.target;
button.disabled = true;
button.innerHTML = '⏳ 실행 중...';
fetch('/batch/hello', { method: 'POST' })
.then(response => response.text())
.then(data => {
document.getElementById('result').style.display = 'block';
document.getElementById('resultText').textContent = data;
button.disabled = false;
button.innerHTML = '📤 Hello World 배치 실행';
})
.catch(error => {
alert('오류 발생: ' + error);
button.disabled = false;
button.innerHTML = '📤 Hello World 배치 실행';
});
}
function openH2Console() {
window.open('/h2-console', '_blank');
}
</script>
</body>
</html>
# 프로젝트 디렉토리로 이동
cd batch-tutorial
# Gradle로 빌드 및 실행
./gradlew bootRun
# Windows에서는
gradlew.bat bootRun
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.0)
2024-08-27 10:30:15.123 INFO --- [main] c.e.b.BatchTutorialApplication : Starting BatchTutorialApplication
2024-08-27 10:30:16.456 INFO --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http)
2024-08-27 10:30:16.789 INFO --- [main] c.e.b.BatchTutorialApplication : Started BatchTutorialApplication in 2.345 seconds
# 배치 실행
curl -X POST http://localhost:8080/batch/hello
# 응답 예시:
# 배치가 실행되었습니다! Job ID: 1, 상태: COMPLETED
// 테스트 클래스 작성
@SpringBootTest
class HelloWorldBatchTest {
@Autowired
private JobLauncher jobLauncher;
@Autowired
private Job helloWorldJob;
@Test
void testHelloWorldBatch() throws Exception {
JobParameters jobParameters = new JobParametersBuilder()
.addLong("timestamp", System.currentTimeMillis())
.toJobParameters();
JobExecution jobExecution = jobLauncher.run(helloWorldJob, jobParameters);
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
}
}
2024-08-27 10:35:22.123 INFO --- [http-nio-8080-exec-1] c.e.b.config.HelloWorldBatchConfig : ===========================================
2024-08-27 10:35:22.124 INFO --- [http-nio-8080-exec-1] c.e.b.config.HelloWorldBatchConfig : 🎉 Hello, Spring Batch World!
2024-08-27 10:35:22.124 INFO --- [http-nio-8080-exec-1] c.e.b.config.HelloWorldBatchConfig : 🚀 첫 번째 배치가 성공적으로 실행되었습니다!
2024-08-27 10:35:22.125 INFO --- [http-nio-8080-exec-1] c.e.b.config.HelloWorldBatchConfig : ⏰ 실행 시간: 2024-08-27T10:35:22.124
2024-08-27 10:35:22.125 INFO --- [http-nio-8080-exec-1] c.e.b.config.HelloWorldBatchConfig : ===========================================
jdbc:h2:mem:batchdbsa-- Job 인스턴스 정보
SELECT * FROM BATCH_JOB_INSTANCE;
-- Job 실행 정보
SELECT
JOB_EXECUTION_ID,
JOB_INSTANCE_ID,
CREATE_TIME,
START_TIME,
END_TIME,
STATUS,
EXIT_CODE
FROM BATCH_JOB_EXECUTION;
-- Step 실행 정보
SELECT
STEP_EXECUTION_ID,
JOB_EXECUTION_ID,
STEP_NAME,
STATUS,
READ_COUNT,
WRITE_COUNT,
COMMIT_COUNT,
START_TIME,
END_TIME
FROM BATCH_STEP_EXECUTION;
-- Job 파라미터
SELECT * FROM BATCH_JOB_EXECUTION_PARAMS;
BATCH_JOB_EXECUTION 테이블:
JOB_EXECUTION_ID | JOB_INSTANCE_ID | STATUS | START_TIME | END_TIME
1 | 1 | COMPLETED | 2024-08-27 10:35:22 | 2024-08-27 10:35:22
BATCH_STEP_EXECUTION 테이블:
STEP_EXECUTION_ID | JOB_EXECUTION_ID | STEP_NAME | STATUS | COMMIT_COUNT
1 | 1 | helloWorldStep | COMPLETED | 1
🎉 축하합니다! 첫 번째 Spring Batch 프로젝트를 성공적으로 완성했습니다. 이제 다음 단계로 넘어가서 더 실용적인 배치를 만들어보겠습니다.