팀프로젝트가 끝나고 되돌아보는 시간 (5) CHART.JS를 통해 데이터정보 시각화
시각적 효과가 미치는 영향이 굉장히 크다고 생각하기 때문에 D3.JS, HIGHCHART.JS 등 다양한 라이브러리 중
개인적으로 가장 시각적으로 이뻐보이는 CHART.JS를 선택했습니다.
관리자페이지 내 이용권 관리는 원래 결제목록(구매, 환불)만 확인 가능하도록 기획했었으나,
완성하고나니 페이지 구성이 부족한 느낌이 들어 차트를 추가 구현하게 되었습니다.
import (chart.js) document🚀
<!-- 제이쿼리 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous" type="text/javascript"></script>
<!-- chart.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
admin_pay.jsp(라인,바,도넛 3종 차트 canvas 생성)
<div class ="chartjs">
<div class ="linechart"><canvas id="linechart" height="250" width="250"></canvas></div>
<div class ="barchart"><canvas id="barchart" height="250" width="250"></canvas></div>
<div class ="donutchart"><canvas id="donutchart" height="250" width="250"></canvas></div>
</div>
chart 생성 javascript
var chart1Labels = [];
var chart1adopt = [];
var chart1home = [];
var chart1free = [];
var chart2Labels = [];
var chart2data1 = [];
var chart2data2 = [];
var chart2data3 = [];
var chart2tooltip1 = [];
var chart2tooltip2 = [];
var chart2tooltip3 = [];
var chart3Labels = [];
var chart3data = [];
$(document).ready(function(){
getTimeStamp();
jQuery.ajax({ // $.ajax 와 동일한 표현
url : '/samsam/storereport.do',
type : 'POST',
dataType : 'json', //서버에서 보내줄 데이터 타입
contentType : 'application/json;charset=utf-8',
success : function(map) {
console.log(map);
if(map.storecount != null || map.standbycount != null){
var count = "<tr><td>총 가입업체 수</td><td>" + map.storecount+"</td></tr>"
count += "<tr><td>인증대기업체 수</td><td>" + map.standbycount+"</td></tr>"
$('.storecount').append(count);
}//if
if(map.chart1 != null && map.chart1 != ""){
console.log("chart1")
console.log(map.chart1)
$.each(map.chart1, function(index, item){
if(item.board == "분양"){
chart1adopt.push(item.write_count);
chart1Labels.push(item.write_date.substr(2,8));
}
if(item.board == "가정"){
chart1home.push(item.write_count);
}
if(item.board == "책임"){
chart1free.push(item.write_count);
}
});
}//map.chart1
console.log("chart2")
console.log(map.adopt)
console.log(map.home)
console.log(map.free)
if(map.adopt != null && map.adopt != ""){
chart2Labels.push("분양")
var maxitem =0;
var max_local=null;
var second = 0;
var second_local=null;
var minitem = 0;
var min_local=null;
$.each(map.adopt, function(index, item){
console.log("인덱스 :"+index)
if (parseInt(item.alocal_count) > maxitem){
second = maxitem
second_local = max_local
maxitem = parseInt(item.alocal_count)
max_local = item.local
}else if(second < parseInt(item.alocal_count) < maxitem){
second = parseInt(item.alocal_count)
second_local = item.local
}else{
minitem = parseInt(item.alocal_count)
min_local = item.local
}
});
chart2data1.push(maxitem)
chart2tooltip1.push(max_local)
chart2data2.push(second)
chart2tooltip2.push(second_local)
chart2data3.push(minitem)
chart2tooltip3.push(min_local)
}
if(map.home != null && map.home != ""){
chart2Labels.push("가정")
var maxitem =0;
var max_local=null;
var second = 0;
var second_local=null;
var minitem = 0;
var min_local =null;
$.each(map.home, function(index, item){
console.log("가정인덱스 :"+index)
if (parseInt(item.hlocal_count) > maxitem){
second = maxitem
second_local = max_local
maxitem = parseInt(item.hlocal_count)
max_local = item.local
}else if(second < parseInt(item.hlocal_count) < maxitem){
second = parseInt(item.hlocal_count)
second_local = item.local
}else{
minitem = parseInt(item.hlocal_count)
min_local = item.local
}
});
chart2data1.push(maxitem)
chart2tooltip1.push(max_local)
chart2data2.push(second)
chart2tooltip2.push(second_local)
chart2data3.push(minitem)
chart2tooltip3.push(min_local)
}
if(map.free != null && map.free != ""){
chart2Labels.push("책임")
var maxitem =0;
var max_local =null;
var second = 0;
var second_local=null;
var minitem = 0;
var min_local =null;
$.each(map.free, function(index, item){
console.log("ㅊㅇ인덱스 :"+index)
if (parseInt(item.flocal_count) > maxitem){
second = maxitem
second_local = max_local
maxitem = parseInt(item.flocal_count)
max_local = item.local
}else if(second < parseInt(item.flocal_count) < maxitem){
second = parseInt(item.flocal_count)
second_local = item.local
}else{
minitem = parseInt(item.flocal_count)
min_local = item.local
}
});
chart2data1.push(maxitem)
chart2tooltip1.push(max_local)
chart2data2.push(second)
chart2tooltip2.push(second_local)
chart2data3.push(minitem)
chart2tooltip3.push(min_local)
}//map.chart2
console.log("chart3")
console.log(map.chart3pay)
console.log(map.chart3repay)
var repayCount = null;
if(map.chart3pay != null && map.chart3pay != ""){
$.each(map.chart3pay, function(index, item){
if(item.refund != null && item.refund !=""){
chart3Labels.push(item.refund);
chart3data.push(item.count)
}
});
}
if(map.chart3repay != null && map.chart3repay != ""){
$.each(map.chart3repay, function(index, item){
repayCount += item.biz_count
chart3Labels.push("Buy again")
chart3data.push(repayCount)
});
}
lineChart()
barChart();
donutChart()
console.log("create Chart");
},
error : function() {
console.log("업체카운팅 and chart ajax실패!!!");
}
});
})//ready
function lineChart(){
console.log("linechart")
console.log(chart1adopt)
console.log(chart1home)
console.log(chart1free)
console.log("툴팁")
console.log(chart1Labels)
var ctx = $('#linechart').get(0).getContext('2d');
var linedata =
{
labels: chart1Labels,
datasets:
[{
labels: '업체',
borderColor: 'rgba(255, 99, 132, 1.5)',
fill: false,
data: chart1adopt
},
{
labels: '가정',
borderColor: 'rgba(54, 162, 235, 1.5)',
fill: false,
data: chart1home
},
{
labels: '책임',
borderColor: 'rgba(255, 206, 86, 1.5)',
fill: false,
data: chart1free
}]
};
var lineoptions = {
responsive: true,
legend : {
display: false
},
title: {
display: true,
text: ' 일간분양게시현황',
fontSize: 17,
fontColor: 'rgba(46, 49, 49, 1)'
},
animation: false,
tooltips: {
displayColors: false, // 툴팁 바 컬러 표시 여부
titleFontColor: '#fff', // 툴팁 폰트 관련
titleAlign: 'center', // 툴팁 폰트 관련
bodyAlign : 'center',
callbacks: {
label: function(tooltipItem, data) {
var item = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
var label = data.datasets[tooltipItem.datasetIndex].labels;
return label + ' : '+item;
}
}
}
};
var lineChart = new Chart(ctx,{
type: 'line',
data: linedata,
options: lineoptions
});
}//line chart
function barChart(){
console.log("barchart")
console.log(chart2data1)
console.log(chart2data2)
console.log(chart2data3)
console.log("툴팁")
console.log(chart2tooltip1)
console.log(chart2tooltip2)
console.log(chart2tooltip3)
var ctx = $('#barchart').get(0).getContext('2d');
var bardata =
{
labels: chart2Labels,
datasets:
[{
labels: chart2tooltip1,
backgroundColor: 'rgba(255, 99, 132, 1)',
borderColor: 'rgba(255, 99, 132, 1.5)',
data: chart2data1
},
{
labels: chart2tooltip2,
backgroundColor: 'rgba(54, 162, 235, 1)',
borderColor: 'rgba(54, 162, 235, 1.5)',
data: chart2data2
},
{
labels: chart2tooltip3,
backgroundColor: 'rgba(255, 206, 86, 1)',
borderColor: 'rgba(255, 206, 86, 1.5)',
data: chart2data3
}]
};
var baroptions = {
responsive: true,
legend : {
display: false
},
title: {
display: true,
text: ' 최근 1주간 게시글 지역 탑3',
fontSize: 17,
fontColor: 'rgba(46, 49, 49, 1)'
},
animation: false,
tooltips: {
displayColors: false, // 툴팁 바 컬러 표시 여부
titleFontColor: '#fff', // 툴팁 폰트 관련
titleAlign: 'center', // 툴팁 폰트 관련
callbacks: {
label: function(tooltipItem, data) {
var item = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
var label = data.datasets[tooltipItem.datasetIndex].labels[tooltipItem.index];
return label + ' : '+item;
}
}
}
};
var barChart = new Chart(ctx,{
type: 'bar',
data: bardata,
options: baroptions
});
}//bar chart
function donutChart(){
console.log("donutchart")
console.log(chart3data)
console.log("툴팁")
console.log(chart3Labels)
var ctx = $('#donutchart').get(0).getContext('2d');
var donutdata =
{
labels: chart3Labels,
datasets:
[{
backgroundColor: [
'rgba(255, 99, 132, 1.5)',
'rgba(54, 162, 235, 1.5)',
'rgba(255, 206, 86, 1.5)'
],
hoverBorderColor : '#fff',
data: chart3data
}]
};
var donutoptions = {
responsive: true,
legend : {
display: true,
position : 'bottom'
},
title: {
display: true,
text: '최근 한달 구매내역',
fontSize: 17,
fontColor: 'rgba(46, 49, 49, 1)'
},
animation: false,
tooltips: {
displayColors: false, // 툴팁 바 컬러 표시 여부
titleFontColor: '#fff', // 툴팁 폰트 관련
titleAlign: 'center', // 툴팁 폰트 관련
bodyAlign : 'center'
}
};
var donutChart = new Chart(ctx,{
type: 'doughnut',
data: donutdata,
options: donutoptions
});
}//donut chart
처음 차트 내용의 구성은 아래와 같이 하려했습니다만,
차트 1 ) 분양게시글 증감현황 (업체/가정/책임) - 콤보 바라인 차트
차트 2 ) 구매 After (환불/재구매/유지) - 도넛
차트 3 ) 이용권 구매현황 (툴팁 - 매출지역top3 노출) - 라인
차트 1 은 증감을 시각화할때의 구체적인 내용이 결국 게시글 갯수를 라인차트로 표현하면 될 것 같았고,
차트 2 와 3은 배치순을 일/주/월 순으로 나열하는 것이 좋을 것 같아 수정하였습니다.
1. 일간분양게시현황(업체/가정/책임) - 라인차트(툴팁 - 게시판이름, 게시글갯수)
2. 최근 1주간 게시글 지역 탑 3 - 바
3. 최근 한달 구매내역 - 도넛
chart를 구성할 데이터를 ajax로 불러오고 미리 선언해둔 배열 변수에 데이터를 넣어줍니다.
중간중간 console.log로 제대로 들어갔는지 확인하면서 진행했습니다.
Controller (chart를 구성할 데이터)
@RequestMapping(value = "/storereport.do")
@ResponseBody
public Map<String, Object> storereport(Model model) {
Map<String, Object> map = new HashMap<String, Object>();
ArrayList<ChartjsVO> ncvo = new ArrayList<ChartjsVO>();
int storecount = adminSV.storecount();
int standbycount = adminSV.standbycount();
//chart1
ArrayList<TboardVO> chart1 = adminSV.getThreeCount();
//chart2
ArrayList<ChartjsVO> adopt = adminSV.weeklyLocala();
ArrayList<ChartjsVO> home = adminSV.weeklyLocalh();
ArrayList<ChartjsVO> free = adminSV.weeklyLocalf();
//chart3
ArrayList<ChartjsVO> chart3pay = adminSV.payedMonth();
ArrayList<ChartjsVO> chart3repay = adminSV.repayedMonth();
map.put("storecount", storecount);
map.put("standbycount", standbycount);
map.put("chart1", chart1);
map.put("adopt", adopt);
map.put("home", home);
map.put("free", free);
map.put("chart3pay", chart3pay);
map.put("chart3repay", chart3repay);
return map;
}
Mapper (Mybatis)
<!-- 인증대기업체 숫자 | 총 분양업체 숫자 -->
<select id="storecount" resultType = "int">
select count(*) from biz_member where status = '0'
</select>
<select id="standbycount" resultType = "int">
select count(*) from biz_member where status = '1'
</select>
<!-- chartjs 차트 1 ) 일간분양게시현황(업체/가정/책임) - 라인차트(툴팁 - 탑 3 작성자, 작성글갯수 요건 생각좀) -->
<insert id="countAdopt"><!-- 업체 -->
<selectKey keyProperty="count" resultType="int" order="BEFORE">
select count(*) from adopt_list where doc_date = TO_CHAR(SYSDATE -1 ,'yyyy-mm-dd')
</selectKey>
insert into threeboard(write_date, write_count, board) values(SYSDATE -1, #{count}, '분양')
</insert>
<insert id="countHome"><!-- 가정 -->
<selectKey keyProperty="count" resultType="int" order="BEFORE">
select count(*) from adopt_home where doc_date = TO_CHAR(SYSDATE -1 ,'yyyy-mm-dd')
</selectKey>
insert into threeboard(write_date, write_count, board) values(SYSDATE -1, #{count}, '가정')
</insert>
<insert id="countFree"><!-- 책임 -->
<selectKey keyProperty="count" resultType="int" order="BEFORE">
select count(*) from free_doc where doc_date = TO_CHAR(SYSDATE -1 ,'yyyy-mm-dd')
</selectKey>
insert into threeboard(write_date, write_count, board) values(SYSDATE -1, #{count}, '책임')
</insert>
<select id="getThreeCount" resultType = "TboardVO"><!-- 업체 -->
select * from threeboard where write_date >= TO_CHAR(sysdate -7, 'yyyy-mm-dd')
</select>
<!-- chartjs 차트 2 ) 최근 1주간 게시글 지역 탑 3 - 바 -->
<select id="weeklyLocala" resultType = "ChartjsVO"><!-- 업체 -->
select rownum, local, alocal_count from
(select m.local, count(m.local) as alocal_count from member_list m,
(select * from adopt_list where doc_date >= TO_CHAR(sysdate -7, 'yyyy-mm-dd')) a
where m.email = a.doc_email group by m.local order by alocal_count desc)
where rownum between 1 and 3
</select>
<select id="weeklyLocalh" resultType = "ChartjsVO"><!-- 가정 -->
select rownum, local, hlocal_count from
(select m.local, count(m.local) as hlocal_count from member_list m,
(select * from adopt_home where doc_date >= TO_CHAR(sysdate -7, 'yyyy-mm-dd')) a
where m.email = a.doc_email group by m.local order by hlocal_count desc)
where rownum between 1 and 3
</select>
<select id="weeklyLocalf" resultType = "ChartjsVO"><!-- 책임 -->
select rownum, local, flocal_count from
(select m.local, count(m.local) as flocal_count from member_list m,
(select * from free_doc where doc_date >= TO_CHAR(sysdate -7, 'yyyy-mm-dd')) a
where m.email = a.doc_email group by m.local order by flocal_count desc)
where rownum between 1 and 3
</select>
<!-- chartjs 차트 3 ) 최근한달 구매내역 (환불/구매) - 도넛 -->
<select id="payedMonth" resultType = "ChartjsVO">
select refund, count(*) as count from payed_list where sysdate >= TO_CHAR(add_months(sysdate,-1)) group by refund
</select>
<select id="repayedMonth" resultType = "ChartjsVO"> <!-- 재구매 -->
select biz_email, count(biz_email) as biz_count from (select * from payed_list where refund ='payed' and sysdate >= TO_CHAR(add_months(sysdate,-1)))
group by biz_email having count(biz_email) >= 2
</select>
'Devme > Project' 카테고리의 다른 글
JAVA / SPRING 프로젝트 삼삼하개(4) 간편결제 API를 이용하여 결제/환불 기능 구현 (4) | 2021.03.02 |
---|---|
JAVA / SPRING 프로젝트 삼삼하개(3) 마이페이지(개인/업체) 내 작성글, 작성댓글 조회 구현 (0) | 2021.03.02 |
JAVA / SPRING 프로젝트 삼삼하개(2) LOCAL DATA API를 이용하여 판매허가번호 인증 구현 (0) | 2021.02.27 |
JAVA / SPRING 프로젝트 삼삼하개(1) 이메일 인증으로 비밀번호찾기 (2) | 2021.02.26 |