스프링 배치1 - 오래된 게시물 에서는 통계 배치가 아닌 단순 배치를 이용하였고 이번 포스트에서는 통계성격의 배치를 이용하여 차트를 적용하겠습니다.
통계식 배치가 뭐가 있을 까 해다가 게시판 관리자가 확인할 수 있는 게시물 통계 배치를 만들자고 생각하였다. 일단 작동 방식은 배치를 돌려 그 시간 기준 게시물의 갯수를 count하는 것이다. 게시물에는 status
라는 컬럼이 존재하는데 이는 sell
, soldOut
, old
로 존재한다. 배치를 돌 때 각 status
별로 group by해서 count하여 차트 형식으로 나타내었다.
@Bean
@JobScope
public Step cntByStatusStep() {
return stepBuilderFactory
.get("cntByStatusStep")
.<BoardEntity, BoardCountEntity>chunk(chunkSize)
.reader(cntByStatusReader())
.processor(processor())
.writer(cntByStatusWriter())
.build();
}
이번 배치에서는 JpaItemReader를 사용하였다. 왜 사용했냐면 많은 것을 사용해보고 싶어서 사용하였다. 이 리더를 사용할 때 queryString
을 작성하여야 하는데 이 부분이 까다로웠다. DB
에 맞게 쿼리를 작성해야하는지 Entity
에 맞게 작성해야 하는지 헷갈렸다. 결과적으로는 Entity
에 맞게 작성을 해야했다. 이후 ChunkSize
를 주고 쿼리를 돌려 작동을 확인 했다.
@Bean
@StepScope
public JpaPagingItemReader<BoardEntity> cntByStatusReader() {
return new JpaPagingItemReaderBuilder<BoardEntity>()
.queryString("select b.status as status ,count(b.status) as count from BoardEntity b group by b.status")
.pageSize(chunkSize)
.entityManagerFactory(entityManagerFactory)
.name("cntByStatusReader")
.build();
}
쿼리 결과 정상적으로 동작
해당 결과값을 BoardCountEntity
라는 테이블에 담았다. 이 테이블에 있는 값을 이용하여 차트를 그릴것이다.
@Bean
@StepScope
public ItemProcessor<Object, BoardCountEntity> processor() {
return items -> {
Object[] objects = (Object[]) items;
Iterator<Object> iterator = Arrays.stream(objects).iterator();
List<String> list = new ArrayList<>();
while(iterator.hasNext()){
String value = iterator.next().toString();
list.add(value);
}
return BoardCountEntity.builder()
.statusName(list.get(0))
.statusCount(Long.parseLong(list.get(1)))
.batchDate(LocalDate.now())
.build();
};
}
아래와 같은 형식으로 저장되었다.
@Bean
@StepScope
public JpaItemWriter<BoardCountEntity> cntByStatusWriter() {
return new JpaItemWriterBuilder<BoardCountEntity>()
.entityManagerFactory(entityManagerFactory)
.build();
}
DB에 저장되어있는 값을 이용해 관리자 페이지에 차트를 그려 넣어려 한다.
먼저 차트 세팅은 아래와 같다. 나는 sell
, old
, soldOut
이 세가지를 이용하여 차트를 그리기 때문에 해당 차트의 색을 설정해 주었다. 이후 data
부분에는 해당 데이터를 배열 형식으로 넣고, labels
에는 x축을 나타낼 값들을 넣어준다.
const config = {
type: "line",
data: {
labels: labels,
datasets: [
{
label: "판매중",
backgroundColor: "transparent",
borderColor: "red",
borderWidth: "2",
data: sellData,
},
{
label: "판매 완료",
backgroundColor: "transparent",
borderColor: "blue",
borderWidth: "2",
data: soldOutData,
},
{
label: "판매중 7일 경과",
backgroundColor: "transparent",
borderColor: "green",
borderWidth: "2",
data: oldData,
},
], //dataset 끝
},//data 끝
// 옵션
options: {
legend: {display: true},
title: {display: true, text: '상태별 게시글 갯수'}
}
}
해당 데이터들을 받아오고 배열에 담는 과정이다. ajax
를 통해 진행되었으며 코드는 아래와 같다. 이때 param값으로 날짜와 연도를 보내고 해당 기간만큼의 데이터를 추출한다. 예를들어 2021년 11월 이면 month가 11월 인 데이터를 추출한다. 만일 이 과정에서 해당하는 데이터가 없을 경우 alert
를 발생 시키도록 구현하였다.
해당 쿼리는 QueryDsl
로 작성하였다.
public List<BoardCountEntity> findByStatus(String statusName,String yearOption, String monthOption) {
return queryFactory.selectFrom(QBoardCountEntity.boardCountEntity)
.where(QBoardCountEntity.boardCountEntity.statusName.eq(statusName)
.and(QBoardCountEntity.boardCountEntity.batchDate.year().eq(Integer.parseInt(yearOption)))
.and(QBoardCountEntity.boardCountEntity.batchDate.month().eq(Integer.parseInt(monthOption)))
)
.orderBy(QBoardCountEntity.boardCountEntity.batchDate.asc())
.fetch();
}
// 그래프 data 세팅
function boardData() {
$.ajax({
method: "GET",
url: '/api/admin/adminBoardChart',
data: {"monthOption": monthOption, "yearOption": yearOption},
success: function (data) {
if (data.sell.length === 0 && data.soldOut.length === 0 && data.old.length === 0)
alert("해당기간에 조회된 데이터가 없습니다.");
for (let i = 0; i < data.sell.length; i++) {
sellData.push(data.sell[i].statusCount);
soldOutData.push(data.soldOut[i].statusCount);
oldData.push(data.old[i].statusCount);
labels.push(data.sell[i].batchDate);
boardCountChart.update();
}
},
error: function (request, status, error) {
alert("code: " + request.status + "\n" + "error: " + error);
}
});
}
이때 month, year가 드롭다운 형식으로 주어지는데 값이 바뀌면 boardData
함수를 실행하여 차트를 그리도록 하였다.
해당 날짜와 년도의 dropdown은 js를 이용하여 근 3년을 볼 수 있게 설정하였고, 페이지 접속 시 현재 년도와 현재 월이 자동으로 선택되게 하였다.
/*년도 option설정*/
for (let i = yearOption - 3; i <= yearOption; i++) {
$("#year").append("<option value='" + i + "'>" + i + "년" + "</option>");
}
/*현재 날짜 select*/
$("#year").val(yearOption).prop("selected", true);
$("#month").val(monthOption).prop("selected", true);
하지만 이 과정에서 차트가 지워지고 새로 그려지는 것이 아닌 기존 차트 옆에 이어져서 그려지게 나타났다.
위 문제를 해결하기 위해 year, month가 바뀌면 원래 있던 배열 즉 data
배열을 초기화 해주고 x축의 값 labels
도 초기화 해주는 작업을 하였다.
sellData =[];
soldOutData=[];
oldData=[];
labels=[];
const dataNameArr = [sellData,soldOutData,oldData];
let i=0;
boardCountChart.data.labels=labels;
boardCountChart.data.datasets.forEach((dataset) => {
dataset.data=dataNameArr[i];
i++
});
monthOption = $(this).val();
boardData();
chart.js에서 제공해주는 기능으로 아래와 같이 범주를 클릭하여 보고싶은 데이터만 볼수 있다.