-
할일목록(TODO) 앱 5 - chart.js 로 그룹핑한 데이터 그래프로 그리기프로젝트/할일목록(TODO) 앱 2023. 8. 9. 01:34728x90
https://www.chartjs.org/docs/latest/getting-started/
Getting Started | Chart.js
Getting Started Let's get started with Chart.js! Alternatively, see the example below or check samples. Create a Chart In this example, we create a bar chart for a single dataset and render it on an HTML page. Add this code snippet to your page: You should
www.chartjs.org
https://www.chartjs.org/docs/latest/general/colors.html
Colors | Chart.js
Colors Charts support three color options: for geometric elements, you can change background and border colors; for textual elements, you can change the font color. Also, you can change the whole canvas background. Default colors If a color is not specifie
www.chartjs.org
* 주의할점
토큰은 언제나 변경되므로 새로 발급받은 다음에 수업을 진행하도록 한다.
반드시 관리자 토큰을 사용해야 한다.
CORS 에러가 나면 아래와 같이 서버쪽 코드를 변경해서 브라우저 요청(live server)을 허용하도록 한다.
const corsOptions = { origin: '*', credentials: true }
* TODO 그룹핑 후 정렬하기 - 리팩토링
router.get('/group/:field', isAuth, isAdmin, expressAsyncHandler(async (req, res, next) => { if(req.params.field === 'category' || req.params.field === 'isDone'){ const docs = await Todo.aggregate([ { $group: { _id: `$${req.params.field}`, count: { $sum: 1 } } }, { $sort : { _id : 1} } ]) console.log(`Number Of Group: , ${docs.length}`) // 그룹 갯수 // docs.sort((d1, d2) => d1._id - d2._id) // _id 기준으로 정렬 res.json({ code: 200, docs }) }else{ res.status(400).json({ code: 400, message: 'you gave wrong field to group documents !'}) } })) router.get('/group/mine/:field', isAuth, expressAsyncHandler(async (req, res, next) => { // 사용자 대쉬보드 if(req.params.field === 'category' || req.params.field === 'isDone'){ const docs = await Todo.aggregate([ { $match: { author: new ObjectId(req.user._id) } // 내가 작성한 할일목록 필터링 }, { $group: { _id: `$${req.params.field}`, count: { $sum: 1 } } }, { $sort : { _id : 1} } ]) console.log(`Number Of Group: , ${docs.length}`) // 그룹 갯수 // docs.sort((d1, d2) => d1._id - d2._id) // _id 기준으로 정렬 res.json({ code: 200, docs }) }else{ res.status(400).json({ code: 400, message: 'you gave wrong field to group documents !'}) } })) router.get('/group/date/:field', isAuth, isAdmin, expressAsyncHandler(async (req, res, next) => { if(req.params.field === 'createdAt' || req.params.field === 'lastModifiedAt' || req.params.field === 'finishedAt'){ const docs = await Todo.aggregate([ { $group: { _id: { year: { $year: `$${req.params.field}`}, month: { $month: `$${req.params.field}` }}, count: { $sum: 1 } } }, { $sort : { _id : 1} } ]) console.log(`Number Of Group: ${docs.length}`) // 그룹 갯수 // docs.sort((d1, d2) => d1._id - d2._id) res.json({ code: 200, docs}) }else{ res.status(400).json({ code: 400, message: 'you gave wrong field to group documents !' }) } })) router.get('/group/mine/date/:field', isAuth, expressAsyncHandler(async (req, res, next) => { if(req.params.field === 'createdAt' || req.params.field === 'lastModifiedAt' || req.params.field === 'finishedAt'){ const docs = await Todo.aggregate([ { $match: { author: new ObjectId(req.user._id) } }, { $group: { _id: { year: { $year: `$${req.params.field}`}, month: { $month: `$${req.params.field}` }}, count: { $sum: 1 } } }, { $sort : { _id : 1} } ]) console.log(`Number Of Group: ${docs.length}`) // 그룹 갯수 // docs.sort((d1, d2) => d1._id - d2._id) res.json({ code: 200, docs}) }else{ res.status(400).json({ code: 400, message: 'you gave wrong field to group documents !' }) } }))
server > src > routes > todos.js 파일의 그룹핑 코드에서 $group 파이프라인 이후 정렬을 위하여 $sort 파이프라인을 적용하는 코드를 추가하였다. _id 기준으로 정렬하기 위하여 위와 같이 코드를 수정하였다.
* 전체 할일목록 카테고리별 그룹핑
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NjM5N2E1YzliYTBmNzE1ZjFlZjZjNmUiLCJuYW1lIjoic3VuQGdtYWlsLmNvbSIsInVzZXJJZCI6InN1bnJpc2UiLCJpc0FkbWluIjp0cnVlLCJjcmVhdGVkQXQiOiIyMDI0LTA1LTA3VDAwOjQ4OjI4LjM3N1oiLCJpYXQiOjE3MTUwNDMxNDksImV4cCI6MTcxNTEyOTU0OSwiaXNzIjoic3VucmlzZSJ9.2fBFKwlA_X5K-SVYXfxIAzpRy_xQcUCIbAZB22nolZw' fetch('http://localhost:5000/api/todos/group/category', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }) // GET 방식의 요청 .then(res => res.json()) .then(data => { const group = data.docs const ctx = document.getElementById('myChart'); new Chart(ctx, { type: 'bar', data: { labels: group.filter(item => item._id).map(item => item._id), datasets: [{ label: '# of category', data: group.filter(item => item._id).map(item => item.count), borderWidth: 1, backgroundColor: '#FFD700', }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: false } } } }) })
chart.js - 전체 할일목록 카테고리별 그룹핑 const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NjM5N2E1YzliYTBmNzE1ZjFlZjZjNmUiLCJuYW1lIjoic3VuQGdtYWlsLmNvbSIsInVzZXJJZCI6InN1bnJpc2UiLCJpc0FkbWluIjp0cnVlLCJjcmVhdGVkQXQiOiIyMDI0LTA1LTA3VDAwOjQ4OjI4LjM3N1oiLCJpYXQiOjE3MTUwNDMxNDksImV4cCI6MTcxNTEyOTU0OSwiaXNzIjoic3VucmlzZSJ9.2fBFKwlA_X5K-SVYXfxIAzpRy_xQcUCIbAZB22nolZw' fetch('http://localhost:5000/api/todos/group/isDone', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }) // GET 방식의 요청 .then(res => res.json()) .then(data => { const group = data.docs const ctx = document.getElementById('myChart'); new Chart(ctx, { type: 'bar', data: { labels: group.filter(item => item._id !== undefined && item._id !== null).map(item => item._id ? "종료" : "진행중"), datasets: [{ label: '# of category', data: group.filter(item => item._id !== undefined && item._id !== null).map(item => item.count), borderWidth: 1, backgroundColor: '#FFD700', }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: false } } } }) })
chart.js - 전체 할일목록 할일종료별 그룹핑 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TODO 분석그래프</title> </head> <body> <div> <canvas id="myChart"></canvas> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGNlMjVlNDc3ZjM5OTA4NjJkYTFhYTEiLCJuYW1lIjoi7YOc7JaRIiwiZW1haWwiOiJzdW5AZ21haWwuY29tIiwidXNlcklkIjoic3VucmlzZSIsImlzQWRtaW4iOnRydWUsImNyZWF0ZWRBdCI6IjIwMjMtMDgtMDVUMTA6MzU6MTYuNTU0WiIsImlhdCI6MTY5MTUxMjc3MywiZXhwIjoxNjkxNTk5MTczLCJpc3MiOiJzdW5yaXNlIn0.H9k4LaU0uKcfZ7UggBeSVLgmv_wSjbAS1YXXJs78arg' const ctx = document.getElementById('myChart'); fetch('http://127.0.0.1:5000/api/todos/group/date/createdAt', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }) .then(res => res.json()) .then(data => { const group = data.docs console.log(group) // 차트 그리기 new Chart(ctx, { type: 'bar', data: { labels: group.map(item => `${item._id.year}년 ${item._id.month}`), datasets: [{ label: '# of Todos', data: group.map(item => item.count), borderWidth: 1, backgroundColor: '#FFD700', }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: true } } } }); }) </script> </body> </html>
chart.js - 전체 할일목록 생성날짜별 그룹핑 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TODO 분석그래프</title> </head> <body> <div> <canvas id="myChart"></canvas> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGNlMjVlNDc3ZjM5OTA4NjJkYTFhYTEiLCJuYW1lIjoi7YOc7JaRIiwiZW1haWwiOiJzdW5AZ21haWwuY29tIiwidXNlcklkIjoic3VucmlzZSIsImlzQWRtaW4iOnRydWUsImNyZWF0ZWRBdCI6IjIwMjMtMDgtMDVUMTA6MzU6MTYuNTU0WiIsImlhdCI6MTY5MTUxMjc3MywiZXhwIjoxNjkxNTk5MTczLCJpc3MiOiJzdW5yaXNlIn0.H9k4LaU0uKcfZ7UggBeSVLgmv_wSjbAS1YXXJs78arg' const ctx = document.getElementById('myChart'); fetch('http://127.0.0.1:5000/api/todos/group/date/lastModifiedAt', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }) .then(res => res.json()) .then(data => { const group = data.docs console.log(group) // 차트 그리기 new Chart(ctx, { type: 'bar', data: { labels: group.map(item => `${item._id.year}년 ${item._id.month}`), datasets: [{ label: '# of Todos', data: group.map(item => item.count), borderWidth: 1, backgroundColor: '#FFD700', }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: true } } } }); }) </script> </body> </html>
chart.js - 전체 할일목록 업데이트 날짜별 그룹핑 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TODO 분석그래프</title> </head> <body> <div> <canvas id="myChart"></canvas> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGNlMjVlNDc3ZjM5OTA4NjJkYTFhYTEiLCJuYW1lIjoi7YOc7JaRIiwiZW1haWwiOiJzdW5AZ21haWwuY29tIiwidXNlcklkIjoic3VucmlzZSIsImlzQWRtaW4iOnRydWUsImNyZWF0ZWRBdCI6IjIwMjMtMDgtMDVUMTA6MzU6MTYuNTU0WiIsImlhdCI6MTY5MTUxMjc3MywiZXhwIjoxNjkxNTk5MTczLCJpc3MiOiJzdW5yaXNlIn0.H9k4LaU0uKcfZ7UggBeSVLgmv_wSjbAS1YXXJs78arg' const ctx = document.getElementById('myChart'); fetch('http://127.0.0.1:5000/api/todos/group/date/finishedAt', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }) .then(res => res.json()) .then(data => { const group = data.docs console.log(group) // 차트 그리기 new Chart(ctx, { type: 'bar', data: { labels: group.map(item => `${item._id.year}년 ${item._id.month}`), datasets: [{ label: '# of Todos', data: group.map(item => item.count), borderWidth: 1, backgroundColor: '#FFD700', }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: true } } } }); }) </script> </body> </html>
chart.js - 전체 할일목록 종료날짜별 그룹핑 만약 날짜별 정렬이 제대로 되어 있지 않으면 프론트쪽 코드에서 then 메서드 안에서 아래 코드를 추가한다.
const group = data.docs console.log(group) group.sort((a, b) => { if(a._id.year > b._id.year) return 1 else if(a._id.year < b._id.year) return -1 if(a._id.month > b._id.month) return 1 else if(a._id.month < b._id.month) return -1 else 0 }) // 차트 그리기
* 특정 사용자의 카테고리별 그룹핑
관리자가 아닌 특정 사용자의 토큰으로 교체해서 서버에 요청을 보내도록 한다.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TODO 분석그래프</title> </head> <body> <div> <canvas id="myChart"></canvas> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGQyMzczMTA0Mzk4MmQ2YTBhOTkzYTYiLCJuYW1lIjoiaGVjYnoiLCJlbWFpbCI6Imx3dmJuZXhAZ21haWwuY29tIiwidXNlcklkIjoicGlldXhuZ3FiZCIsImlzQWRtaW4iOmZhbHNlLCJjcmVhdGVkQXQiOiIyMDIzLTA4LTA4VDEyOjM4OjA5LjY1MloiLCJpYXQiOjE2OTE1MDU5NTksImV4cCI6MTY5MTU5MjM1OSwiaXNzIjoic3VucmlzZSJ9.Rfv2hJsRM9uWwtpq7R3IpwEViHi7YYZsFz7GZguWq2A' const ctx = document.getElementById('myChart'); fetch('http://127.0.0.1:5000/api/todos/group/mine/category', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }) .then(res => res.json()) .then(data => { const group = data.docs console.log(group) // 차트 그리기 new Chart(ctx, { type: 'bar', data: { labels: group.map(item => item._id), datasets: [{ label: '# of Todos By', data: group.map(item => item.count), borderWidth: 1, backgroundColor: '#FFB1C1', }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: true } } } }); }) </script> </body> </html>
chart.js - 특정 사용자의 카테고리별 TODO * 특정 사용자의 할일종료 여부별 그룹핑
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TODO 분석그래프</title> </head> <body> <div> <canvas id="myChart"></canvas> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGQyMzczMTA0Mzk4MmQ2YTBhOTkzYTYiLCJuYW1lIjoiaGVjYnoiLCJlbWFpbCI6Imx3dmJuZXhAZ21haWwuY29tIiwidXNlcklkIjoicGlldXhuZ3FiZCIsImlzQWRtaW4iOmZhbHNlLCJjcmVhdGVkQXQiOiIyMDIzLTA4LTA4VDEyOjM4OjA5LjY1MloiLCJpYXQiOjE2OTE1MDU5NTksImV4cCI6MTY5MTU5MjM1OSwiaXNzIjoic3VucmlzZSJ9.Rfv2hJsRM9uWwtpq7R3IpwEViHi7YYZsFz7GZguWq2A' const ctx = document.getElementById('myChart'); fetch('http://127.0.0.1:5000/api/todos/group/mine/isDone', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }) .then(res => res.json()) .then(data => { const group = data.docs console.log(group) // 차트 그리기 new Chart(ctx, { type: 'bar', data: { labels: group.map(item => item._id ? "종료": "진행중"), datasets: [{ label: '# of Todos By', data: group.map(item => item.count), borderWidth: 1, backgroundColor: '#FFB1C1', }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: true } } } }); }) </script> </body> </html>
chart.js - 특정 사용자의 할일종료 여부 그룹핑 * 특정 사용자의 TODO 생성날짜별 그룹핑
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TODO 분석그래프</title> </head> <body> <div> <canvas id="myChart"></canvas> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGQyMzczMTA0Mzk4MmQ2YTBhOTkzYTYiLCJuYW1lIjoiaGVjYnoiLCJlbWFpbCI6Imx3dmJuZXhAZ21haWwuY29tIiwidXNlcklkIjoicGlldXhuZ3FiZCIsImlzQWRtaW4iOmZhbHNlLCJjcmVhdGVkQXQiOiIyMDIzLTA4LTA4VDEyOjM4OjA5LjY1MloiLCJpYXQiOjE2OTE1MDU5NTksImV4cCI6MTY5MTU5MjM1OSwiaXNzIjoic3VucmlzZSJ9.Rfv2hJsRM9uWwtpq7R3IpwEViHi7YYZsFz7GZguWq2A' const ctx = document.getElementById('myChart'); fetch('http://127.0.0.1:5000/api/todos/group/mine/date/createdAt', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }) .then(res => res.json()) .then(data => { const group = data.docs console.log(group) // 차트 그리기 new Chart(ctx, { type: 'bar', data: { labels: group.map(item => `${item._id.year}년 ${item._id.month}월`), datasets: [{ label: '# of Todos', data: group.map(item => item.count), borderWidth: 1, backgroundColor: '#FFB1C1', }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: true } } } }); }) </script> </body> </html>
chart.js - 특정 사용자의 TODO 생성날짜별 그룹핑 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TODO 분석그래프</title> </head> <body> <div> <canvas id="myChart"></canvas> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGQyMzczMTA0Mzk4MmQ2YTBhOTkzYTYiLCJuYW1lIjoiaGVjYnoiLCJlbWFpbCI6Imx3dmJuZXhAZ21haWwuY29tIiwidXNlcklkIjoicGlldXhuZ3FiZCIsImlzQWRtaW4iOmZhbHNlLCJjcmVhdGVkQXQiOiIyMDIzLTA4LTA4VDEyOjM4OjA5LjY1MloiLCJpYXQiOjE2OTE1MDU5NTksImV4cCI6MTY5MTU5MjM1OSwiaXNzIjoic3VucmlzZSJ9.Rfv2hJsRM9uWwtpq7R3IpwEViHi7YYZsFz7GZguWq2A' const ctx = document.getElementById('myChart'); fetch('http://127.0.0.1:5000/api/todos/group/mine/date/lastModifiedAt', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }) .then(res => res.json()) .then(data => { const group = data.docs console.log(group) // 차트 그리기 new Chart(ctx, { type: 'bar', data: { labels: group.map(item => `${item._id.year}년 ${item._id.month}월`), datasets: [{ label: '# of Todos', data: group.map(item => item.count), borderWidth: 1, backgroundColor: '#FFB1C1', }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: true } } } }); }) </script> </body> </html>
특정 사용자의 TODO 업데이트 날짜별 그룹핑 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TODO 분석그래프</title> </head> <body> <div> <canvas id="myChart"></canvas> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGQyMzczMTA0Mzk4MmQ2YTBhOTkzYTYiLCJuYW1lIjoiaGVjYnoiLCJlbWFpbCI6Imx3dmJuZXhAZ21haWwuY29tIiwidXNlcklkIjoicGlldXhuZ3FiZCIsImlzQWRtaW4iOmZhbHNlLCJjcmVhdGVkQXQiOiIyMDIzLTA4LTA4VDEyOjM4OjA5LjY1MloiLCJpYXQiOjE2OTE1MDU5NTksImV4cCI6MTY5MTU5MjM1OSwiaXNzIjoic3VucmlzZSJ9.Rfv2hJsRM9uWwtpq7R3IpwEViHi7YYZsFz7GZguWq2A' const ctx = document.getElementById('myChart'); fetch('http://127.0.0.1:5000/api/todos/group/mine/date/finishedAt', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }) .then(res => res.json()) .then(data => { const group = data.docs console.log(group) // 차트 그리기 new Chart(ctx, { type: 'bar', data: { labels: group.map(item => `${item._id.year}년 ${item._id.month}월`), datasets: [{ label: '# of Todos', data: group.map(item => item.count), borderWidth: 1, backgroundColor: '#FFB1C1', }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: true } } } }); }) </script> </body> </html>
특정 사용자의 할일종료 날짜별 그룹핑 * 도넛 그래프 그리기
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TODO 분석그래프</title> </head> <body> <div> <canvas id="myChart"></canvas> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGNlMjVlNDc3ZjM5OTA4NjJkYTFhYTEiLCJuYW1lIjoi7YOc7JaRIiwiZW1haWwiOiJzdW5AZ21haWwuY29tIiwidXNlcklkIjoic3VucmlzZSIsImlzQWRtaW4iOnRydWUsImNyZWF0ZWRBdCI6IjIwMjMtMDgtMDVUMTA6MzU6MTYuNTU0WiIsImlhdCI6MTY5MTUxMjc3MywiZXhwIjoxNjkxNTk5MTczLCJpc3MiOiJzdW5yaXNlIn0.H9k4LaU0uKcfZ7UggBeSVLgmv_wSjbAS1YXXJs78arg' const ctx = document.getElementById('myChart'); fetch('http://127.0.0.1:5000/api/todos/group/date/finishedAt', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }) .then(res => res.json()) .then(data => { const group = data.docs console.log(group) // 차트 그리기 new Chart(ctx, { type: 'doughnut', data: { labels: group.map(item => `${item._id.year}년 ${item._id.month}일`), datasets: [{ label: '# of Todos', data: group.map(item => item.count), borderWidth: 1, backgroundColor: ['orange', 'purple', 'skyblue', 'green' ] }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: true } } } }); }) </script> </body> </html>
chart.js - 전체 할일목록 종료 날짜별 도넛 그래프 * 라인 그래프 그리기
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TODO 분석그래프</title> </head> <body> <div> <canvas id="myChart"></canvas> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NGNlMjVlNDc3ZjM5OTA4NjJkYTFhYTEiLCJuYW1lIjoi7YOc7JaRIiwiZW1haWwiOiJzdW5AZ21haWwuY29tIiwidXNlcklkIjoic3VucmlzZSIsImlzQWRtaW4iOnRydWUsImNyZWF0ZWRBdCI6IjIwMjMtMDgtMDVUMTA6MzU6MTYuNTU0WiIsImlhdCI6MTY5MTUxMjc3MywiZXhwIjoxNjkxNTk5MTczLCJpc3MiOiJzdW5yaXNlIn0.H9k4LaU0uKcfZ7UggBeSVLgmv_wSjbAS1YXXJs78arg' const ctx = document.getElementById('myChart'); fetch('http://127.0.0.1:5000/api/todos/group/date/finishedAt', { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }) .then(res => res.json()) .then(data => { const group = data.docs console.log(group) // 차트 그리기 new Chart(ctx, { type: 'line', data: { labels: group.map(item => `${item._id.year}년 ${item._id.month}일`), datasets: [{ label: '# of Todos', data: group.map(item => item.count), borderWidth: 1, backgroundColor: '#FFD700', borderColor: '#FFD700', }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: true } } } }); }) </script> </body> </html>
chart.js - 전체 할일목록 종료 날짜별 라인 그래프 * 년, 월 오름차순 정렬하기
server > src > routes > todos.js 파일의 해당 부분을 아래와 같이 수정한다.
router.get('/group/date/:field', isAuth, expressAsyncHandler(async (req, res, next) => { // 어드민 페이지 if(!req.user.isAdmin){ res.status(401).json({ code: 401, message: 'You are not authorized to use this service !'}) }else{ if(req.params.field === 'createdAt' || req.params.field === 'lastModifiedAt' || req.params.field === 'finishedAt'){ const docs = await Todo.aggregate([ { $group: { _id: { year: { $year: `$${req.params.field}` }, month: { $month: `$${req.params.field}` } }, count: { $sum: 1 } } }, { $sort : { _id : 1 } } // 날짜 오름차순 정렬 ]) console.log(`Number Of Group: ${docs.length}`) // 그룹 갯수 docs.sort((d1, d2) => d1._id - d2._id) res.json({ code: 200, docs}) }else{ res.status(204).json({ code: 204, message: 'No Content'}) } } }))
router.get('/group/mine/date/:field', isAuth, expressAsyncHandler(async (req, res, next) => { // 어드민 페이지 if(req.params.field === 'createdAt' || req.params.field === 'lastModifiedAt' || req.params.field === 'finishedAt'){ const docs = await Todo.aggregate([ { $match: { author: new ObjectId(req.user._id) } }, { $group: { _id: { year: { $year: `$${req.params.field}` }, month: { $month: `$${req.params.field}` } }, count: { $sum: 1 } } }, { $sort : { _id : 1 } } // 날짜 오름차순 정렬 ]) console.log(`Number Of Group: ${docs.length}`) // 그룹 갯수 docs.sort((d1, d2) => d1._id - d2._id) res.json({ code: 200, docs}) }else{ res.status(204).json({ code: 204, message: 'No Content'}) } }))
그룹핑 결과를 날짜별로 오름차순 정렬한다.
chart.js - 전체 할일목록 종료 날짜별 라인 그래프 (오름차순 정렬) * 프로그램적으로 로그인후 그룹핑하기
랜덤으로 생성한 사용자의 이메일, 비밀번호를 몽고 DB 데이터베이스에서 찾아서 로그인하도록 한다.
const BASE_URL = '127.0.0.1:5000' async function login(email, password){ const userJSON = await fetch(`http://${BASE_URL}/api/users/login`, { headers: { 'Content-Type': 'application/json' }, method: 'POST', body: JSON.stringify({ email, password }) }) const user = await userJSON.json() return user } async function getGroups(field, token){ const groupJSON = await fetch(`http://${BASE_URL}/api/todos/group/mine/${field}`, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, }) const group = await groupJSON.json() return group.docs } async function fetchData(){ const user = await login("enwrvuf@gmail.com", "ajtgnhhpesywh") const group = await getGroups("category", user.token) return group } fetchData() .then(group => { console.log(group) // 차트 그리기 const ctx = document.getElementById('myChart'); new Chart(ctx, { type: 'line', data: { labels: group.map(item => item._id), datasets: [{ label: '# of Todos', data: group.map(item => item.count), borderWidth: 1, backgroundColor: '#FFD700', borderColor: '#FFD700', }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: true } } } }); })
* 관리자 사용자로 전체목록 그룹핑하고 그래프로 보여주기
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div> <canvas id="myChart"></canvas> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> async function login(email, password){ const user = await fetch('http://127.0.0.1:5000/api/users/login', { headers: { 'Content-Type': 'application/json' }, method: 'POST', body: JSON.stringify({ email, password }) }).then(res => res.json()) return user } async function getGroups(field, token){ const group = await fetch(`http://127.0.0.1:5000/api/todos/group/${field}`, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }).then(res => res.json()) return group } async function fetchData(){ const user = await login("youna@gmail.com", "yona1234") // 로그인 let group = await getGroups('category', user.token) group = group.docs return group } fetchData() .then(group => { console.log(group) // 차트 그리기 const ctx = document.getElementById('myChart'); new Chart(ctx, { type: 'line', data: { labels: group.filter(item => item._id).map(item => item._id), datasets: [{ label: '# of Todos', data: group.filter(item => item._id).map(item => item.count), borderWidth: 1, backgroundColor: '#FFD700', borderColor: '#FFD700', }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: true } } } }); }) </script> </body> </html>
몽고디비에 10000개의 TODO 를 생성해서 전체 할일목록을 카테고리별로 그룹핑해보았다.
* 유니버셜 API 테스트 코드
const BASE_URL = 'http://127.0.0.1:5000' // 도메인 주소 const email = 'enwrvuf@gmail.com' // 사용자 연락처 const password = 'ajtgnhhpes123@' // 사용자 비밀번호 const graphType = 'line' // 그래프 종류 const field = 'createdAt' // 그룹핑 기준 async function login(email, password){ const userJSON = await fetch(`${BASE_URL}/api/users/login`, { headers: { 'Content-Type': 'application/json' }, method: 'POST', body: JSON.stringify({ email, password }) }) const user = await userJSON.json() return user } async function getGroups(field, user){ let base_url = `${BASE_URL}/api/todos/group` if(!user.isAdmin){ base_url += '/mine' } if(field === 'createdAt' || field === 'lastModifiedAt' || field === 'finishedAt'){ base_url += '/date' } const groupJSON = await fetch(`${base_url}/${field}`, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${user.token}` } }) const group = await groupJSON.json() return group.docs } async function fetchData(email, password, field){ const user = await login(email, password) // 로그인 const group = await getGroups(field, user) // 그룹핑 return group } function displayChart(type, group){ const ctx = document.getElementById('myChart') new Chart(ctx, { type, data: { labels: group .filter(item => item._id !== null && item._id !== undefined && item._id !== '') .map(item => item._id.year ? `${item._id.year}년 ${item._id.month}월` : typeof item._id === 'boolean' ? (item._id === true ? "종료" : "진행중") : item._id), datasets: [{ label: '# of Todos', data: group .filter(item => item._id !== null && item._id !== undefined && item._id !== '') .map(item => item.count), borderWidth: 1, backgroundColor: '#FFD700', borderColor: '#FFD700', }] }, options: { scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: true } } } }); } fetchData(email, password, field) .then(group => { console.log(group) displayChart(graphType, group) })
* 가로방향 그래프 그리기
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div> <canvas id="myChart"></canvas> </div> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> const email = "ycukkza@gmail.com" // 사용자 연락처 const password = "lvnqlofkwmddh" // 사용자 비밀번호 const graphType = 'bar' // 그래프 종류 const field = 'category' // 그룹핑 기준 async function login(email, password){ const user = await fetch('http://127.0.0.1:5000/api/users/login', { headers: { 'Content-Type': 'application/json' }, method: 'POST', body: JSON.stringify({ email, password }) }).then(res => res.json()) return user } async function getGroups(field, user){ let baseUrl = 'http://127.0.0.1:5000/api/todos/group' if(!user.isAdmin){ baseUrl += '/mine' } if(field === 'createdAt' || field === 'lastModifiedAt' || field === 'finishedAt'){ baseUrl += '/date' } const group = await fetch(`${baseUrl}/${field}`, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${user.token}` } }).then(res => res.json()) return group } async function fetchData(email, password, field){ const user = await login(email, password) // 로그인 let group = await getGroups(field, user) group = group.docs return group } function displayChart(type, group){ // 차트 그리기 const ctx = document.getElementById('myChart'); new Chart(ctx, { type, data: { labels: group.filter(item => item._id !== null && item._id !== undefined && item._id !== '').map(item => item._id.year ? `${item._id.year}년 ${item._id.month}월` : typeof item._id === 'boolean' ? (item._id === true ? "종료": "진행중") : item._id), datasets: [{ label: '# of Todos', data: group.filter(item => item._id !== null && item._id !== undefined && item._id !== '').map(item => item.count), borderWidth: 1, backgroundColor: '#FFD700', borderColor: '#FFD700', }] }, options: { indexAxis: 'y', // 가로방향 그래프 scales: { y: { beginAtZero: true } }, plugins: { colors: { enabled: true } } } }); } fetchData(email, password, field) .then(group => { console.log(group) displayChart(graphType, group) }) </script> </body> </html>
728x90'프로젝트 > 할일목록(TODO) 앱' 카테고리의 다른 글
할일목록(TODO) 앱 7 - Mongoose virtual 과 moment.js 로 현재 시각 기준으로 시간 표시하기 (0) 2023.08.12 할일목록(TODO) 앱 6 - 데이터 검증하기 (validation) (1) 2023.08.11 할일목록(TODO) 앱 4 - API 설계 및 구현 (0) 2021.10.05 할일목록(TODO) 앱 3 - Mongoose 로 데이터 모델 설계 및 구현하기 (0) 2021.10.05 할일목록(TODO) 앱 2 - 기본적인 서버 설정 및 Mongo DB 연동 (0) 2021.10.05