[게시판 프로젝트] 스프링 배치 적용(2)

J_Eddy·2021년 12월 28일
0
post-thumbnail

스프링 배치1 - 오래된 게시물 에서는 통계 배치가 아닌 단순 배치를 이용하였고 이번 포스트에서는 통계성격의 배치를 이용하여 차트를 적용하겠습니다.

📌 Spring Batch - 통계 배치

통계식 배치가 뭐가 있을 까 해다가 게시판 관리자가 확인할 수 있는 게시물 통계 배치를 만들자고 생각하였다. 일단 작동 방식은 배치를 돌려 그 시간 기준 게시물의 갯수를 count하는 것이다. 게시물에는 status라는 컬럼이 존재하는데 이는 sell, soldOut, old로 존재한다. 배치를 돌 때 각 status별로 group by해서 count하여 차트 형식으로 나타내었다.

JobStep

@Bean
    @JobScope
    public Step cntByStatusStep() {
        return stepBuilderFactory
                .get("cntByStatusStep")
                .<BoardEntity, BoardCountEntity>chunk(chunkSize)
                .reader(cntByStatusReader())
                .processor(processor())
                .writer(cntByStatusWriter())
                .build();
    }

Reader

이번 배치에서는 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();
    }

쿼리 결과 정상적으로 동작

processor

해당 결과값을 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();
        };
    }

아래와 같은 형식으로 저장되었다.

writer

@Bean
    @StepScope
    public JpaItemWriter<BoardCountEntity> cntByStatusWriter() {
        return new JpaItemWriterBuilder<BoardCountEntity>()
                .entityManagerFactory(entityManagerFactory)
                .build();
    }

📌 chart.js

세팅

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: '상태별 게시글 갯수'}
    }
}

data

해당 데이터들을 받아오고 배열에 담는 과정이다. 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에서 제공해주는 기능으로 아래와 같이 범주를 클릭하여 보고싶은 데이터만 볼수 있다.

profile
논리적으로 사고하고 해결하는 것을 좋아하는 개발자입니다.

0개의 댓글