월별 비용을 그래픽적으로 볼 수 있는 차트를 만들어 보자.
먼저 새로운 Chart 폴더를 만들어 차트와 관련된 컴포넌트를 생성해보자.
1. 앱에 추가할 수 있는 <Chart />
컴포넌트, 차트 안에 1~12월에 각각 생성되는 <ChartBar />
컴포넌트를 만든다.
<Chart />
컴포넌트 안에서 <ChartBar />
컴포넌트를 렌더링한다.filteredExpenses를 ExpenseChart 컴포넌트로 보내준다.
import Card from "../UI/Card";
import "./Expenses.css";
import ExpensesFilter from "./ExpensesFilter";
import { useState } from "react";
import ExpensesList from "./ExpensesList";
import ExpenseChart from "./ExpenseChart";
const Expenses = (props) => {
const [filteredYear, setFilteredYear] = useState("2020");
const filterChangedYearHandler = (selectedYear) => {
setFilteredYear(selectedYear);
};
const filteredExpenses = props.items.filter(
(expense) => expense.date.getFullYear().toString() === filteredYear
);
return (
<Card className="expenses">
<ExpensesFilter
onChangeFilterYear={filterChangedYearHandler}
selected={filteredYear}
/>
//🔥
<ExpenseChart filteredExpenses={filteredExpenses} />
{<ExpensesList filteredExpenses={filteredExpenses} />}
</Card>
);
};
export default Expenses;
차트를 반환하는 <ExpenseChart />
컴포넌트를 만들어 보자.
import Chart from "../Chart/Chart";
//필터링된 비용을 props으로 가져온다.
const ExpenseChart = (props) => {
//ChartDataPoint는 배열로 1~12월에 대한 지출비용을 담는다.
const ChartDataPoint = [
{ label: "1월", value: 0 },
{ label: "2월", value: 0 },
{ label: "3월", value: 0 },
{ label: "4월", value: 0 },
{ label: "5월", value: 0 },
{ label: "6월", value: 0 },
{ label: "7월", value: 0 },
{ label: "8월", value: 0 },
{ label: "9월", value: 0 },
{ label: "10월", value: 0 },
{ label: "11월", value: 0 },
{ label: "12월", value: 0 },
];
//props으로 받아온 필터링된 비용을 for 반복문을 사용하여 props에서 얻은 모든 비용을 반복 실행
//해당 비용이 지출된 달을 가져와서 비용 금액에 따라 dataPoints 값 업데이트
//of: 배열 / in: 객체
for (const expense of props.filteredExpenses) {
//month는 0부터 시작하기 때문에 1월은 0임
const expenseMonth = expense.date.getMonth();
//ChartDataPoint 배열의 인덱스로 month 값을 사용하자: ChartDataPoint[0] => 1월에 지출한 비용에 대한 객체
//숏컷 연산자로 선택된 데이터 포인트에 대한 값을 업데이트할 수 있다.
ChartDataPoint[expenseMonth].value += expense.amount;
//그런다음 모든 비용을 검토해서, 각 달의 모든 비용을 합산한다.
//그리고 해당하는 달, 해당하는 dataPoints에 값을 할당한다.
//반복문이 끝나면 value:0 으로 초기에 설정했던 값은 각 월에 지출된 비용을 합산한 값으로 업데이트 되어 있다.
//Chart에 dataPoints props으로 업데이트된 ChartDataPoint를 보낸다.
}
return <Chart dataPoints={ChartDataPoint} />;
};
export default ExpenseChart;
import "./Chart.css";
import ChartBar from "./ChartBar";
const Chart = (props) => {
/*
유동적으로 차트 컴포넌트를 활용하기 위해서 Chart 컴포넌트가 앱 어딘가에서 사용될 때, 그려야하는 데이터 포인트를 props으로 받도록 만들자.
데이터 포인트를 통해 차트 바가 동적으로 출력되도록, 모든 데이터 포인트를 차트 바에 매핑하면 된다.
ExpenseChart에서 props으로 받아오는 dataPoints는 배열이다.
각각의 데이터 포인트를 ChartBar 컴포넌트에 매핑하면 가지고 있는 dataPoints 만큼 ChartBar 컴포넌트를 생성할 수 있다.
2. 모든 차트의 바는 전체 차트의 최대값을 기준으로 값을 표시해야 한다.
따라서 속성으로 maxValue도 전달해야 한다.
*/
//객체에서 숫자로 변환하기
//map()사용하여 객체 안에 든 value 값만 가져오는 배열 새로 만들어 주기
const dataPointValues = props.dataPoints.map((dataPoint) => dataPoint.value);
//전체 달 중에 가장 큰 비용이 지출된 달의 값을 찾아서 maxValue에 넣어주자.
//max()는 쉽표로 구분되는 인자로 숫자를 받음, 그런데 내가 가진건 숫자 배열이 아닌 객체 배열임
//map으로 value만으로 이루어진 숫자 배열로 만들었지만 여전히 배열임!
//스프레드 연산자를 사용하여 배열의 모든 요소를 가져와서 max 메소드에 독립적인 인자로 추가하자.
//이렇게 하면 12개의 인자를 갖개된다.
const totalMaximum = Math.max(...dataPointValues);
return (
<div className="chart">
{props.dataPoints.map((dataPoint) => (
<ChartBar
key={dataPoint.label}
value={dataPoint.value}
maxValue={totalMaximum}
label={dataPoint.label}
/>
))}
</div>
);
};
export default Chart;
import "./ChartBar.css";
const ChartBar = (props) => {
//bar가 얼마나 fill되어야 할지 계산되는 변수
//초기값은 "0%"로 준다. 왜냐하면 css의 height 값에 들어가야 하기 때문!
let barFillHeight = "0%";
//최대값(최대 지출 비용)이 0보다 큰지 확인
//참고: 지출이 0인 달은 props.maxValue === 0
if (props.maxValue > 0) {
//1~100% 사이로 바가 채워질 퍼센티지를 가까운 정수로 반올림한 수에 %를 추가한다.
barFillHeight = Math.round((props.value / props.maxValue) * 100) + "%";
}
return (
<div className="chart-bar">
<div className="chart-bar__inner">
{/*스타일 동적으로 설정하기
동적으로 출력하기 위해 이중 중괄호 구문이 아닌 단일 중괄호 사용해야 함
여기서는 객체를 보내기 때문에 이중 중괄호 처럼 보이는 것*/}
<div
className="chart-bar__fill"
style={{ height: barFillHeight }}
></div>
</div>
<div className="chart-bar__label">{props.label}</div>
</div>
);
};
export default ChartBar;