diff --git a/FE/src/components/StocksDetail/Chart.tsx b/FE/src/components/StocksDetail/Chart.tsx index 91e3b8ab..439e9ee4 100644 --- a/FE/src/components/StocksDetail/Chart.tsx +++ b/FE/src/components/StocksDetail/Chart.tsx @@ -1,21 +1,34 @@ import { useEffect, useRef, useState } from 'react'; -import { dummy } from './dummy'; import { TiemCategory } from 'types'; import { drawBarChart, drawCandleChart, drawLineChart } from 'utils/chart'; +import { useQuery } from '@tanstack/react-query'; +import { getStocksChartDataByCode } from 'service/stocks'; -export default function Chart() { +const categories: { label: string; value: TiemCategory }[] = [ + { label: '일', value: 'D' }, + { label: '주', value: 'W' }, + { label: '월', value: 'M' }, + { label: '년', value: 'Y' }, +]; + +type StocksDeatailChartProps = { + code: string; +}; + +export default function Chart({ code }: StocksDeatailChartProps) { const containerRef = useRef(null); const canvasRef = useRef(null); const [timeCategory, setTimeCategory] = useState('D'); - const categories: { label: string; value: TiemCategory }[] = [ - { label: '일', value: 'D' }, - { label: '주', value: 'W' }, - { label: '월', value: 'M' }, - { label: '년', value: 'Y' }, - ]; + const { data, isLoading } = useQuery( + ['stocksChartData', code, timeCategory], + () => getStocksChartDataByCode(code, timeCategory), + ); useEffect(() => { + if (isLoading) return; + if (!data) return; + const parent = containerRef.current; const canvas = canvasRef.current; @@ -49,12 +62,22 @@ export default function Chart() { const chartHeight = canvas.height - padding.top - padding.bottom; const boundary = chartHeight * 0.8; // chartHeight의 80% - const data = dummy.map((e) => e.low); + const reverseData = data.reverse(); + + const arr = reverseData.map((e) => +e.stck_oprc); - drawLineChart(ctx, data, 0, 0, chartWidth, boundary, padding, 0.1); - drawBarChart(ctx, dummy, 0, boundary, chartWidth, chartHeight, padding); - drawCandleChart(ctx, dummy, 0, 0, chartWidth, boundary, padding, 0.1); - }, []); + drawLineChart(ctx, arr, 0, 0, chartWidth, boundary, padding, 0.1); + drawBarChart( + ctx, + reverseData, + 0, + boundary, + chartWidth, + chartHeight, + padding, + ); + drawCandleChart(ctx, reverseData, 0, 0, chartWidth, boundary, padding, 0.1); + }, [timeCategory, data, isLoading]); return (
+ getStocksByCode(code), + ); + + if (isLoading) return; + if (!data) return; + + const { stck_prpr, prdy_vrss, prdy_vrss_sign, prdy_ctrt, hts_avls, per } = + data; + + const stockInfo: { label: string; value: string }[] = [ + { label: '시총', value: `${Number(hts_avls).toLocaleString()}억원` }, + { label: 'PER', value: `${per}배` }, + ]; + + const colorStyleBySign = + prdy_vrss_sign === '3' + ? '' + : prdy_vrss_sign < '3' + ? 'text-juga-red-60' + : 'text-juga-blue-40'; + return (
-

삼성전자

-

005930

+

{'empty'}

+

{code}

-

60,900원

+

{Number(stck_prpr).toLocaleString()}원

어제보다

-

+1800원 (3.0%)

+

+ +{Number(prdy_vrss).toLocaleString()}원 ({prdy_ctrt}%) +

-
-

당기순이익

-

9조 8,143억

-
-
-

영업이익

-

10조 4,439억

-
-
-

매출액

-

74조 683억

-
-
-

시총

-

361조 1,718억

-
-
-

PER

-

14.79배

-
+ {stockInfo.map((e, idx) => ( +
+

{e.label}

+

{e.value}

+
+ ))}
); diff --git a/FE/src/page/StocksDetail.tsx b/FE/src/page/StocksDetail.tsx index ff88dca2..5ad96d4d 100644 --- a/FE/src/page/StocksDetail.tsx +++ b/FE/src/page/StocksDetail.tsx @@ -2,14 +2,20 @@ import Chart from 'components/StocksDetail/Chart'; import Header from 'components/StocksDetail/Header'; import PriceSection from 'components/StocksDetail/PriceSection'; import TradeSection from 'components/StocksDetail/TradeSection'; +import { useParams } from 'react-router-dom'; export default function StocksDetail() { + const params = useParams(); + const { id } = params; + + if (!id) return; + return (
-
+
- +
diff --git a/FE/src/service/stocks.ts b/FE/src/service/stocks.ts new file mode 100644 index 00000000..d135fb72 --- /dev/null +++ b/FE/src/service/stocks.ts @@ -0,0 +1,24 @@ +import { StockChartUnit, StockDetailType, TiemCategory } from 'types'; + +export async function getStocksByCode(code: string): Promise { + return fetch(`${import.meta.env.VITE_API_URL}/stocks/${code}`).then((res) => + res.json(), + ); +} + +export async function getStocksChartDataByCode( + code: string, + peroid: TiemCategory = 'D', + start: string = '', + end: string = '', +): Promise { + return fetch(`${import.meta.env.VITE_API_URL}/stocks/${code}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + fid_input_date_1: start, + fid_input_date_2: end, + fid_period_div_code: peroid, + }), + }).then((res) => res.json()); +} diff --git a/FE/src/types.ts b/FE/src/types.ts index 8577b090..c6160fb4 100644 --- a/FE/src/types.ts +++ b/FE/src/types.ts @@ -16,3 +16,24 @@ export type Padding = { right: number; bottom: number; }; + +export type StockDetailType = { + hts_kor_isnm: string; + stck_shrn_iscd: string; + stck_prpr: string; + prdy_vrss: string; + prdy_vrss_sign: string; + prdy_ctrt: string; + hts_avls: string; + per: string; +}; + +export type StockChartUnit = { + stck_bsop_date: string; + stck_clpr: string; + stck_oprc: string; + stck_hgpr: string; + stck_lwpr: string; + acml_vol: string; + prdy_vrss_sign: string; +}; diff --git a/FE/src/utils/chart.ts b/FE/src/utils/chart.ts index 3e582aed..04db6061 100644 --- a/FE/src/utils/chart.ts +++ b/FE/src/utils/chart.ts @@ -1,5 +1,4 @@ -import { DummyStock } from 'components/StocksDetail/dummy'; -import { Padding } from 'types'; +import { Padding, StockChartUnit } from 'types'; export function drawLineChart( ctx: CanvasRenderingContext2D, @@ -37,7 +36,7 @@ export function drawLineChart( export function drawBarChart( ctx: CanvasRenderingContext2D, - data: DummyStock[], + data: StockChartUnit[], x: number, y: number, width: number, @@ -50,8 +49,12 @@ export function drawBarChart( ctx.beginPath(); - const yMax = Math.round(Math.max(...data.map((d) => d.volume)) * 1 + weight); - const yMin = Math.round(Math.min(...data.map((d) => d.volume)) * 1 - weight); + const yMax = Math.round( + Math.max(...data.map((d) => +d.acml_vol)) * 1 + weight, + ); + const yMin = Math.round( + Math.min(...data.map((d) => +d.acml_vol)) * 1 - weight, + ); const gap = Math.floor((width / n) * 0.8); @@ -60,9 +63,10 @@ export function drawBarChart( data.forEach((e, i) => { const cx = x + padding.left + (width * i) / (n - 1); - const cy = padding.top + ((height - y) * (e.volume - yMin)) / (yMax - yMin); + const cy = + padding.top + ((height - y) * (+e.acml_vol - yMin)) / (yMax - yMin); - ctx.fillStyle = e.open < e.close ? red : blue; + ctx.fillStyle = +e.stck_oprc < +e.stck_clpr ? red : blue; ctx.fillRect(cx, height, gap, -cy); }); @@ -71,7 +75,7 @@ export function drawBarChart( export function drawCandleChart( ctx: CanvasRenderingContext2D, - data: DummyStock[], + data: StockChartUnit[], x: number, y: number, width: number, @@ -83,15 +87,13 @@ export function drawCandleChart( const n = data.length; - const yMax = Math.round( - Math.max(...data.map((d) => Math.max(d.close, d.open, d.high, d.low))) * - (1 + weight), - ); - const yMin = Math.round( - Math.min(...data.map((d) => Math.max(d.close, d.open, d.high, d.low))) * - (1 - weight), + const arr = data.map((d) => + Math.max(+d.stck_clpr, +d.stck_oprc, +d.stck_hgpr, +d.stck_lwpr), ); + const yMax = Math.round(Math.max(...arr) * (1 + weight)); + const yMin = Math.round(Math.min(...arr) * (1 - weight)); + const labels = getYAxisLabels(yMin, yMax); labels.forEach((label) => { const yPos = @@ -114,23 +116,23 @@ export function drawCandleChart( data.forEach((e, i) => { ctx.beginPath(); - const { open, close, high, low } = e; + const { stck_oprc, stck_clpr, stck_hgpr, stck_lwpr } = e; const gap = Math.floor((width / n) * 0.8); const cx = x + padding.left + (width * i) / (n - 1); const openY = - y + padding.top + height - (height * (open - yMin)) / (yMax - yMin); + y + padding.top + height - (height * (+stck_oprc - yMin)) / (yMax - yMin); const closeY = - y + padding.top + height - (height * (close - yMin)) / (yMax - yMin); + y + padding.top + height - (height * (+stck_clpr - yMin)) / (yMax - yMin); const highY = - y + padding.top + height - (height * (high - yMin)) / (yMax - yMin); + y + padding.top + height - (height * (+stck_hgpr - yMin)) / (yMax - yMin); const lowY = - y + padding.top + height - (height * (low - yMin)) / (yMax - yMin); + y + padding.top + height - (height * (+stck_lwpr - yMin)) / (yMax - yMin); const blue = '#2175F3'; const red = '#FF3700'; - if (open > close) { + if (+stck_oprc > +stck_clpr) { ctx.fillStyle = blue; ctx.strokeStyle = blue; ctx.fillRect(cx, closeY, gap, openY - closeY);