SpringBoot Batch Monitoring System (1/3)
배치 모니터링 시스템 만들기 - 1단계
스프링부트 배치 모니터링 시스템 이란?
스프링 배치를 실행하면 실행정보들이 메타 데이타 테이블에 저장되는데, 배치를 실행한 뒤 데이타를 조회할 수 있는 표준 관리도구가 없기에
매번 데이타를 쿼리로 조회를 하려니 불편하기도 하고, 스프링부트 환경에서 웹 UI 기능도 익힐 겸 모니터링 시스템을 만들어 보았다.
이 스프링부트 모니터링 시스템은 Batch Job의 관리도구로써 목적보다는
- 스프링부트 환경에서 DB 접속 + MVC 구조 + Security + Thymeleaf 를 이용한 UI 구성을 목표로 하였으며
- (덤으로) Job 실행 결과 및 Job이 어떤 Parameter 를 가지고 어느 Step까지 실행했는지 등을 쉽게 확인하고
- Thymeleaf를 이용하여 UI를 적용한 모니터링 시스템을 만드는 것에 중점을 두었다.
배치에 대한 자세한 내용은 아래 링크를 참고하고, 이 글에서는 배치 모니터링 시스템에 대해서만 설명하려 한다.
스프링부트 개발환경 적용을 위한 첫 모니터링 시스템은 유료 디자인으로 오픈이 불가하여 Bootstrap의 샘플들을 조합하여 UI를 재구성하고 모니터링 + Batch Job 예제등을 포함한 간단한 데모 프로젝트로 구성했다.
유료 디자인 적용한 첫 UI
Bootstrap 샘플 디자인 적용한 UI
배치 메타테이블 데이타
배치 메타테이블 ERD
개발 환경
- Eclipse 2021-06 (4.20.0) + Spring Tools 4
- Java 11
- Spring Boot v2.7.8
- Thymeleaf-spring5 3.0.15
- Bootstrap v5
- MySQL
- Maven
배치 모니터링 시스템
1. 프로젝트 생성하기
@pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<!-- jdbc + mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.1.0</version>
</dependency>
<!-- html thymeleaf 사용 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- html thymeleaf layout 사용 -->
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>
<!-- html thymeleaf sec 사용 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<!-- Entity to Vo 객체 변환 -->
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.9</version>
</dependency>
</dependencies>
2. SpringBootApplication.java 작성
- 배치기능을 이용하기 위한 어노테이션 (@EnableBatchProcessing) 추가
@SpringBootApplication
@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class }) // Security 비활성
@ComponentScan(basePackages = "com.onda2me.app")
@MapperScan(basePackages = "com.onda2me.app.mapper")
@EnableBatchProcessing // 배치 사용
public class OndaBootApplication {
public static void main(String[] args) {
SpringApplication.run(OndaBootApplication.class, args);
}
}
3. application.yml 설정
#------------------------------------------------
# Web Server Config
#------------------------------------------------
server:
port: 8007
servlet:
jsp:
init-parameters:
development: true
#------------------------------------------------
# Spring Config
# - jsp, Thymeleaf, Security, Batch Job
# - spring.profiles : local / sample
#------------------------------------------------
spring:
main:
allow-bean-definition-overriding: true
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/bootdb
username: dbuser
password: dbpwd
hikari:
maximum-pool-size: 2
mvc:
view:
prefix: /WEB-INF/jsp
suffix: .jsp
security:
user:
name: admin01
password: 1122
role: ADMIN
#------------------------------------------------
# controller.view-name : /html/* -> thymeleaf(else jsp)
#------------------------------------------------
thymeleaf:
cache: false
check-template-location: false
enabled: true
prefix: classpath:/static/templates
suffix: .html
view-names: /html/*
#------------------------------------------------
# spring.batch.job.enabled=false/true
# job.names = simpleJob, multiStepJob
#------------------------------------------------
batch:
job:
enabled: false
#------------------------------------------------
# DB - mybatis config
#------------------------------------------------
mybatis:
configuration:
default-fetch-size: 100
default-statement-timeout: 30
map-underscore-to-camel-case: true
type-aliases-package: com.onda2me.app.entity
mapper-locations: classpath:mapper/*.xml
#------------------------------------------------
#log file config
#spring.config.activate.on-profile
#------------------------------------------------
logging:
config: classpath:logging/logback.xml
--- #local 환경
spring.config.activate.on-profile: local
server:
host: local
--- #sample 환경
spring.config.activate.on-profile: sample
server:
host: sample
4. Controller 작성
@HomeController.java
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
@RequestMapping(value="/")
public String index(Model model, Principal principal) {
logger.debug("---------------------------------");
logger.debug("index");
logger.debug("---------------------------------");
return "/html/index";
}
}
5. Thymeleaf 설정
@/templates/layouts/layout-dev.html
<!DOCTYPE html>
<html lang="ko"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<head th:replace="/layouts/common-head :: head"></head>
<th:block layout:fragment="css"></th:block>
</head>
<body>
<div class="body-container">
<aside th:replace="/layouts/common-aside :: aside"></aside>
<div id="main" class="main-container">
<div class="content-container">
<th:block layout:fragment="content"></th:block>
</div>
<div class="content-container">
<footer th:replace="/layouts/common-footer :: footer"></footer>
</div>
</div>
</div>
</body>
<th:block layout:fragment="script"></th:block>
</html>
@/templates/layouts/common-head.html
<th:block
xmlns:th="http://www.thymeleaf.org"
th:fragment="head">
<!--Common Head-->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/assets/css/bootstrap.min.css">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto'>
<script src='https://kit.fontawesome.com/a076d05399.js' crossorigin='anonymous'></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.3/jquery.min.js"></script>
</th:block>
@/templates/layouts/common-aside.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<th:block th:fragment="common-aside">
<aside id="sideContainer" class="side-container" >
<div class="vertical-menu ">
<div class="btn-group w-100 btn-group-lg" style="">
<button type="button" id="homeNav" class="btn btn-success w-100" onclick="goHref('/')"><span class="fa fa-home"></span> Home</button>
</div>
<a href="#" class="active"><i class="fas fa-thumbtack"></i><span> Ing..</span></a>
<ul class="side-nav nav flex-column">
<li class="nav-item">
<a class="nav-link" href="/batch/jobList">/batch/jobList</a>
</li>
</ul>
<a href="#" class="active"><i class="fas fa-arrow-alt-circle-right"></i><span> batch run</span></a>
<ul class="side-nav nav flex-column">
<li class="nav-item">
<a class="nav-link" href="/batch/simpleJob">simpleJob</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/batch/runJob?jobName=multiStepJob">runJob?multiStepJob</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/batch/runJob?jobName=multiTaskletJob&">runJob?multiTaskletJob</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/batch/runJob?jobName=simpleJob&period=daily">runJob?simpleJob&daily</a>
</li>
</ul>
</div>
</aside>
</th:block>
</html>
@/templates/layouts/common-footer.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<th:block th:fragment="common-footer">
<!-- Footer -->
<footer class="d-flex flex-wrap justify-content-between align-items-center py-3 my-4 border-top">
<p class="col-md-4 mb-0 text-muted">© 2022 onda2me, </p>
<ul class="nav col-md-4 justify-content-end">
<li class="nav-item"><a href="/" class="nav-link px-2 text-muted" target="_blank">Home</a></li>
<li class="nav-item"><a href="#" class="nav-link px-2 text-muted" target="_blank">About</a></li>
</ul>
</footer>
</th:block>
</html>
6. html 작성
@/templates/html/index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{/layouts/layout-dev}">
<th:block layout:fragment="content">
<h2>Welcome!! Onda2Me Home</h2>
<div class="prd-row">
<th:block th:each="num : ${#numbers.sequence(1,8)}">
<div class="prd-column nature">
<img src="/assets/images/main/mountains.jpg" alt="Mountains" style="width:100%;">
<div class="prd-content">
<p class="prd-name">
<a href="#" class="prd-link">Mountains</a>
</p>
<p>판매가 : <span class="" th:text="${num}"></span>0,000 원</p>
<p>할인가 : <span class="prd-price" th:text="${num}+'0,000'"></span> 원</p>
</div>
</div>
</th:block>
</div>
</th:block>
</html>
7. 웹에서 확인
Thymeleaf 적용한 메인 UI
배치 모니터링 시스템 만들기
▶ 1단계 기본환경 설정
- 프로젝트 생성 글보기
- Application properties 설정
- Thymeleaf 설정
- welcome 페이지 생성
2단계 배치 생성
- 배치 메타 테이블 생성 글보기
- 배치 클래스 생성
- 배치 실행 스케줄러 생성
3단계 모니터링 UI 작성
- Controller 클래스 생성 글보기
- Service 클래스 생성
- Mapper 클래스 생성
- mapper.xml (MySQL query) 생성
- Html 파일 생성
댓글남기기