지난 01. ~ 05. 포스트에서의 환경세팅 및 백엔드 데이터 수집을 통해 차트를 그릴 수 있는 준비가 끝났습니다.
이번엔 본격적으로 차트를 그려보도록 하겠습니다.
차트 표현을 위해 Rechart
라는 라이브러리를 사용하였습니다.
Rechart는 React에서 사용할 수 있는 다양한 그래프 및 차트를 표현할 수 있는 라이브러리입니다.
Rechart BarChart 공식문서
Rechart에서 주식차트처럼 y방향으로의 범위 데이터를 그리는 BarChart를 그리기 위해서는, 아래와 같은 코드를 사용합니다.
<BarChart width={730} height={250} data={rangeData} >
<XAxis dataKey="day" />
<YAxis />
<Tooltip />
<Bar dataKey="temperature" fill="#8884d8" />
</BarChart>
그 중 rangeData 의 형식은 아래와 같습니다.
const data = [
{
"day": "05-01",
"temperature": [-1,10]
},
{
"day": "05-02",
"temperature": [2,15]
},
{
"day": "05-03",
"temperature": [3,12]
},
{
"day": "05-04",
"temperature": [4,12]
},
{
"day": "05-05",
"temperature": [12,16]
},
{
"day": "05-06",
"temperature": [5,16]
},
{
"day": "05-07",
"temperature": [3,12]
},
{
"day": "05-08",
"temperature": [0,8]
},
{
"day": "05-09",
"temperature": [-3,5]
}
]
위 예시는 x 좌표가 될 day
라는 key와, y방향의 value가 될 temperature
라는 key로 구성된 구조체로 이루어진 array 입니다.
이전 과정에서 주식차트를 그리기 위해서 아래와 같은 형식으로 백엔드에서 데이터를 가공하였습니다.
parseData =
[
...
{
"name" : "하이브보통주",
"date" : "20240304",
"openClose" : [192700,199200],
"highLow" : [192300,202000]
},
...
]
이 중, date는 x축의 data가 될 것이고,
openClose
(시가와 종가)는 차트 데이터가 될 것입니다. (highLow는 다음 단계에서 사용합니다.)
<BarChart width={1000} height={500} data={parseData} margin={{top: 20, right: 20, bottom: 20, left: 20}} >
<XAxis dataKey="date" />
<YAxis />
<CartesianGrid strokeDasharray="2 2" />
<Tooltip />
<Bar dataKey="openClose" fill="#8884d8" >
</Bar>
</BarChart>
일단 알맞게 데이터를 BarChart
컴포넌트에 넣어줍니다.
결과
실제 차트와 유사한 것은 맞지만, 아직 갈 길은 멀어 보입니다,,,
<BarChart width={1000} height={500} data={parseData} margin={{top: 20, right: 20, bottom: 20, left: 20}} >
<XAxis dataKey="date" />
<YAxis />
<CartesianGrid strokeDasharray="2 2" />
<Tooltip />
<Bar dataKey="openClose" fill="#8884d8">
{
parseData && parseData.map((entry, index) => (
<Cell key={`cell-${index}`}
fill={Number(entry.openClose[0]) > Number(entry.openClose[1]) ? '#0048ff' : '#c20404' }
/>
))
}
</Bar>
</BarChart>
구조체 안의 시가(openClose[0]
) 와 종가(openClose[1]
) 를 비교하여, 그 결과에 따라 색을 fill
옵션으로 지정해줍니다.
이는 ReChart
에서 제공하는 Cell
컴포넌트를 Bar
컴포넌트 내부에 넣어주어 설정할 수 있습니다.
parseData && parseData.map( ~~ )
위와 같이 표현하지 않으면,parseData
가 아직 불러와지지 않아 구조체가 비어 있을 경우, 웹에서 아래와 같은 오류가 날 수 있습니다.
AxiosError: Request failed with status code 500
결과
기본적인 틀은 완료되었는데, 중요한 고가와 저가 막대가 없으니 허전합니다.
<BarChart barGap={-440/dataNum} width={1000} height={500} data={parseData} margin={{top: 20, right: 20, bottom: 20, left: 20}} >
<XAxis dataKey="date" />
<YAxis />
<CartesianGrid strokeDasharray="2 2" />
<Tooltip />
<Bar dataKey="openClose" fill="#8884d8" barSize={800/dataNum}>
{
parseData && parseData.map((entry, index) => (
<Cell key={`cell-${index}`}
fill={Number(entry.openClose[0]) > Number(entry.openClose[1]) ? '#0048ff' : '#c20404' }
/>
))
}
</Bar>
<Bar dataKey="highLow" fill="#8884d8" barSize={80/dataNum}>
{
parseData && parseData.map((entry, index) => (
<Cell key={`cell-${index}`}
fill={Number(entry.openClose[0]) > Number(entry.openClose[1]) ? '#0048ff' : '#c20404' }
/>
))
}
</Bar>
</BarChart>
두개의 BarChart를 겹치는 방식을 사용하였습니다.
이번에는 highLow
(고가와 저가) 를 dataKey
로 사용하는 차트를 그렸습니다.
그리고 두 개의 Bar 모두 barSize
를 data의 개수를 나타내는 dataNum
에 반비례하게 설정하여 차트 조회 기간 변경에 따른 bar 폭 변경에 대응할 수 있도록 수정하였습니다.
시가/종가의 봉 그래프와 고가/저가의 꼬리 그래프의 폭을 10배 차이를 주는 방식으로 꼬리를 표현하였습니다.
또한, 꼬리의 위치는 BarChart
컴포넌트의 옵션인 barGap
으로 계산하여 표현했습니다.
결과
y축 범위의 수정은 필요해보입니다.
// 변경 전
<YAxis/>
// 변경 후
<YAxis type="number" domain={[minPrice-(maxPrice-minPrice)*0.1, maxPrice+(maxPrice-minPrice)*0.1]}/>
// 참고
maxPrice = Math.max.apply(null,parseData.map((e)=> e?parseInt(e.highLow[0]):0));
minPrice = Math.min.apply(null,parseData.map((e)=> e?parseInt(e.highLow[1]):0));
dataNum = parseData.length;
YAxis의 domain 옵션을 사용하고, 위아래 10프로 여유를 두며 domain을 설정하여 차트를 완성합니다.
결과
차트 디자인은 종료인 듯 합니다.
<div>
{"차트 기간 : "}
{DAYLIST.map((k) => (
<button className = {k}
style={{marginRight: '10px', marginBottom: '10px', width: '60px',
background : 'white'}}
onClick={changeDay}>
{k}
</button>
))}
</div>
<div className="select" style={{width:"1000px"}}>
<Select
placeholder="종목을 선택하세요"
options={apiListData?JSON.parse(apiListData) : ItemJsonList} // 종목 정보가 로드가 안되어 있을 땐 임시 파일 사용
onChange={handleItemId}
/>
</div>
Select
boxSelect
react-select
라는 컴포넌트를 사용하였습니다.
import Select from "react-select";
데이터는 역시 공공데이터포탈 API 를 사용했습니다.
금융위원회_KRX상장종목정보
이 또한 공공데이터포탈 API 호출 딜레이가 걱정이 되어 local에 2024년 버전의 주식종목정보 json 데이터(ItemJsonList
)를 따로 두어 에러에 대비하였습니다.
시가와 종가 같아 range bar 그래프를 그릴 수 없는 캔들은 어찌 표현할 방도가 없었습니다.
쉽게 표현할 수 있는 방법을 떠올리지 못하여 그냥 두게 되었는데, 이게 계속 눈에 밟히고 아쉬웠습니다.