미니프로젝트 개요
- 추석 연휴동안 개인적으로 진행하였고, 코인원 암호화폐 거래 사이트의 웹디자인을 모티브로 하여 레이아웃을 구성하였습니다.
- 최소한의 목표는 올해 초에 배웠던 데이터 분석
python
을 활용하여javascript
에 적용해보고 그래프를 구현하는 것이었습니다.
데이터 출저
kaggle S&P 500 stock data
- 캐글에서 5년간의 S&P 지수 데이터를 받았고, 15-16년도 부분만 잘라내서 활용하였습니다.
- csv 파일을 가공하여 json 형식으로 변환하였고, mockData 처럼 직접적인 호출 방식으로 사용하였습니다.
import React, { useState, useEffect } from "react";
import styled from "styled-components";
import "./Main.scss";
const Main = () => {
// useState(초기값) = [초기값, 값을 통제하는 함수]
// 비구조화 할당 (배열, 객체)
const [value, setValue] = useState({
id: 0,
number: 0,
});
const { id, number } = value;
// 이렇게 썼지만 useState로 모든 변수를 한번에 관리하는 것보다
// 변하는 값끼리 묶어서
// state 값을 갱신하는 함수
// 기존값(...value)에 변하는 key, value 값을 새롭게!!! 추가한다.
// 이런 식으로 해야 변화를 인식할 수 있다고 한다.
// useState 다룰 때에는 두번째 파라미터로 콜백함수를 넣을 수 없다고 한다.
const plusNum = () => {
setValue({
...value,
number: number + 1,
});
};
// render 직후에 실행되는데 콜백함수로 [] 넣어주면 처음에만 실행된다(=컴디마)
useEffect(() => {
console.log("렌더링!");
}, []);
// render 직후에 실행되는데 콜백함수로 [변수] 넣어주면
// 해당 변수가 변했을 때에만 실행된다(=컴디업, 비동기처리)
useEffect(() => {
console.log(number);
}, [number]);
useEffect(() => {
console.log(id);
}, [id]);
return (
<Flex>
<div className="Main">
<Border>
<div>Main</div>
<div>
<span>{number}만큼 담대하게</span>
</div>
<button name={id} onClick={plusNum}>
담대버튼
</button>
</Border>
</div>
</Flex>
);
};
export default Main;
state
값에 따라 css
속성을 변화시킬 때 props
기능을 활용할 수 있다.import React, { useEffect, useState } from "react";
import styled from "styled-components";
import menuData from "./menuData";
import "./Nav.scss";
// 네이게이션바 js
const Nav = () => {
const [menu, changeMenu] = useState(menuData);
const { menuContents, menuValid } = menu;
const clickMenu = (idx) => {
let handleMenuDeatail = menuValid;
handleMenuDeatail[idx] = !handleMenuDeatail[idx];
changeMenu({
...menu,
menuValid: handleMenuDeatail,
});
};
return (
...
// 'menu' state값내에 있는 menuDetail과 menuValid를 props로 전달
<MenuDetailBox
menuDatil={menuDetail}
validIdx={menuValid[idx]}
>
<ul className="menuDetail">
{menuDetail.map((menuDetail) => {
return <li>{menuDetail}</li>;
})}
</ul>
</MenuDetailBox>
...
);
};
// props 활용하여 조건문을 생성할 수 있다
const MenuDetailBox = styled.ul`
display: ${(props) => (props.menuDatil.length === 0 ? "none" : null)};
border: 1px solid lightgray;
border-radius: 10px;
padding: 20px;
background-color: white;
opacity: ${(props) => (props.validIdx ? 1 : 0)};
li {
padding: 10px;
&:hover {
cursor: ${(props) => props.validIdx && "pointer"};
background-color: rgb(229, 249, 255);
}
}
`;
export default Nav;
import pandas as pd
# 먼저 csv파일을 불러온다.
df = pd.read_csv('c:/data/all_stocks_5yr.csv')
# 쓰지 않을 데이터를 잘라내준다.
df.drop(['open', 'high', 'low', 'volume'], axis='columns', inplace=True)
# 15-16년도 데이터만 남긴다.
idx = df[(df['date'] < '2015-01-01') | (df['date'] >='2017-01-01')].index
re_df = df.drop(idx)
re_df.reset_index(drop=True, inplace=True) # 리인덱싱
re_df.drop(['date'], axis='columns', inplace=True) # 날짜 데이터는 js에서 넣어준다.
re_df = re_df.rename({"close":"value"}, axis="columns") # 컬럼 이름 변경
# 종목 이름을 기준으로 주가 데이터를 배열로 묶어준다.
re_df_binding = re_df.groupby('Name')['value'].apply(list)
re_df_binding_df = pd.DataFrame(re_df_binding)
# json 파일로 저장해준다.
re_df_binding_df.to_json("c:/data/stockSeriesData.json", orient="table")
{
"schema": {
"fields":[
{"name":"Name","type":"string"},
{"name":"value","type":"string"}],
"primaryKey":["Name"],
"pandas_version":"0.20.0"},
"data": [
{
"Name":"A",
"value":[40.56,39.8,39.18,39.7,40.89,40.59,40.11,39.55,39.06,38.01,38.25,37.93,38.16,
39.65,38.81,39.15,38.75,38.0,38.46,37.77,38.69,39.62,39.11,39.53,39.34,39.04,
39.67,39.33,40.02,40.15,40.52,41.54,41.15,41.95,41.73,42.06,42.2,42.36,42.21,
42.7,42.26,42.0,42.22,41.53,41.74,40.63,40.85,41.1,40.87,41.81,41.58,42.12,
42.21,42.21,42.2,41.09,40.81,40.7,41.11,41.72,41.55,41.39,42.05,41.93,42.44,
...]
},
{
"Name":"ATS",
"value":[...]
},
{
"Name":"ZWS",
"value":[...]
},
...]
}
import
되어 그래프를 만드는 컴포넌트// Chart.js
import React, { Component } from "react";
import Highcharts from "highcharts";
class Chart extends Component {
constructor(props) {
super(props);
this.chartContainer = React.createRef();
}
componentDidMount() {
this.chart = new Highcharts[this.props.type || "Chart"](
this.chartContainer.current,
this.props.options
);
}
// 그래프 데이터를 갱신할 때에는 state 관리하는 것 이외에 componentDidUpdate 함수를 선언해주어야 한다.
componentDidUpdate() {
if (this.props.allowChartUpdate !== false) {
this.chart.update(
this.props.options,
...(this.props.updateArgs || [true, true])
);
}
}
componentWillUnmount() {
this.chart.destroy();
}
render() {
return <div ref={this.chartContainer} />;
}
}
export default Chart;
// Graph.js
import React, { useEffect, useState } from "react";
import styled from "styled-components";
import Chart from "./Chart";
import "./Graph.scss";
const Graph = () => {
// 인덱스 값이 바뀜에 따라 데이터가 변하도록 세팅하였다.
const [chartData, updateData] = useState({});
const [stockIdx, plusIdx] = useState(1);
// 데이터의 형식을 가져온다.
const ClickEvent = () => {
fetch("/data/stockData.json")
// 아래 주석이 stockData.json 하이차트에서 인식하는 데이터의 형식이다.
// {
// "stock": [
// {
// "title": { "text": "S&P 500 stock data (15'~16`)" },
// "xAxis": {
// "type": "datetime",
// "dateTimeLabelFormats": {
// "day": "%d %b"
// },
// "labels": {
// "enabled": true
// }
// },
// "chart": {
// "type": "line"
// },
// "series": []
// }
// ]
// }
.then((res) => res.json())
.then((res) => {
updateData(res.stock[0]);
});
};
// 주가 데이터 내용을 가져온다.
const getStockData = (idx) => {
console.log("success");
fetch("/data/stockSeriesData.json")
.then((res) => res.json())
.then((res) => {
updateData({
...chartData,
// pointStart : x축 날짜의 시작점을 지정해준다.
// pointInterval : 데이터간의 간격이 얼마만큼인지 설정해준다(데이터에 영향을 주지 않고 x축 표기에 영향을 준다).
series: [
{
data: res.data[idx].value,
pointStart: Date.UTC(2015, 1, 1),
pointInterval: 24 * 3600 * 1000 * 1,
name: res.data[idx].Name,
},
],
});
});
};
const changeData = () => {
plusIdx(stockIdx + 1);
getStockData(stockIdx);
};
useEffect(() => {
ClickEvent();
getStockData(0);
}, []);
useEffect(() => {
console.log(chartData);
});
return (
<div className="Graph">
<GraphContainer>
<div className="graphContainer">
// 하이차트 들어갈 위치
<Chart options={chartData} />
</div>
</GraphContainer>
<Container>
<div className="stocksContainer">
<button onClick={changeData}>ChageStockData</button>
</div>
</Container>
</div>
);
};
export default Graph;