From 35f4dd789153e40353d8055d8cb64643ed178549 Mon Sep 17 00:00:00 2001 From: Seo San Date: Sun, 1 Dec 2024 16:27:29 +0900 Subject: [PATCH 01/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20?= =?UTF-8?q?=EC=B0=A8=ED=8A=B8=20=EC=B5=9C=EC=A0=81=ED=99=94=20&=20?= =?UTF-8?q?=EB=A6=AC=ED=8E=99=ED=86=A0=EB=A7=81=20#192=20=EC=B0=A8?= =?UTF-8?q?=ED=8A=B8=EC=97=90=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20beg?= =?UTF-8?q?inpath=EC=A0=9C=EA=B1=B0,=20fill=EA=B3=BC=20stroke=20=EA=B0=99?= =?UTF-8?q?=EC=9D=80=20=EB=A1=9C=EC=A7=81=EC=9D=84=20=EB=A7=88=EC=A7=80?= =?UTF-8?q?=EB=A7=89=EC=97=90=20=ED=95=9C=EB=B2=88=EB=A7=8C=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=EC=B5=9C=EC=A0=81=ED=99=94=20=EC=A7=84=ED=96=89.?= =?UTF-8?q?=20=EB=B0=98=EB=B3=B5=EB=90=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/utils/chart/drawCandleChart.ts | 39 +++---------------------- FE/src/utils/chart/drawChartGrid.ts | 9 ------ FE/src/utils/chart/drawLineChart.ts | 34 ++------------------- FE/src/utils/chart/drawUpperYAxis.ts | 37 ++--------------------- FE/src/utils/chart/makeChartDataFlat.ts | 36 +++++++++++++++++++++++ 5 files changed, 44 insertions(+), 111 deletions(-) create mode 100644 FE/src/utils/chart/makeChartDataFlat.ts diff --git a/FE/src/utils/chart/drawCandleChart.ts b/FE/src/utils/chart/drawCandleChart.ts index ea5c3ff4..a602f5fd 100644 --- a/FE/src/utils/chart/drawCandleChart.ts +++ b/FE/src/utils/chart/drawCandleChart.ts @@ -1,4 +1,5 @@ import { Padding, StockChartUnit } from '../../types.ts'; +import { makeChartDataFlat } from './makeChartDataFlat.ts'; export function drawCandleChart( ctx: CanvasRenderingContext2D, @@ -12,40 +13,11 @@ export function drawCandleChart( ) { const n = data.length; - const values = data - .map((d) => { - if (d.mov_avg_20 && d.mov_avg_5) { - return [ - +d.stck_hgpr, - +d.stck_lwpr, - +d.stck_clpr, - +d.stck_oprc, - Math.floor(+d.mov_avg_5), - Math.floor(+d.mov_avg_20), - ]; - } else if (d.mov_avg_5) { - return [ - +d.stck_hgpr, - +d.stck_lwpr, - +d.stck_clpr, - +d.stck_oprc, - Math.floor(+d.mov_avg_5), - ]; - } else if (d.mov_avg_20) { - return [ - +d.stck_hgpr, - +d.stck_lwpr, - +d.stck_clpr, - +d.stck_oprc, - Math.floor(+d.mov_avg_20), - ]; - } else { - return [+d.stck_hgpr, +d.stck_lwpr, +d.stck_clpr, +d.stck_oprc]; - } - }) - .flat(); + const values = makeChartDataFlat(data); const yMax = Math.round(Math.max(...values) * (1 + weight)); const yMin = Math.round(Math.min(...values) * (1 - weight)); + const blue = '#2175F3'; + const red = '#FF3700'; data.forEach((e, i) => { ctx.beginPath(); @@ -63,9 +35,6 @@ export function drawCandleChart( const lowY = y + padding.top + height - (height * (+stck_lwpr - yMin)) / (yMax - yMin); - const blue = '#2175F3'; - const red = '#FF3700'; - if (+stck_oprc > +stck_clpr) { ctx.fillStyle = blue; ctx.strokeStyle = blue; diff --git a/FE/src/utils/chart/drawChartGrid.ts b/FE/src/utils/chart/drawChartGrid.ts index 2d91399f..f246d069 100644 --- a/FE/src/utils/chart/drawChartGrid.ts +++ b/FE/src/utils/chart/drawChartGrid.ts @@ -37,20 +37,12 @@ export const drawChartGrid = ( ); } }); - lowerChartCtx.strokeStyle = '#D2DAE0'; - lowerChartCtx.lineWidth = 1; - lowerChartCtx.stroke(); - upperChartCtx.strokeStyle = '#D2DAE0'; - upperChartCtx.lineWidth = 1; - upperChartCtx.stroke(); const volumes = data.map((d) => +d.acml_vol); const volumeMax = Math.round(Math.max(...volumes) * 1.2); const volumeMin = Math.round(Math.min(...volumes) * 0.8); const lowerLabels = makeYLabels(volumeMax, volumeMin, lowerLabelsNum); - lowerChartCtx.beginPath(); - lowerLabels.forEach((label) => { const valueRatio = (label - volumeMin) / (volumeMax - volumeMin); const yPos = lowerChartHeight - valueRatio * lowerChartHeight; @@ -79,7 +71,6 @@ export const drawChartGrid = ( const valueMin = Math.round(Math.min(...values) * (1 - 0.1)); const upperLabels = makeYLabels(valueMax, valueMin, upperLabelsNum); - upperChartCtx.beginPath(); upperLabels.forEach((label) => { const yPos = padding.top + diff --git a/FE/src/utils/chart/drawLineChart.ts b/FE/src/utils/chart/drawLineChart.ts index b44abccf..554868b0 100644 --- a/FE/src/utils/chart/drawLineChart.ts +++ b/FE/src/utils/chart/drawLineChart.ts @@ -1,4 +1,5 @@ import { Padding, StockChartUnit } from '../../types.ts'; +import { makeChartDataFlat } from './makeChartDataFlat.ts'; export function drawLineChart( ctx: CanvasRenderingContext2D, @@ -18,38 +19,7 @@ export function drawLineChart( const n = data.length; const gap = Math.floor(width / n); - const values = data - .map((d) => { - if (d.mov_avg_20 && d.mov_avg_5) { - return [ - +d.stck_hgpr, - +d.stck_lwpr, - +d.stck_clpr, - +d.stck_oprc, - Math.floor(+d.mov_avg_5), - Math.floor(+d.mov_avg_20), - ]; - } else if (d.mov_avg_5) { - return [ - +d.stck_hgpr, - +d.stck_lwpr, - +d.stck_clpr, - +d.stck_oprc, - Math.floor(+d.mov_avg_5), - ]; - } else if (d.mov_avg_20) { - return [ - +d.stck_hgpr, - +d.stck_lwpr, - +d.stck_clpr, - +d.stck_oprc, - Math.floor(+d.mov_avg_20), - ]; - } else { - return [+d.stck_hgpr, +d.stck_lwpr, +d.stck_clpr, +d.stck_oprc]; - } - }) - .flat(); + const values = makeChartDataFlat(data); const yMax = Math.round(Math.max(...values) * (1 + weight)); const yMin = Math.round(Math.min(...values) * (1 - weight)); diff --git a/FE/src/utils/chart/drawUpperYAxis.ts b/FE/src/utils/chart/drawUpperYAxis.ts index 769a7e0f..37c11043 100644 --- a/FE/src/utils/chart/drawUpperYAxis.ts +++ b/FE/src/utils/chart/drawUpperYAxis.ts @@ -1,6 +1,7 @@ import { Padding, StockChartUnit } from '../../types.ts'; import { makeYLabels } from './makeLabels.ts'; import { MousePositionType } from '../../components/StocksDetail/Chart.tsx'; +import { makeChartDataFlat } from './makeChartDataFlat.ts'; export const drawUpperYAxis = ( ctx: CanvasRenderingContext2D, @@ -14,41 +15,7 @@ export const drawUpperYAxis = ( upperChartWidth: number, upperChartHeight: number, ) => { - const values = data - .map((d) => { - - if (d.mov_avg_20 && d.mov_avg_5) { - return [ - +d.stck_hgpr, - +d.stck_lwpr, - +d.stck_clpr, - +d.stck_oprc, - Math.floor(+d.mov_avg_5), - Math.floor(+d.mov_avg_20), - ]; - - } else if (d.mov_avg_5) { - return [ - +d.stck_hgpr, - +d.stck_lwpr, - +d.stck_clpr, - +d.stck_oprc, - Math.floor(+d.mov_avg_5), - ]; - - } else if (d.mov_avg_20) { - return [ - +d.stck_hgpr, - +d.stck_lwpr, - +d.stck_clpr, - +d.stck_oprc, - Math.floor(+d.mov_avg_20), - ]; - } else { - return [+d.stck_hgpr, +d.stck_lwpr, +d.stck_clpr, +d.stck_oprc]; - } - }) - .flat(); + const values = makeChartDataFlat(data); const yMax = Math.round(Math.max(...values) * (1 + weight)); const yMin = Math.round(Math.min(...values) * (1 - weight)); ctx.clearRect( diff --git a/FE/src/utils/chart/makeChartDataFlat.ts b/FE/src/utils/chart/makeChartDataFlat.ts new file mode 100644 index 00000000..10eb13c8 --- /dev/null +++ b/FE/src/utils/chart/makeChartDataFlat.ts @@ -0,0 +1,36 @@ +import { StockChartUnit } from '../../types.ts'; + +export const makeChartDataFlat = (data: StockChartUnit[]) => { + return data + .map((d) => { + if (d.mov_avg_20 && d.mov_avg_5) { + return [ + +d.stck_hgpr, + +d.stck_lwpr, + +d.stck_clpr, + +d.stck_oprc, + Math.floor(+d.mov_avg_5), + Math.floor(+d.mov_avg_20), + ]; + } else if (d.mov_avg_5) { + return [ + +d.stck_hgpr, + +d.stck_lwpr, + +d.stck_clpr, + +d.stck_oprc, + Math.floor(+d.mov_avg_5), + ]; + } else if (d.mov_avg_20) { + return [ + +d.stck_hgpr, + +d.stck_lwpr, + +d.stck_clpr, + +d.stck_oprc, + Math.floor(+d.mov_avg_20), + ]; + } else { + return [+d.stck_hgpr, +d.stck_lwpr, +d.stck_clpr, +d.stck_oprc]; + } + }) + .flat(); +}; From 1884f2fbde01ac942581f16ef1c30f9ccb37e7b1 Mon Sep 17 00:00:00 2001 From: Seo San Date: Sun, 1 Dec 2024 18:11:25 +0900 Subject: [PATCH 02/34] =?UTF-8?q?=E2=9C=A8=20feat:=20=ED=99=95=EB=8C=80=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20#192?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/components/StocksDetail/Chart.tsx | 75 +++++++++++++++++++++--- FE/src/utils/chart/drawXAxis.ts | 2 +- 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/FE/src/components/StocksDetail/Chart.tsx b/FE/src/components/StocksDetail/Chart.tsx index 84b26203..3823f165 100644 --- a/FE/src/components/StocksDetail/Chart.tsx +++ b/FE/src/components/StocksDetail/Chart.tsx @@ -1,4 +1,11 @@ -import { MouseEvent, useCallback, useEffect, useRef, useState } from 'react'; +import { + MouseEvent, + useCallback, + useEffect, + useRef, + useState, + WheelEvent, +} from 'react'; import { ChartSizeConfigType, Padding, @@ -65,6 +72,11 @@ export default function Chart({ code }: StocksDeatailChartProps) { y: 0, }); const [mouseIndex, setMouseIndex] = useState(null); + const [dataRange, setDataRange] = useState({ + start: 0, + end: 0, + }); + const minDisplayData = 20; const { data, isLoading } = useQuery( ['stocksChartData', code, timeCategory], @@ -178,7 +190,7 @@ export default function Chart({ code }: StocksDeatailChartProps) { const UpperYCtx = upperChartYCanvas.getContext('2d'); const LowerYCtx = lowerChartYCanvas.getContext('2d'); const ChartXCtx = chartXCanvas.getContext('2d'); - + const displayData = chartData.slice(dataRange.start, dataRange.end + 1); if ( !UpperChartCtx || !LowerChartCtx || @@ -197,14 +209,14 @@ export default function Chart({ code }: StocksDeatailChartProps) { lowerChartCanvas.width - padding.left - padding.right, lowerChartCanvas.height - padding.top - padding.bottom, lowerLabelNum, - chartData, + displayData, padding, ); if (moveAverageToggle) { drawLineChart( UpperChartCtx, - chartData, + displayData, 0, 0, upperChartCanvas.width - padding.left - padding.right, @@ -216,7 +228,7 @@ export default function Chart({ code }: StocksDeatailChartProps) { drawCandleChart( UpperChartCtx, - chartData, + displayData, 0, 0, upperChartCanvas.width - padding.left - padding.right, @@ -227,7 +239,7 @@ export default function Chart({ code }: StocksDeatailChartProps) { drawBarChart( LowerChartCtx, - chartData, + displayData, lowerChartCanvas.width - padding.left - padding.right, lowerChartCanvas.height - padding.top - padding.bottom, padding, @@ -235,7 +247,7 @@ export default function Chart({ code }: StocksDeatailChartProps) { drawUpperYAxis( UpperYCtx, - chartData, + displayData, upperChartYCanvas.width - padding.left - padding.right, upperChartYCanvas.height - padding.top - padding.bottom, upperLabelNum, @@ -248,7 +260,7 @@ export default function Chart({ code }: StocksDeatailChartProps) { drawLowerYAxis( LowerYCtx, - chartData, + displayData, lowerChartYCanvas.width - padding.left - padding.right, lowerChartYCanvas.height - padding.top - padding.bottom, lowerLabelNum, @@ -261,7 +273,7 @@ export default function Chart({ code }: StocksDeatailChartProps) { drawXAxis( ChartXCtx, - chartData, + displayData, chartXCanvas.width - padding.left - padding.right, chartXCanvas.height, padding, @@ -300,8 +312,52 @@ export default function Chart({ code }: StocksDeatailChartProps) { drawLowerYAxis, drawXAxis, moveAverageToggle, + dataRange, ], ); + const handleWheel = useCallback( + (e: WheelEvent) => { + if (!data) return; + + const wheelPower = 0.1; + const curRange = dataRange.end - dataRange.start; + + // 축소 + if (e.deltaY > 0) { + // 늘어나야함 & 데이터 최대 갯수보다 작아야함. + const newRange = Math.min(curRange * (1 + wheelPower), data.length); + const newStart = Math.max(data.length - newRange, 0); + + setDataRange({ + start: newStart, + end: data.length - 1, + }); + } + + // 확대 + if (e.deltaY < 0) { + // 줄어야함 & 최소 데이터 갯수보단 커야함. + const newRange = Math.max(curRange * (1 - wheelPower), minDisplayData); + const newStart = Math.max(data.length - newRange, 0); + + setDataRange({ + start: newStart, + end: data.length - 1, + }); + } + }, + [data, dataRange], + ); + + useEffect(() => { + if (data) { + setDataRange((prev) => ({ + ...prev, + start: 0, + end: data.length - 1, + })); + } + }, [data]); useEffect(() => { if (isLoading || !data) return; @@ -382,6 +438,7 @@ export default function Chart({ code }: StocksDeatailChartProps) { ref={containerRef} className='mt-2 flex h-[200px] w-full flex-col' onMouseMove={getCanvasMousePosition} + onWheel={handleWheel} > {/* Upper 차트 영역 */}
diff --git a/FE/src/utils/chart/drawXAxis.ts b/FE/src/utils/chart/drawXAxis.ts index fca31589..2ec7f975 100644 --- a/FE/src/utils/chart/drawXAxis.ts +++ b/FE/src/utils/chart/drawXAxis.ts @@ -30,7 +30,7 @@ export const drawXAxis = ( const barWidth = Math.floor(width / data.length); data.forEach((item, i) => { - if (labels.includes(item.stck_bsop_date) || i === data.length - 1) { + if (labels.includes(item.stck_bsop_date)) { ctx.fillText( formatTime(item.stck_bsop_date), padding.left + (width * i) / (data.length - 1) + barWidth / 2, From 334a054ef554ec98b9b5aa762a3a773c91cc9f7a Mon Sep 17 00:00:00 2001 From: Seo San Date: Mon, 2 Dec 2024 11:14:06 +0900 Subject: [PATCH 03/34] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=B0=A8=ED=8A=B8=20?= =?UTF-8?q?=EA=B3=A0=EB=8F=84=ED=99=94=20#192=20Y=EC=B6=95=20=EB=9D=BC?= =?UTF-8?q?=EB=B2=A8=20=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95.=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=ED=8F=89=EA=B7=A0=EC=84=A0=20NaN=20?= =?UTF-8?q?=ED=91=9C=EA=B8=B0=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/components/StocksDetail/Chart.tsx | 49 +++++++++++++++--------- FE/src/utils/chart/drawLowerYAxis.ts | 8 +++- FE/src/utils/chart/drawUpperYAxis.ts | 8 +++- 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/FE/src/components/StocksDetail/Chart.tsx b/FE/src/components/StocksDetail/Chart.tsx index 3823f165..ca702a04 100644 --- a/FE/src/components/StocksDetail/Chart.tsx +++ b/FE/src/components/StocksDetail/Chart.tsx @@ -476,24 +476,37 @@ export default function Chart({ code }: StocksDeatailChartProps) {
이동평균선
- 5 - {mouseIndex !== null && data ? ( - - {Math.floor( - Number(data[mouseIndex].mov_avg_5), - ).toLocaleString()} - 원 - - ) : null} - 20 - {mouseIndex !== null && data ? ( - - {Math.floor( - Number(data[mouseIndex].mov_avg_20), - ).toLocaleString()} - 원 - - ) : null} + {mouseIndex !== null ? ( + data && !isNaN(Number(data[mouseIndex].mov_avg_5)) ? ( + <> + 5 + + {Math.floor( + Number(data[mouseIndex].mov_avg_5), + ).toLocaleString()} + 원 + + + ) : null + ) : ( + 5 + )} + + {mouseIndex !== null ? ( + data && !isNaN(Number(data[mouseIndex].mov_avg_20)) ? ( + <> + 20 + + {Math.floor( + Number(data[mouseIndex].mov_avg_20), + ).toLocaleString()} + 원 + + + ) : null + ) : ( + 20 + )}
diff --git a/FE/src/utils/chart/drawLowerYAxis.ts b/FE/src/utils/chart/drawLowerYAxis.ts index 65fcf9b4..f26318e9 100644 --- a/FE/src/utils/chart/drawLowerYAxis.ts +++ b/FE/src/utils/chart/drawLowerYAxis.ts @@ -35,7 +35,11 @@ export const drawLowerYAxis = ( const valueRatio = (label - yMin) / (yMax - yMin); const yPos = height - valueRatio * height; const formattedValue = formatNumber(label); - ctx.fillText(formattedValue, width / 2 + padding.left, yPos + padding.top); + ctx.fillText( + formattedValue, + width / 2 + padding.left / 2, + yPos + padding.top, + ); }); if ( @@ -58,7 +62,7 @@ export const drawLowerYAxis = ( const textWidth = ctx.measureText(valueText).width; ctx.fillStyle = '#2175F3'; - const boxX = width / 2 + padding.left - 12; + const boxX = width / 2 + padding.left / 2 - 12; const boxY = mousePosition.y - upperChartHeight - padding.bottom - boxHeight / 2; diff --git a/FE/src/utils/chart/drawUpperYAxis.ts b/FE/src/utils/chart/drawUpperYAxis.ts index 37c11043..21131126 100644 --- a/FE/src/utils/chart/drawUpperYAxis.ts +++ b/FE/src/utils/chart/drawUpperYAxis.ts @@ -36,7 +36,11 @@ export const drawUpperYAxis = ( const valueRatio = (label - yMin) / (yMax - yMin); const yPos = height - valueRatio * height; const formattedValue = label.toLocaleString(); - ctx.fillText(formattedValue, width / 2 + padding.left, yPos + padding.top); + ctx.fillText( + formattedValue, + width / 2 + padding.left / 2, + yPos + padding.top, + ); }); if ( @@ -58,7 +62,7 @@ export const drawUpperYAxis = ( const textWidth = ctx.measureText(valueText).width; ctx.fillStyle = '#2175F3'; - const boxX = width / 2 + padding.left - 12; + const boxX = width / 2 + padding.left / 2 - 12; const boxY = mousePosition.y - boxHeight / 2; ctx.fillRect(boxX, boxY, textWidth + boxPadding * 2, boxHeight); From 7ed037d471435affdac0536530623c33c3ccaa57 Mon Sep 17 00:00:00 2001 From: Seo San Date: Mon, 2 Dec 2024 11:20:42 +0900 Subject: [PATCH 04/34] =?UTF-8?q?=F0=9F=92=84=20design:=20Top5=ED=95=AD?= =?UTF-8?q?=EB=AA=A9=EC=97=90=20localScale=20=EC=B6=94=EA=B0=80.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/components/TopFive/Card.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FE/src/components/TopFive/Card.tsx b/FE/src/components/TopFive/Card.tsx index de7a31ce..d9905d21 100644 --- a/FE/src/components/TopFive/Card.tsx +++ b/FE/src/components/TopFive/Card.tsx @@ -50,7 +50,7 @@ export default function Card({

{plusOrMinus} - {Math.abs(Number(changePrice))}({percentAbsolute} + {Math.abs(Number(changePrice)).toLocaleString()}({percentAbsolute} %)

From 6b3de6dcd8e83228113b5c70c1fe7c89588a2ae7 Mon Sep 17 00:00:00 2001 From: Seo San Date: Mon, 2 Dec 2024 11:21:15 +0900 Subject: [PATCH 05/34] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20chore:=20react-helme?= =?UTF-8?q?t=20=EC=B6=94=EA=B0=80.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/package-lock.json | 63 +++++++++++++++++++++++++++++++++++++++----- FE/package.json | 1 + 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/FE/package-lock.json b/FE/package-lock.json index 1bc3638e..76198c20 100644 --- a/FE/package-lock.json +++ b/FE/package-lock.json @@ -14,6 +14,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-error-boundary": "^4.1.2", + "react-helmet": "^6.1.0", "react-router-dom": "^6.27.0", "react-toastify": "^10.0.6", "socket.io-client": "^4.8.1", @@ -823,10 +824,11 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", - "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", + "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "levn": "^0.4.1" }, @@ -1973,10 +1975,11 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2975,7 +2978,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3393,6 +3395,17 @@ } } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3456,6 +3469,33 @@ "react": ">=16.13.1" } }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" + }, + "node_modules/react-helmet": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", + "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.1.1", + "react-side-effect": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.3.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -3495,6 +3535,15 @@ "react-dom": ">=16.8" } }, + "node_modules/react-side-effect": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", + "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-toastify": { "version": "10.0.6", "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.6.tgz", diff --git a/FE/package.json b/FE/package.json index dd0e90c4..cd6f9135 100644 --- a/FE/package.json +++ b/FE/package.json @@ -16,6 +16,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-error-boundary": "^4.1.2", + "react-helmet": "^6.1.0", "react-router-dom": "^6.27.0", "react-toastify": "^10.0.6", "socket.io-client": "^4.8.1", From c5be573fdce75a3b3a262b2ed99b02f4cdaa2550 Mon Sep 17 00:00:00 2001 From: Seo San Date: Mon, 2 Dec 2024 15:46:44 +0900 Subject: [PATCH 06/34] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=B0=A8=ED=8A=B8=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=BA=90=EC=8B=B1=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?#241?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/components/StocksDetail/Chart.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FE/src/components/StocksDetail/Chart.tsx b/FE/src/components/StocksDetail/Chart.tsx index ca702a04..81557067 100644 --- a/FE/src/components/StocksDetail/Chart.tsx +++ b/FE/src/components/StocksDetail/Chart.tsx @@ -81,7 +81,7 @@ export default function Chart({ code }: StocksDeatailChartProps) { const { data, isLoading } = useQuery( ['stocksChartData', code, timeCategory], () => getStocksChartDataByCode(code, timeCategory), - { staleTime: 1000 }, + { staleTime: 1000 * 60 }, ); const handleMouseDown = useCallback((e: MouseEvent) => { From f3a37a6892bd0313d9487343cfc71bceeb929c19 Mon Sep 17 00:00:00 2001 From: Seo San Date: Mon, 2 Dec 2024 16:35:00 +0900 Subject: [PATCH 07/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=20=EA=B5=AC=EC=A1=B0=20=EC=A0=95=EB=A6=AC=20?= =?UTF-8?q?#241=20type=20=ED=8C=8C=EC=9D=BC=20=EA=B4=80=EB=A6=AC.=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EB=AA=85=20=EC=88=98=EC=A0=95.=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/components/Login/type.ts | 9 ++++ FE/src/components/Mypage/Nav.tsx | 4 +- FE/src/components/Mypage/type.ts | 1 + FE/src/components/News/Card.tsx | 2 +- FE/src/components/News/News.tsx | 2 +- FE/src/components/News/newsMockData.ts | 46 ------------------- .../News/{NewsDataType.ts => type.ts} | 0 .../Rank/{RankCard.tsx => Card.tsx} | 4 +- .../Rank/{RankList.tsx => List.tsx} | 10 ++-- .../components/Rank/{RankType.ts => type.ts} | 0 FE/src/components/Search/SearchCard.tsx | 2 +- .../components/Search/SearchCardHighlight.tsx | 2 +- .../components/Search/SearchHistoryList.tsx | 2 +- FE/src/components/Search/SearchList.tsx | 2 +- FE/src/components/Search/index.tsx | 4 +- .../Search/{searchDataType.ts => type.ts} | 0 FE/src/components/StockIndex/Card.tsx | 13 +++--- FE/src/components/StockIndex/index.tsx | 2 +- FE/src/components/StockIndex/type.ts | 15 ++++++ FE/src/components/StocksDetail/Chart.tsx | 8 +--- .../TableColumn.tsx} | 2 +- .../TableDayCard.tsx} | 6 +-- .../TableLiveCard.tsx} | 4 +- .../index.tsx} | 17 +++---- .../type.ts} | 0 .../StocksDetail/PriceSectionSseHook.ts | 28 ----------- FE/src/components/StocksDetail/type.ts | 7 +++ .../TopFive/{TopFive.tsx => index.tsx} | 14 ++---- FE/src/components/TopFive/type.ts | 13 ------ FE/src/constants.ts | 7 +++ .../Search => hooks}/searchHistoryHook.ts | 2 +- FE/src/{utils => hooks}/useDebounce.ts | 0 FE/src/page/Home.tsx | 4 +- FE/src/page/Rank.tsx | 6 +-- FE/src/page/StocksDetail.tsx | 4 +- FE/src/types.ts | 10 ---- 36 files changed, 93 insertions(+), 159 deletions(-) create mode 100644 FE/src/components/Login/type.ts create mode 100644 FE/src/components/Mypage/type.ts delete mode 100644 FE/src/components/News/newsMockData.ts rename FE/src/components/News/{NewsDataType.ts => type.ts} (100%) rename FE/src/components/Rank/{RankCard.tsx => Card.tsx} (91%) rename FE/src/components/Rank/{RankList.tsx => List.tsx} (83%) rename FE/src/components/Rank/{RankType.ts => type.ts} (100%) rename FE/src/components/Search/{searchDataType.ts => type.ts} (100%) create mode 100644 FE/src/components/StockIndex/type.ts rename FE/src/components/StocksDetail/{PriceTableColumn.tsx => PriceSection/TableColumn.tsx} (95%) rename FE/src/components/StocksDetail/{PriceTableDayCard.tsx => PriceSection/TableDayCard.tsx} (86%) rename FE/src/components/StocksDetail/{PriceTableLiveCard.tsx => PriceSection/TableLiveCard.tsx} (89%) rename FE/src/components/StocksDetail/{PriceSection.tsx => PriceSection/index.tsx} (90%) rename FE/src/components/StocksDetail/{PriceDataType.ts => PriceSection/type.ts} (100%) delete mode 100644 FE/src/components/StocksDetail/PriceSectionSseHook.ts create mode 100644 FE/src/components/StocksDetail/type.ts rename FE/src/components/TopFive/{TopFive.tsx => index.tsx} (82%) create mode 100644 FE/src/constants.ts rename FE/src/{components/Search => hooks}/searchHistoryHook.ts (95%) rename FE/src/{utils => hooks}/useDebounce.ts (100%) diff --git a/FE/src/components/Login/type.ts b/FE/src/components/Login/type.ts new file mode 100644 index 00000000..06b1b10b --- /dev/null +++ b/FE/src/components/Login/type.ts @@ -0,0 +1,9 @@ +export type LoginSuccessResponse = { + accessToken: string; +}; + +export type LoginFailResponse = { + error: string; + message: string[]; + statusCode: number; +}; diff --git a/FE/src/components/Mypage/Nav.tsx b/FE/src/components/Mypage/Nav.tsx index 1c94434c..ea56ae57 100644 --- a/FE/src/components/Mypage/Nav.tsx +++ b/FE/src/components/Mypage/Nav.tsx @@ -1,5 +1,5 @@ import { useSearchParams } from 'react-router-dom'; -import { MypageSectionType } from 'types'; +import { MypageSectionType } from './type.ts'; const mapping = { account: '보유 자산 현황', @@ -18,7 +18,7 @@ export default function Nav() { }; return ( -
+
{sections.map((e, idx) => (
) : null} diff --git a/FE/src/components/Rank/RankType.ts b/FE/src/components/Rank/type.ts similarity index 100% rename from FE/src/components/Rank/RankType.ts rename to FE/src/components/Rank/type.ts diff --git a/FE/src/components/Search/SearchCard.tsx b/FE/src/components/Search/SearchCard.tsx index 860f09f4..24633522 100644 --- a/FE/src/components/Search/SearchCard.tsx +++ b/FE/src/components/Search/SearchCard.tsx @@ -1,8 +1,8 @@ -import { SearchDataType } from './searchDataType.ts'; import { useNavigate } from 'react-router-dom'; import useSearchModalStore from 'store/useSearchModalStore.ts'; import useSearchInputStore from 'store/useSearchInputStore.ts'; import { SearchCardHighLight } from './SearchCardHighlight.tsx'; +import { SearchDataType } from './type.ts'; type SearchCardProps = { data: SearchDataType; diff --git a/FE/src/components/Search/SearchCardHighlight.tsx b/FE/src/components/Search/SearchCardHighlight.tsx index 8c543443..f107717f 100644 --- a/FE/src/components/Search/SearchCardHighlight.tsx +++ b/FE/src/components/Search/SearchCardHighlight.tsx @@ -1,4 +1,4 @@ -import { formatNoSpecialChar } from '../../utils/formatNoSpecialChar.ts'; +import { formatNoSpecialChar } from 'utils/formatNoSpecialChar.ts'; type SearchCardHighLightProps = { text: string; diff --git a/FE/src/components/Search/SearchHistoryList.tsx b/FE/src/components/Search/SearchHistoryList.tsx index caa1d7df..3a64ca75 100644 --- a/FE/src/components/Search/SearchHistoryList.tsx +++ b/FE/src/components/Search/SearchHistoryList.tsx @@ -1,5 +1,5 @@ import { SearchHistoryItem } from './SearchHistoryItem.tsx'; -import { HistoryType } from './searchDataType.ts'; +import { HistoryType } from './type.ts'; type SearchHistoryListProps = { searchHistory: HistoryType[]; diff --git a/FE/src/components/Search/SearchList.tsx b/FE/src/components/Search/SearchList.tsx index 89c0adf6..f3e06475 100644 --- a/FE/src/components/Search/SearchList.tsx +++ b/FE/src/components/Search/SearchList.tsx @@ -1,7 +1,7 @@ import SearchCard from './SearchCard.tsx'; -import { SearchDataType } from './searchDataType.ts'; import Lottie from 'lottie-react'; import noResultAnimation from 'assets/noResultAnimation.json'; +import { SearchDataType } from './type.ts'; type SearchListProps = { searchData: SearchDataType[]; diff --git a/FE/src/components/Search/index.tsx b/FE/src/components/Search/index.tsx index e9a543c4..eaf4308a 100644 --- a/FE/src/components/Search/index.tsx +++ b/FE/src/components/Search/index.tsx @@ -5,12 +5,12 @@ import { SearchInput } from './SearchInput'; import { SearchHistoryList } from './SearchHistoryList'; import SearchList from './SearchList.tsx'; import useSearchInputStore from 'store/useSearchInputStore.ts'; -import { useDebounce } from 'utils/useDebounce.ts'; +import { useDebounce } from 'hooks/useDebounce.ts'; import { useQuery } from '@tanstack/react-query'; import { getSearchResults } from 'service/getSearchResults.ts'; import Lottie from 'lottie-react'; import searchAnimation from 'assets/searchAnimation.json'; -import { useSearchHistory } from './searchHistoryHook.ts'; +import { useSearchHistory } from '../../hooks/searchHistoryHook.ts'; import { formatNoSpecialChar } from '../../utils/formatNoSpecialChar.ts'; export default function SearchModal() { diff --git a/FE/src/components/Search/searchDataType.ts b/FE/src/components/Search/type.ts similarity index 100% rename from FE/src/components/Search/searchDataType.ts rename to FE/src/components/Search/type.ts diff --git a/FE/src/components/StockIndex/Card.tsx b/FE/src/components/StockIndex/Card.tsx index bd1e5dce..9b49511d 100644 --- a/FE/src/components/StockIndex/Card.tsx +++ b/FE/src/components/StockIndex/Card.tsx @@ -1,17 +1,16 @@ +import { useEffect, useRef, useState } from 'react'; +import { socket } from 'utils/socket.ts'; +import { drawChart } from 'utils/chart/drawChart.ts'; import { ChartData, + MarketType, StockIndexData, StockIndexValue, -} from 'components/TopFive/type'; -import { useEffect, useRef, useState } from 'react'; -import { socket } from 'utils/socket.ts'; -import { drawChart } from 'utils/chart/drawChart.ts'; - -// const X_LENGTH = 79; +} from './type.ts'; type StockIndexChartProps = { name: string; - id: 'KOSPI' | 'KOSDAQ' | 'KOSPI200' | 'KSQ150'; + id: MarketType; initialData: StockIndexData; }; diff --git a/FE/src/components/StockIndex/index.tsx b/FE/src/components/StockIndex/index.tsx index 6b0adb1e..5e0253a6 100644 --- a/FE/src/components/StockIndex/index.tsx +++ b/FE/src/components/StockIndex/index.tsx @@ -1,6 +1,6 @@ import { Card } from './Card.tsx'; import { useQuery } from '@tanstack/react-query'; -import { getStockIndex } from '../../service/getStockIndex.ts'; +import { getStockIndex } from 'service/getStockIndex.ts'; export default function StockIndex() { const { data, isLoading, isError } = useQuery({ diff --git a/FE/src/components/StockIndex/type.ts b/FE/src/components/StockIndex/type.ts new file mode 100644 index 00000000..77a10c4c --- /dev/null +++ b/FE/src/components/StockIndex/type.ts @@ -0,0 +1,15 @@ +export type ChartData = { time: string; value: string; diff: string }; + +export type StockIndexValue = { + curr_value: string; + diff: string; + diff_rate: string; + sign: string; +}; + +export type StockIndexData = { + chart: ChartData[]; + value: StockIndexValue; +}; + +export type MarketType = 'KOSPI' | 'KOSDAQ' | 'KOSPI200' | 'KSQ150'; diff --git a/FE/src/components/StocksDetail/Chart.tsx b/FE/src/components/StocksDetail/Chart.tsx index 81557067..502fe3a1 100644 --- a/FE/src/components/StocksDetail/Chart.tsx +++ b/FE/src/components/StocksDetail/Chart.tsx @@ -6,12 +6,7 @@ import { useState, WheelEvent, } from 'react'; -import { - ChartSizeConfigType, - Padding, - StockChartUnit, - TiemCategory, -} from 'types'; +import { Padding, StockChartUnit, TiemCategory } from 'types'; import { useQuery } from '@tanstack/react-query'; import { getStocksChartDataByCode } from 'service/stocks'; import { drawLineChart } from 'utils/chart/drawLineChart.ts'; @@ -23,6 +18,7 @@ import { drawLowerYAxis } from 'utils/chart/drawLowerYAxis.ts'; import { drawChartGrid } from 'utils/chart/drawChartGrid.ts'; import { drawMouseGrid } from 'utils/chart/drawMouseGrid.ts'; import { EyeIcon, EyeSlashIcon } from '@heroicons/react/16/solid'; +import { ChartSizeConfigType } from './type.ts'; const categories: { label: string; value: TiemCategory }[] = [ { label: '일', value: 'D' }, diff --git a/FE/src/components/StocksDetail/PriceTableColumn.tsx b/FE/src/components/StocksDetail/PriceSection/TableColumn.tsx similarity index 95% rename from FE/src/components/StocksDetail/PriceTableColumn.tsx rename to FE/src/components/StocksDetail/PriceSection/TableColumn.tsx index 8c548ba4..ddc64a50 100644 --- a/FE/src/components/StocksDetail/PriceTableColumn.tsx +++ b/FE/src/components/StocksDetail/PriceSection/TableColumn.tsx @@ -1,7 +1,7 @@ type Props = { viewMode: boolean; }; -export default function PriceTableColumn({ viewMode }: Props) { +export default function TableColumn({ viewMode }: Props) { if (!viewMode) { return ( diff --git a/FE/src/components/StocksDetail/PriceTableDayCard.tsx b/FE/src/components/StocksDetail/PriceSection/TableDayCard.tsx similarity index 86% rename from FE/src/components/StocksDetail/PriceTableDayCard.tsx rename to FE/src/components/StocksDetail/PriceSection/TableDayCard.tsx index 62bde95a..28d2dca0 100644 --- a/FE/src/components/StocksDetail/PriceTableDayCard.tsx +++ b/FE/src/components/StocksDetail/PriceSection/TableDayCard.tsx @@ -1,11 +1,11 @@ -import { DailyPriceDataType } from './PriceDataType.ts'; -import { formatTime } from '../../utils/formatTime.ts'; +import { formatTime } from 'utils/formatTime.ts'; +import { DailyPriceDataType } from './type.ts'; type PriceTableDayCardProps = { data: DailyPriceDataType; }; -export default function PriceTableDayCard({ data }: PriceTableDayCardProps) { +export default function TableDayCard({ data }: PriceTableDayCardProps) { const color = data.prdy_vrss_sign === '3' ? '' diff --git a/FE/src/components/StocksDetail/PriceTableLiveCard.tsx b/FE/src/components/StocksDetail/PriceSection/TableLiveCard.tsx similarity index 89% rename from FE/src/components/StocksDetail/PriceTableLiveCard.tsx rename to FE/src/components/StocksDetail/PriceSection/TableLiveCard.tsx index 9ccb6a2c..b1e44d0c 100644 --- a/FE/src/components/StocksDetail/PriceTableLiveCard.tsx +++ b/FE/src/components/StocksDetail/PriceSection/TableLiveCard.tsx @@ -1,9 +1,9 @@ -import { PriceDataType } from './PriceDataType.ts'; +import { PriceDataType } from './type.ts'; type PriceTableLiveCardProps = { data: PriceDataType; }; -export default function PriceTableLiveCard({ data }: PriceTableLiveCardProps) { +export default function TableLiveCard({ data }: PriceTableLiveCardProps) { const color = data.prdy_vrss_sign === '3' ? '' diff --git a/FE/src/components/StocksDetail/PriceSection.tsx b/FE/src/components/StocksDetail/PriceSection/index.tsx similarity index 90% rename from FE/src/components/StocksDetail/PriceSection.tsx rename to FE/src/components/StocksDetail/PriceSection/index.tsx index 3bbb13c4..844385ba 100644 --- a/FE/src/components/StocksDetail/PriceSection.tsx +++ b/FE/src/components/StocksDetail/PriceSection/index.tsx @@ -1,14 +1,15 @@ import { useCallback, useEffect, useRef, useState } from 'react'; -import PriceTableColumn from './PriceTableColumn.tsx'; -import PriceTableLiveCard from './PriceTableLiveCard.tsx'; -import PriceTableDayCard from './PriceTableDayCard.tsx'; +import TableColumn from './TableColumn.tsx'; +import TableLiveCard from './TableLiveCard.tsx'; +import TableDayCard from './TableDayCard.tsx'; import { useParams } from 'react-router-dom'; import { useQuery, useQueryClient } from '@tanstack/react-query'; -import { DailyPriceDataType, PriceDataType } from './PriceDataType.ts'; + import { getTradeHistory } from 'service/getTradeHistory.ts'; import { socket } from 'utils/socket.ts'; +import { DailyPriceDataType, PriceDataType } from './type.ts'; -export default function PriceSection() { +export default function Index() { const { id } = useParams(); const [buttonFlag, setButtonFlag] = useState(true); const indicatorRef = useRef(null); @@ -107,7 +108,7 @@ export default function PriceSection() {
- + {isLoading ? ( @@ -119,14 +120,14 @@ export default function PriceSection() { ) : buttonFlag ? ( tradeData.map((eachData: PriceDataType, index: number) => ( - )) ) : ( tradeData.map((eachData: DailyPriceDataType, index: number) => ( - diff --git a/FE/src/components/StocksDetail/PriceDataType.ts b/FE/src/components/StocksDetail/PriceSection/type.ts similarity index 100% rename from FE/src/components/StocksDetail/PriceDataType.ts rename to FE/src/components/StocksDetail/PriceSection/type.ts diff --git a/FE/src/components/StocksDetail/PriceSectionSseHook.ts b/FE/src/components/StocksDetail/PriceSectionSseHook.ts deleted file mode 100644 index 237657e2..00000000 --- a/FE/src/components/StocksDetail/PriceSectionSseHook.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { PriceDataType } from './PriceDataType.ts'; - -export const createSSEConnection = ( - url: string, - onMessage: (data: PriceDataType) => void, -) => { - const eventSource = new EventSource(url); - - eventSource.onmessage = (event) => { - try { - const data = JSON.parse(event.data); - onMessage(data.tradeData); - } catch (error) { - console.error('Failed to parse SSE message:', error); - } - }; - - eventSource.onerror = (error) => { - console.error('SSE Error:', error); - eventSource.close(); - }; - - eventSource.onopen = () => { - console.log('SSE connection opened'); - }; - - return eventSource; -}; diff --git a/FE/src/components/StocksDetail/type.ts b/FE/src/components/StocksDetail/type.ts new file mode 100644 index 00000000..56a64ca5 --- /dev/null +++ b/FE/src/components/StocksDetail/type.ts @@ -0,0 +1,7 @@ +export type ChartSizeConfigType = { + upperHeight: number; + lowerHeight: number; + chartWidth: number; + yAxisWidth: number; + xAxisHeight: number; +}; diff --git a/FE/src/components/TopFive/TopFive.tsx b/FE/src/components/TopFive/index.tsx similarity index 82% rename from FE/src/components/TopFive/TopFive.tsx rename to FE/src/components/TopFive/index.tsx index aef005b1..64e6467b 100644 --- a/FE/src/components/TopFive/TopFive.tsx +++ b/FE/src/components/TopFive/index.tsx @@ -2,22 +2,18 @@ import List from './List'; import Nav from './Nav'; import { useSearchParams } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; -import { MarketType } from './type.ts'; + import { getTopFiveStocks } from 'service/getTopFiveStocks.ts'; +import { MarketType } from './type.ts'; +import { stockIndexMap } from 'constants.ts'; -const paramsMap = { - 전체: 'ALL', - 코스피: 'KOSPI', - 코스닥: 'KOSDAQ', - 코스피200: 'KOSPI200', -}; -export default function TopFive() { +export default function Index() { const [searchParams] = useSearchParams(); const currentMarket = (searchParams.get('top') || '전체') as MarketType; const { data, isLoading } = useQuery({ queryKey: ['topfive', currentMarket], - queryFn: () => getTopFiveStocks(paramsMap[currentMarket]), + queryFn: () => getTopFiveStocks(stockIndexMap[currentMarket]), keepPreviousData: true, staleTime: 1000, cacheTime: 30000, diff --git a/FE/src/components/TopFive/type.ts b/FE/src/components/TopFive/type.ts index 1f515a51..300b4c74 100644 --- a/FE/src/components/TopFive/type.ts +++ b/FE/src/components/TopFive/type.ts @@ -8,16 +8,3 @@ export type StockData = { }; export type MarketType = '전체' | '코스피' | '코스닥' | '코스피200'; - -export type ChartData = { time: string; value: string; diff: string }; -export type StockIndexValue = { - curr_value: string; - diff: string; - diff_rate: string; - sign: string; -}; - -export type StockIndexData = { - chart: ChartData[]; - value: StockIndexValue; -}; diff --git a/FE/src/constants.ts b/FE/src/constants.ts new file mode 100644 index 00000000..fab13693 --- /dev/null +++ b/FE/src/constants.ts @@ -0,0 +1,7 @@ +export const stockIndexMap: Record = { + 전체: 'ALL', + 코스피: 'KOSPI', + 코스닥: 'KOSDAQ', + 코스피200: 'KOSPI200', + 코스닥150: 'KSQ150', +}; diff --git a/FE/src/components/Search/searchHistoryHook.ts b/FE/src/hooks/searchHistoryHook.ts similarity index 95% rename from FE/src/components/Search/searchHistoryHook.ts rename to FE/src/hooks/searchHistoryHook.ts index 469d272b..d3f958fb 100644 --- a/FE/src/components/Search/searchHistoryHook.ts +++ b/FE/src/hooks/searchHistoryHook.ts @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react'; -import { HistoryType } from './searchDataType'; +import { HistoryType } from '../components/Search/type.ts'; const STORAGE_KEY = import.meta.env.VITE_STORAGE_KEY; const MAX_HISTORY_ITEMS = import.meta.env.VITE_MAX_HISTORY_ITEMS; diff --git a/FE/src/utils/useDebounce.ts b/FE/src/hooks/useDebounce.ts similarity index 100% rename from FE/src/utils/useDebounce.ts rename to FE/src/hooks/useDebounce.ts diff --git a/FE/src/page/Home.tsx b/FE/src/page/Home.tsx index 938d05f7..77bb72b3 100644 --- a/FE/src/page/Home.tsx +++ b/FE/src/page/Home.tsx @@ -1,4 +1,4 @@ -import TopFive from 'components/TopFive/TopFive'; +import Index from 'components/TopFive'; import StockIndex from 'components/StockIndex/index.tsx'; import News from '../components/News/News.tsx'; @@ -6,7 +6,7 @@ export default function Home() { return ( <> - + ); diff --git a/FE/src/page/Rank.tsx b/FE/src/page/Rank.tsx index c12599c2..a4781336 100644 --- a/FE/src/page/Rank.tsx +++ b/FE/src/page/Rank.tsx @@ -1,5 +1,5 @@ import Nav from 'components/Rank/Nav.tsx'; -import RankList from '../components/Rank/RankList.tsx'; +import List from '../components/Rank/List.tsx'; import { getRanking } from '../service/getRanking.ts'; import { useQuery } from '@tanstack/react-query'; @@ -19,8 +19,8 @@ export default function Rank() {
- - + +
); diff --git a/FE/src/page/StocksDetail.tsx b/FE/src/page/StocksDetail.tsx index 42e336eb..cdd973f0 100644 --- a/FE/src/page/StocksDetail.tsx +++ b/FE/src/page/StocksDetail.tsx @@ -1,7 +1,7 @@ import { useQuery } from '@tanstack/react-query'; import Chart from 'components/StocksDetail/Chart'; import Header from 'components/StocksDetail/Header'; -import PriceSection from 'components/StocksDetail/PriceSection'; +import Index from 'components/StocksDetail/PriceSection'; import TradeSection from 'components/StocksDetail/TradeSection'; import { useParams } from 'react-router-dom'; import { getStocksByCode } from 'service/stocks'; @@ -26,7 +26,7 @@ export default function StocksDetail() {
- +
diff --git a/FE/src/types.ts b/FE/src/types.ts index e175d827..936b6746 100644 --- a/FE/src/types.ts +++ b/FE/src/types.ts @@ -43,8 +43,6 @@ export type StockChartUnit = { mov_avg_20?: string; }; -export type MypageSectionType = 'account' | 'order' | 'bookmark' | 'info'; - export type Asset = { cash_balance: string; stock_balance: string; @@ -80,14 +78,6 @@ export type Order = { created_at: string; }; -export type ChartSizeConfigType = { - upperHeight: number; - lowerHeight: number; - chartWidth: number; - yAxisWidth: number; - xAxisHeight: number; -}; - export type Profile = { name: string; email: string; From 7bb0ee29c699c8ccfccbd1856548aeda13a69f36 Mon Sep 17 00:00:00 2001 From: dongree Date: Mon, 2 Dec 2024 17:38:18 +0900 Subject: [PATCH 08/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=9D=B4=EB=A6=84=20=ED=86=B5=EC=9D=BC,?= =?UTF-8?q?=20price=20section=EC=97=90=20=EC=A2=80=20=EB=8D=94=20=EB=AA=85?= =?UTF-8?q?=ED=99=95=ED=95=9C=20=ED=83=80=EC=9E=85=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?#241?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/components/Mypage/CancleAlertModal.tsx | 6 +- FE/src/components/Mypage/Order.tsx | 6 +- FE/src/components/News/Card.tsx | 8 +-- FE/src/components/News/News.tsx | 4 +- FE/src/components/Rank/List.tsx | 12 ++-- FE/src/components/Search/index.tsx | 8 +-- FE/src/components/StockIndex/index.tsx | 4 +- .../StocksDetail/PriceSection/TableColumn.tsx | 5 +- .../PriceSection/TableDayCard.tsx | 2 +- .../StocksDetail/PriceSection/index.tsx | 41 ++++++------- .../StocksDetail/TradeSection/BuySection.tsx | 12 ++-- .../StocksDetail/TradeSection/SellSection.tsx | 16 ++--- .../TradeSection/TradeAlertModal.tsx | 6 +- FE/src/components/TopFive/index.tsx | 5 +- ...HistoryHook.ts => useSearchHistoryHook.ts} | 0 FE/src/page/Home.tsx | 6 +- FE/src/page/Rank.tsx | 4 +- FE/src/page/StocksDetail.tsx | 4 +- FE/src/service/getStockIndex.ts | 7 --- FE/src/service/getTopFiveStocks.ts | 9 --- FE/src/service/{getNewsData.ts => news.ts} | 0 FE/src/service/{getRanking.ts => ranking.ts} | 0 .../{getSearchResults.ts => search.ts} | 0 FE/src/service/stocks.ts | 18 ++++++ .../{getTradeHistory.ts => tradeHistory.ts} | 9 ++- .../store/{authStore.ts => useAuthStore.ts} | 0 ...re.ts => useOrderCancleAlertModalStore.ts} | 0 ...dalStore.ts => useTradeAlertModalStore.ts} | 0 FE/src/types.ts | 2 + FE/src/utils/chart/drawBarChart.ts | 2 +- FE/src/utils/chart/drawCandleChart.ts | 2 +- FE/src/utils/chart/drawChartGrid.ts | 2 +- FE/src/utils/chart/drawLineChart.ts | 2 +- FE/src/utils/chart/drawLowerYAxis.ts | 31 +--------- FE/src/utils/chart/drawUpperYAxis.ts | 4 +- FE/src/utils/chart/drawXAxis.ts | 2 +- FE/src/utils/chart/makeChartDataFlat.ts | 2 +- FE/src/utils/chart/makeLabels.ts | 2 +- FE/src/utils/common.ts | 21 ------- FE/src/utils/format.ts | 60 +++++++++++++++++++ FE/src/utils/formatNoSpecialChar.ts | 3 - FE/src/utils/formatTime.ts | 12 ---- 42 files changed, 169 insertions(+), 170 deletions(-) rename FE/src/hooks/{searchHistoryHook.ts => useSearchHistoryHook.ts} (100%) delete mode 100644 FE/src/service/getStockIndex.ts delete mode 100644 FE/src/service/getTopFiveStocks.ts rename FE/src/service/{getNewsData.ts => news.ts} (100%) rename FE/src/service/{getRanking.ts => ranking.ts} (100%) rename FE/src/service/{getSearchResults.ts => search.ts} (100%) rename FE/src/service/{getTradeHistory.ts => tradeHistory.ts} (57%) rename FE/src/store/{authStore.ts => useAuthStore.ts} (100%) rename FE/src/store/{orderCancleAlertModalStore.ts => useOrderCancleAlertModalStore.ts} (100%) rename FE/src/store/{tradeAlertModalStore.ts => useTradeAlertModalStore.ts} (100%) create mode 100644 FE/src/utils/format.ts delete mode 100644 FE/src/utils/formatNoSpecialChar.ts delete mode 100644 FE/src/utils/formatTime.ts diff --git a/FE/src/components/Mypage/CancleAlertModal.tsx b/FE/src/components/Mypage/CancleAlertModal.tsx index 5caeac15..10ba69bc 100644 --- a/FE/src/components/Mypage/CancleAlertModal.tsx +++ b/FE/src/components/Mypage/CancleAlertModal.tsx @@ -1,5 +1,5 @@ import Overay from 'components/ModalOveray'; -import useOrderCancelAlertModalStore from 'store/orderCancleAlertModalStore'; +import useOrderCancelAlertModalStore from 'store/useOrderCancleAlertModalStore'; export default function CancleAlertModal() { const { close, onSuccess, order } = useOrderCancelAlertModalStore(); @@ -17,7 +17,7 @@ export default function CancleAlertModal() {
-
-

+

+

{data.description}

diff --git a/FE/src/components/News/News.tsx b/FE/src/components/News/News.tsx index 075a1d8f..aaf48a77 100644 --- a/FE/src/components/News/News.tsx +++ b/FE/src/components/News/News.tsx @@ -1,6 +1,6 @@ import Card from './Card.tsx'; import { useQuery } from '@tanstack/react-query'; -import { getNewsData } from '../../service/getNewsData.ts'; +import { getNewsData } from '../../service/news.ts'; import { NewsDataType } from './type.ts'; export default function News() { @@ -17,7 +17,7 @@ export default function News() { return (
-
+

주요 뉴스

diff --git a/FE/src/components/Rank/List.tsx b/FE/src/components/Rank/List.tsx index 0b20c85f..fbf7b9f2 100644 --- a/FE/src/components/Rank/List.tsx +++ b/FE/src/components/Rank/List.tsx @@ -1,5 +1,5 @@ import Card from './Card.tsx'; -import useAuthStore from '../../store/authStore.ts'; +import useAuthStore from '../../store/useAuthStore.ts'; import { RankingCategory } from './type.ts'; type Props = { @@ -12,18 +12,14 @@ export default function List({ title, data }: Props) { const { isLogin } = useAuthStore(); return (
-
-
+
+

{title}

{topRank.map((item, index) => ( - + ))}
diff --git a/FE/src/components/Search/index.tsx b/FE/src/components/Search/index.tsx index eaf4308a..9b4ef2fb 100644 --- a/FE/src/components/Search/index.tsx +++ b/FE/src/components/Search/index.tsx @@ -7,11 +7,11 @@ import SearchList from './SearchList.tsx'; import useSearchInputStore from 'store/useSearchInputStore.ts'; import { useDebounce } from 'hooks/useDebounce.ts'; import { useQuery } from '@tanstack/react-query'; -import { getSearchResults } from 'service/getSearchResults.ts'; import Lottie from 'lottie-react'; import searchAnimation from 'assets/searchAnimation.json'; -import { useSearchHistory } from '../../hooks/searchHistoryHook.ts'; -import { formatNoSpecialChar } from '../../utils/formatNoSpecialChar.ts'; +import { useSearchHistory } from 'hooks/useSearchHistoryHook.ts'; +import { getSearchResults } from 'service/search.ts'; +import { formatNoSpecialChar } from 'utils/format.ts'; export default function SearchModal() { const { isOpen, toggleSearchModal } = useSearchModalStore(); @@ -36,7 +36,7 @@ export default function SearchModal() { if (data && data.length > 0 && debounceValue && !isLoading) { addSearchHistory(formatNoSpecialChar(debounceValue)); } - }, [data, debounceValue]); + }, [data, debounceValue, addSearchHistory, isLoading]); if (!isOpen) return null; diff --git a/FE/src/components/StockIndex/index.tsx b/FE/src/components/StockIndex/index.tsx index 5e0253a6..3523d417 100644 --- a/FE/src/components/StockIndex/index.tsx +++ b/FE/src/components/StockIndex/index.tsx @@ -1,6 +1,6 @@ +import { getStockIndex } from 'service/stocks.ts'; import { Card } from './Card.tsx'; import { useQuery } from '@tanstack/react-query'; -import { getStockIndex } from 'service/getStockIndex.ts'; export default function StockIndex() { const { data, isLoading, isError } = useQuery({ @@ -16,7 +16,7 @@ export default function StockIndex() { const { KOSPI, KOSDAQ, KOSPI200, KSQ150 } = data; return ( -
+
diff --git a/FE/src/components/StocksDetail/PriceSection/TableColumn.tsx b/FE/src/components/StocksDetail/PriceSection/TableColumn.tsx index ddc64a50..92642593 100644 --- a/FE/src/components/StocksDetail/PriceSection/TableColumn.tsx +++ b/FE/src/components/StocksDetail/PriceSection/TableColumn.tsx @@ -1,6 +1,9 @@ +import { PriceSectionViewType } from 'types'; + type Props = { - viewMode: boolean; + viewMode: PriceSectionViewType; }; + export default function TableColumn({ viewMode }: Props) { if (!viewMode) { return ( diff --git a/FE/src/components/StocksDetail/PriceSection/TableDayCard.tsx b/FE/src/components/StocksDetail/PriceSection/TableDayCard.tsx index 28d2dca0..7b9af93c 100644 --- a/FE/src/components/StocksDetail/PriceSection/TableDayCard.tsx +++ b/FE/src/components/StocksDetail/PriceSection/TableDayCard.tsx @@ -1,4 +1,4 @@ -import { formatTime } from 'utils/formatTime.ts'; +import { formatTime } from 'utils/format.ts'; import { DailyPriceDataType } from './type.ts'; type PriceTableDayCardProps = { diff --git a/FE/src/components/StocksDetail/PriceSection/index.tsx b/FE/src/components/StocksDetail/PriceSection/index.tsx index 844385ba..6f11d0f8 100644 --- a/FE/src/components/StocksDetail/PriceSection/index.tsx +++ b/FE/src/components/StocksDetail/PriceSection/index.tsx @@ -4,39 +4,38 @@ import TableLiveCard from './TableLiveCard.tsx'; import TableDayCard from './TableDayCard.tsx'; import { useParams } from 'react-router-dom'; import { useQuery, useQueryClient } from '@tanstack/react-query'; - -import { getTradeHistory } from 'service/getTradeHistory.ts'; +import { getTradeHistory } from 'service/tradeHistory.ts'; import { socket } from 'utils/socket.ts'; import { DailyPriceDataType, PriceDataType } from './type.ts'; +import { PriceSectionViewType } from 'types.ts'; -export default function Index() { +export default function PriceSection() { const { id } = useParams(); - const [buttonFlag, setButtonFlag] = useState(true); + const [viewMode, setViewMode] = useState('today'); const indicatorRef = useRef(null); const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]); const queryClient = useQueryClient(); const { data: tradeData = [], isLoading } = useQuery({ - queryKey: ['detail', id, buttonFlag], - queryFn: () => getTradeHistory(id as string, buttonFlag), - cacheTime: 30000, - staleTime: 1000, + queryKey: ['detail', id, viewMode], + queryFn: () => getTradeHistory(id as string, viewMode), + staleTime: 1000 * 60 * 3, }); const addData = useCallback( (newData: PriceDataType) => { queryClient.setQueryData( - ['detail', id, buttonFlag], + ['detail', id, viewMode], (old: PriceDataType[] = []) => { return [newData, ...old].slice(0, 30); }, ); }, - [id, buttonFlag], + [id, viewMode, queryClient], ); useEffect(() => { - if (!buttonFlag) return; + if (viewMode === 'daily') return; const handleTradeHistory = (chartData: PriceDataType) => { addData(chartData); }; @@ -45,10 +44,10 @@ export default function Index() { return () => { socket.off(`trade-history/${id}`, handleTradeHistory); }; - }, [id, addData, buttonFlag]); + }, [id, addData, viewMode]); useEffect(() => { - const tmpIndex = buttonFlag ? 0 : 1; + const tmpIndex = viewMode === 'today' ? 0 : 1; const currentButton = buttonRefs.current[tmpIndex]; const indicator = indicatorRef.current; @@ -56,7 +55,7 @@ export default function Index() { indicator.style.left = `${currentButton.offsetLeft}px`; indicator.style.width = `${currentButton.offsetWidth}px`; } - }, [buttonFlag]); + }, [viewMode]); return (
- + {isLoading ? ( @@ -118,7 +115,7 @@ export default function Index() { - ) : buttonFlag ? ( + ) : viewMode === 'today' ? ( tradeData.map((eachData: PriceDataType, index: number) => (
-
+

매수 가격

{lowerLimitFlag && ( @@ -129,13 +129,13 @@ export default function BuySection({ code, detailInfo }: BuySectionProps) { 이 주식의 최대 가격은 {(+stck_mxpr).toLocaleString()}입니다.
)} -
+

수량

@@ -154,7 +154,7 @@ export default function BuySection({ code, detailInfo }: BuySectionProps) {
-
+
{lackAssetFlag && (

잔액이 부족해요!

)} diff --git a/FE/src/components/StocksDetail/TradeSection/SellSection.tsx b/FE/src/components/StocksDetail/TradeSection/SellSection.tsx index 167cb403..351f32ad 100644 --- a/FE/src/components/StocksDetail/TradeSection/SellSection.tsx +++ b/FE/src/components/StocksDetail/TradeSection/SellSection.tsx @@ -12,7 +12,7 @@ import { } from 'react'; import { StockDetailType } from 'types'; import useAuthStore from 'store/authStore'; -import useTradeAlertModalStore from 'store/tradeAlertModalStore'; +import useTradeAlertModalStore from 'store/useTradeAlertModalStore'; import { calcYield, isNumericString } from 'utils/common'; import TradeAlertModal from './TradeAlertModal'; @@ -120,10 +120,10 @@ export default function SellSection({ code, detailInfo }: SellSectionProps) { if (!isLogin || quantity === 0) { return ( -
+

매도할 주식이 없어요

@@ -135,14 +135,14 @@ export default function SellSection({ code, detailInfo }: SellSectionProps) { <>
-
+

매도 가격

{lowerLimitFlag && ( @@ -155,14 +155,14 @@ export default function SellSection({ code, detailInfo }: SellSectionProps) { 이 주식의 최대 가격은 {(+stck_mxpr).toLocaleString()}입니다.
)} -
+

수량

@@ -198,7 +198,7 @@ export default function SellSection({ code, detailInfo }: SellSectionProps) {

{totalPrice.toLocaleString()}원

-
+
-
+

{count}주 희망가격

{totalPrice.toLocaleString()}원

@@ -59,7 +59,7 @@ export default function TradeAlertModal({
diff --git a/FE/src/components/Search/SearchCardHighlight.tsx b/FE/src/components/Search/SearchCardHighlight.tsx index f107717f..d5e1306c 100644 --- a/FE/src/components/Search/SearchCardHighlight.tsx +++ b/FE/src/components/Search/SearchCardHighlight.tsx @@ -1,4 +1,4 @@ -import { formatNoSpecialChar } from 'utils/formatNoSpecialChar.ts'; +import { formatNoSpecialChar } from 'utils/format.ts'; type SearchCardHighLightProps = { text: string; diff --git a/FE/src/components/StocksDetail/Chart.tsx b/FE/src/components/StocksDetail/Chart.tsx index 502fe3a1..ad90dfcd 100644 --- a/FE/src/components/StocksDetail/Chart.tsx +++ b/FE/src/components/StocksDetail/Chart.tsx @@ -1,12 +1,5 @@ -import { - MouseEvent, - useCallback, - useEffect, - useRef, - useState, - WheelEvent, -} from 'react'; -import { Padding, StockChartUnit, TiemCategory } from 'types'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { MousePositionType, StockChartUnit, TiemCategory } from 'types'; import { useQuery } from '@tanstack/react-query'; import { getStocksChartDataByCode } from 'service/stocks'; import { drawLineChart } from 'utils/chart/drawLineChart.ts'; @@ -18,61 +11,31 @@ import { drawLowerYAxis } from 'utils/chart/drawLowerYAxis.ts'; import { drawChartGrid } from 'utils/chart/drawChartGrid.ts'; import { drawMouseGrid } from 'utils/chart/drawMouseGrid.ts'; import { EyeIcon, EyeSlashIcon } from '@heroicons/react/16/solid'; -import { ChartSizeConfigType } from './type.ts'; - -const categories: { label: string; value: TiemCategory }[] = [ - { label: '일', value: 'D' }, - { label: '주', value: 'W' }, - { label: '월', value: 'M' }, - { label: '년', value: 'Y' }, -]; - -const padding: Padding = { - top: 20, - right: 80, - bottom: 10, - left: 40, -}; - -type StocksDeatailChartProps = { +import { setCanvasSize } from 'utils/chart/setCanvasSize.ts'; +import { useMouseMove } from 'hooks/useMouseMove.ts'; +import { useMouseUpDown } from 'hooks/useMouseUpDown.ts'; +import { useMouseWheel } from 'hooks/useMouseWheel.ts'; +import { categories, padding } from 'constants.ts'; +import { useCanvasRef } from 'hooks/useCanvasRef.ts'; +import { useCanvasResize } from 'hooks/useCanvasResize.ts'; + +type StocksDetailChartProps = { code: string; }; -export type MousePositionType = { - x: number; - y: number; -}; - -export default function Chart({ code }: StocksDeatailChartProps) { +export default function Chart({ code }: StocksDetailChartProps) { const containerRef = useRef(null); - const upperChartCanvasRef = useRef(null); - const lowerChartCanvasRef = useRef(null); - const upperChartY = useRef(null); - const lowerChartY = useRef(null); - const chartX = useRef(null); - const rafRef = useRef(); + const { + upperChartCanvasRef, + lowerChartCanvasRef, + upperChartY, + lowerChartY, + chartX, + } = useCanvasRef(); const [timeCategory, setTimeCategory] = useState('D'); const [moveAverageToggle, setMoveAverageToggle] = useState(true); - const [charSizeConfig, setChartSizeConfig] = useState({ - upperHeight: 0.5, - lowerHeight: 0.4, - chartWidth: 0.92, - yAxisWidth: 0.08, - xAxisHeight: 0.1, - }); - const [isDragging, setIsDragging] = useState(false); - const [upperLabelNum, setUpperLabelNum] = useState(3); - const [lowerLabelNum, setLowerLabelNum] = useState(3); - const [mousePosition, setMousePosition] = useState({ - x: 0, - y: 0, - }); + const [mouseIndex, setMouseIndex] = useState(null); - const [dataRange, setDataRange] = useState({ - start: 0, - end: 0, - }); - const minDisplayData = 20; const { data, isLoading } = useQuery( ['stocksChartData', code, timeCategory], @@ -80,95 +43,20 @@ export default function Chart({ code }: StocksDeatailChartProps) { { staleTime: 1000 * 60 }, ); - const handleMouseDown = useCallback((e: MouseEvent) => { - e.preventDefault(); - setIsDragging(true); - }, []); - - const handleMouseMove = useCallback( - (e: globalThis.MouseEvent) => { - if (!isDragging || !containerRef.current) return; - const minHeight = 0.2; - const containerRect = containerRef.current.getBoundingClientRect(); - const mouseY = e.clientY - containerRect.top; - const ratio = mouseY / containerRef.current.clientHeight; - const maxHeight = 0.9 - minHeight; - const upperRatio = Math.min(maxHeight, Math.max(minHeight, ratio)); - const lowerRatio = 0.9 - upperRatio; - - const calculateLabelNum = (ratio: number) => { - if (ratio <= 0.2) return 1; - if (ratio <= 0.35) return 2; - if (ratio <= 0.55) return 3; - return 4; - }; - - if (lowerRatio >= minHeight && upperRatio >= minHeight) { - setChartSizeConfig((prev) => ({ - ...prev, - upperHeight: upperRatio, - lowerHeight: lowerRatio, - })); - - setUpperLabelNum(calculateLabelNum(upperRatio)); - setLowerLabelNum(calculateLabelNum(lowerRatio)); - } - }, - [ - isDragging, - containerRef, - setChartSizeConfig, - setUpperLabelNum, - setLowerLabelNum, - ], - ); - - const handleMouseUp = useCallback(() => { - setIsDragging(false); - }, []); - - const getCanvasMousePosition = (e: MouseEvent) => { - if (!containerRef.current) return; - - if (rafRef.current) { - cancelAnimationFrame(rafRef.current); - } - - rafRef.current = requestAnimationFrame(() => { - const rect = containerRef.current?.getBoundingClientRect(); - if (!rect) return; - - setMousePosition({ - x: (e.clientX - rect.left) * 2, - y: (e.clientY - rect.top) * 2, - }); - }); - }; - - useEffect(() => { - if (isDragging) { - window.addEventListener('mousemove', handleMouseMove); - window.addEventListener('mouseup', handleMouseUp); - } - - return () => { - window.removeEventListener('mousemove', handleMouseMove); - window.removeEventListener('mouseup', handleMouseUp); - if (rafRef.current) { - cancelAnimationFrame(rafRef.current); - } - }; - }, [isDragging, handleMouseDown, handleMouseUp, handleMouseMove]); - const setCanvasSize = useCallback( - (canvas: HTMLCanvasElement, widthConfig: number, heightConfig: number) => { - if (!containerRef.current) return; - - canvas.width = containerRef.current.clientWidth * widthConfig * 2; - canvas.height = containerRef.current.clientHeight * heightConfig * 2; - canvas.style.width = `${containerRef.current.clientWidth * widthConfig}px`; - canvas.style.height = `${containerRef.current.clientHeight * heightConfig}px`; + const { mousePosition, getCanvasMousePosition } = useMouseMove(containerRef); + const { chartSizeConfig, upperLabelNum, lowerLabelNum, handleMouseDown } = + useMouseUpDown(containerRef); + const { dataRange, handleWheel } = useMouseWheel(data); + const { resizeCanvases } = useCanvasResize( + { + upperChartCanvasRef, + lowerChartCanvasRef, + upperChartY, + lowerChartY, + chartX, }, - [containerRef], + chartSizeConfig, + containerRef, ); const renderChart = useCallback( @@ -311,49 +199,6 @@ export default function Chart({ code }: StocksDeatailChartProps) { dataRange, ], ); - const handleWheel = useCallback( - (e: WheelEvent) => { - if (!data) return; - - const wheelPower = 0.1; - const curRange = dataRange.end - dataRange.start; - - // 축소 - if (e.deltaY > 0) { - // 늘어나야함 & 데이터 최대 갯수보다 작아야함. - const newRange = Math.min(curRange * (1 + wheelPower), data.length); - const newStart = Math.max(data.length - newRange, 0); - - setDataRange({ - start: newStart, - end: data.length - 1, - }); - } - - // 확대 - if (e.deltaY < 0) { - // 줄어야함 & 최소 데이터 갯수보단 커야함. - const newRange = Math.max(curRange * (1 - wheelPower), minDisplayData); - const newStart = Math.max(data.length - newRange, 0); - - setDataRange({ - start: newStart, - end: data.length - 1, - }); - } - }, - [data, dataRange], - ); - - useEffect(() => { - if (data) { - setDataRange((prev) => ({ - ...prev, - start: 0, - end: data.length - 1, - })); - } - }, [data]); useEffect(() => { if (isLoading || !data) return; @@ -366,35 +211,8 @@ export default function Chart({ code }: StocksDeatailChartProps) { !chartX.current ) return; - setCanvasSize( - upperChartCanvasRef.current, - charSizeConfig.chartWidth, - charSizeConfig.upperHeight, - ); - setCanvasSize( - upperChartY.current, - charSizeConfig.yAxisWidth, - charSizeConfig.upperHeight, - ); - - setCanvasSize( - lowerChartCanvasRef.current, - charSizeConfig.chartWidth, - charSizeConfig.lowerHeight, - ); - - setCanvasSize( - lowerChartY.current, - charSizeConfig.yAxisWidth, - charSizeConfig.lowerHeight, - ); - - setCanvasSize( - chartX.current, - charSizeConfig.chartWidth, - charSizeConfig.xAxisHeight, - ); + resizeCanvases(); renderChart( upperChartCanvasRef.current, @@ -411,9 +229,10 @@ export default function Chart({ code }: StocksDeatailChartProps) { isLoading, setCanvasSize, renderChart, - charSizeConfig, + chartSizeConfig, mousePosition, ]); + return (
@@ -441,24 +260,6 @@ export default function Chart({ code }: StocksDeatailChartProps) {
- {mouseIndex !== null && data ? ( -
- - 시작 {Number(data[mouseIndex].stck_oprc).toLocaleString()}원 - - - 고가 {Number(data[mouseIndex].stck_hgpr).toLocaleString()}원 - - - 저가 {Number(data[mouseIndex].stck_lwpr).toLocaleString()}원 - - - 종가 {Number(data[mouseIndex].stck_clpr).toLocaleString()}원 - -
- ) : null}
+ {mouseIndex !== null && data ? ( +
+ + 시작 {Number(data[mouseIndex].stck_oprc).toLocaleString()}원 + + + 고가 {Number(data[mouseIndex].stck_hgpr).toLocaleString()}원 + + + 저가 {Number(data[mouseIndex].stck_lwpr).toLocaleString()}원 + + + 종가 {Number(data[mouseIndex].stck_clpr).toLocaleString()}원 + +
+ ) : null}
diff --git a/FE/src/components/StocksDetail/Header.tsx b/FE/src/components/StocksDetail/Header.tsx index e7a54720..122c8470 100644 --- a/FE/src/components/StocksDetail/Header.tsx +++ b/FE/src/components/StocksDetail/Header.tsx @@ -3,7 +3,7 @@ import Toast from 'components/Toast'; import { useEffect, useState } from 'react'; import { bookmark, unbookmark } from 'service/bookmark'; import { unsubscribe } from 'service/stocks'; -import useAuthStore from 'store/authStore'; +import useAuthStore from 'store/useAuthStore.ts'; import useLoginModalStore from 'store/useLoginModalStore'; import { StockDetailType } from 'types'; import { stringToLocaleString } from 'utils/common'; @@ -95,7 +95,7 @@ export default function Header({ code, data }: StocksDetailHeaderProps) { currPrdyVrssSign === '3' ? '' : currPrdyVrssSign < '3' ? '+' : '-'; return ( -
+

{hts_kor_isnm}

diff --git a/FE/src/components/StocksDetail/TradeSection/BuySection.tsx b/FE/src/components/StocksDetail/TradeSection/BuySection.tsx index ec05400f..88cbc29c 100644 --- a/FE/src/components/StocksDetail/TradeSection/BuySection.tsx +++ b/FE/src/components/StocksDetail/TradeSection/BuySection.tsx @@ -9,7 +9,7 @@ import { import useTradeAlertModalStore from 'store/useTradeAlertModalStore'; import { StockDetailType } from 'types'; import { isNumericString } from 'utils/common'; -import useAuthStore from 'store/authStore'; +import useAuthStore from 'store/useAuthStore.ts'; import { useQuery } from '@tanstack/react-query'; import { getCash } from 'service/assets'; import TradeAlertModal from './TradeAlertModal'; diff --git a/FE/src/components/StocksDetail/TradeSection/SellSection.tsx b/FE/src/components/StocksDetail/TradeSection/SellSection.tsx index 351f32ad..085b49ce 100644 --- a/FE/src/components/StocksDetail/TradeSection/SellSection.tsx +++ b/FE/src/components/StocksDetail/TradeSection/SellSection.tsx @@ -11,7 +11,7 @@ import { useState, } from 'react'; import { StockDetailType } from 'types'; -import useAuthStore from 'store/authStore'; +import useAuthStore from 'store/useAuthStore.ts'; import useTradeAlertModalStore from 'store/useTradeAlertModalStore'; import { calcYield, isNumericString } from 'utils/common'; import TradeAlertModal from './TradeAlertModal'; diff --git a/FE/src/constants.ts b/FE/src/constants.ts index fab13693..dcfd90cd 100644 --- a/FE/src/constants.ts +++ b/FE/src/constants.ts @@ -1,3 +1,5 @@ +import { Padding, TiemCategory } from './types.ts'; + export const stockIndexMap: Record = { 전체: 'ALL', 코스피: 'KOSPI', @@ -5,3 +7,17 @@ export const stockIndexMap: Record = { 코스피200: 'KOSPI200', 코스닥150: 'KSQ150', }; + +export const categories: { label: string; value: TiemCategory }[] = [ + { label: '일', value: 'D' }, + { label: '주', value: 'W' }, + { label: '월', value: 'M' }, + { label: '년', value: 'Y' }, +]; + +export const padding: Padding = { + top: 20, + right: 80, + bottom: 10, + left: 40, +}; diff --git a/FE/src/hooks/useCanvasRef.ts b/FE/src/hooks/useCanvasRef.ts new file mode 100644 index 00000000..38e48234 --- /dev/null +++ b/FE/src/hooks/useCanvasRef.ts @@ -0,0 +1,17 @@ +import { useRef } from 'react'; + +export const useCanvasRef = () => { + const upperChartCanvasRef = useRef(null); + const lowerChartCanvasRef = useRef(null); + const upperChartY = useRef(null); + const lowerChartY = useRef(null); + const chartX = useRef(null); + + return { + upperChartCanvasRef, + lowerChartCanvasRef, + upperChartY, + lowerChartY, + chartX, + }; +}; diff --git a/FE/src/hooks/useCanvasResize.ts b/FE/src/hooks/useCanvasResize.ts new file mode 100644 index 00000000..5135ff88 --- /dev/null +++ b/FE/src/hooks/useCanvasResize.ts @@ -0,0 +1,57 @@ +import { RefObject, useCallback } from 'react'; +import { ChartSizeConfigType } from 'components/StocksDetail/type.ts'; +import { setCanvasSize } from 'utils/chart/setCanvasSize.ts'; + +type CanvasRefType = { + upperChartCanvasRef: RefObject; + upperChartY: RefObject; + lowerChartCanvasRef: RefObject; + lowerChartY: RefObject; + chartX: RefObject; +}; + +export const useCanvasResize = ( + canvasRefs: CanvasRefType, + chartSizeConfig: ChartSizeConfigType, + containerRef: RefObject, +) => { + const resizeCanvases = useCallback(() => { + if (!containerRef.current) return; + + const configs = [ + { + ref: canvasRefs.upperChartCanvasRef, + width: chartSizeConfig.chartWidth, + height: chartSizeConfig.upperHeight, + }, + { + ref: canvasRefs.upperChartY, + width: chartSizeConfig.yAxisWidth, + height: chartSizeConfig.upperHeight, + }, + { + ref: canvasRefs.lowerChartCanvasRef, + width: chartSizeConfig.chartWidth, + height: chartSizeConfig.lowerHeight, + }, + { + ref: canvasRefs.lowerChartY, + width: chartSizeConfig.yAxisWidth, + height: chartSizeConfig.lowerHeight, + }, + { + ref: canvasRefs.chartX, + width: chartSizeConfig.chartWidth, + height: chartSizeConfig.xAxisHeight, + }, + ]; + + configs.forEach(({ ref, width, height }) => { + if (ref.current) { + setCanvasSize(ref.current, width, height, containerRef); + } + }); + }, [chartSizeConfig, containerRef]); + + return { resizeCanvases }; +}; diff --git a/FE/src/hooks/useChart.ts b/FE/src/hooks/useChart.ts new file mode 100644 index 00000000..4d27269f --- /dev/null +++ b/FE/src/hooks/useChart.ts @@ -0,0 +1,11 @@ +import { useRef } from 'react'; + +export const useChart = () => { + const upperChartCanvasRef = useRef(null); + + // const upperChartY = useRef(null); + + return { + upperChartCanvasRef, + }; +}; diff --git a/FE/src/hooks/useMouseMove.ts b/FE/src/hooks/useMouseMove.ts new file mode 100644 index 00000000..c1e799a8 --- /dev/null +++ b/FE/src/hooks/useMouseMove.ts @@ -0,0 +1,29 @@ +import { MouseEvent, RefObject, useRef, useState } from 'react'; +import { MousePositionType } from '../components/StocksDetail/Chart.tsx'; + +export const useMouseMove = (containerRef: RefObject) => { + const rafRef = useRef(); + const [mousePosition, setMousePosition] = useState({ + x: 0, + y: 0, + }); + + const getCanvasMousePosition = (e: MouseEvent) => { + if (!containerRef.current) return; + + if (rafRef.current) { + cancelAnimationFrame(rafRef.current); + } + + rafRef.current = requestAnimationFrame(() => { + const rect = containerRef.current?.getBoundingClientRect(); + if (!rect) return; + + setMousePosition({ + x: (e.clientX - rect.left) * 2, + y: (e.clientY - rect.top) * 2, + }); + }); + }; + return { mousePosition, getCanvasMousePosition }; +}; diff --git a/FE/src/hooks/useMouseUpDown.ts b/FE/src/hooks/useMouseUpDown.ts new file mode 100644 index 00000000..0d6d9651 --- /dev/null +++ b/FE/src/hooks/useMouseUpDown.ts @@ -0,0 +1,80 @@ +import { RefObject, useCallback, useEffect, useState } from 'react'; +import { ChartSizeConfigType } from '../components/StocksDetail/type.ts'; + +export const useMouseUpDown = (containerRef: RefObject) => { + const [chartSizeConfig, setChartSizeConfig] = useState({ + upperHeight: 0.5, + lowerHeight: 0.4, + chartWidth: 0.92, + yAxisWidth: 0.08, + xAxisHeight: 0.1, + }); + const [isDragging, setIsDragging] = useState(false); + const [upperLabelNum, setUpperLabelNum] = useState(3); + const [lowerLabelNum, setLowerLabelNum] = useState(3); + + const handleMouseDown = useCallback(() => { + setIsDragging(true); + }, []); + + const handleMouseMove = useCallback( + (e: globalThis.MouseEvent) => { + if (!isDragging || !containerRef.current) return; + const minHeight = 0.2; + const containerRect = containerRef.current.getBoundingClientRect(); + const mouseY = e.clientY - containerRect.top; + const ratio = mouseY / containerRef.current.clientHeight; + const maxHeight = 0.9 - minHeight; + const upperRatio = Math.min(maxHeight, Math.max(minHeight, ratio)); + const lowerRatio = 0.9 - upperRatio; + + const calculateLabelNum = (ratio: number) => { + if (ratio <= 0.2) return 1; + if (ratio <= 0.35) return 2; + if (ratio <= 0.55) return 3; + return 4; + }; + + if (lowerRatio >= minHeight && upperRatio >= minHeight) { + setChartSizeConfig((prev) => ({ + ...prev, + upperHeight: upperRatio, + lowerHeight: lowerRatio, + })); + + setUpperLabelNum(calculateLabelNum(upperRatio)); + setLowerLabelNum(calculateLabelNum(lowerRatio)); + } + }, + [ + isDragging, + containerRef, + setChartSizeConfig, + setUpperLabelNum, + setLowerLabelNum, + ], + ); + + const handleMouseUp = useCallback(() => { + setIsDragging(false); + }, []); + + useEffect(() => { + if (isDragging) { + window.addEventListener('mousemove', handleMouseMove); + window.addEventListener('mouseup', handleMouseUp); + } + + return () => { + window.removeEventListener('mousemove', handleMouseMove); + window.removeEventListener('mouseup', handleMouseUp); + }; + }, [isDragging, handleMouseDown, handleMouseUp, handleMouseMove]); + + return { + chartSizeConfig, + upperLabelNum, + lowerLabelNum, + handleMouseDown, + }; +}; diff --git a/FE/src/hooks/useMouseWheel.ts b/FE/src/hooks/useMouseWheel.ts new file mode 100644 index 00000000..d1468d53 --- /dev/null +++ b/FE/src/hooks/useMouseWheel.ts @@ -0,0 +1,56 @@ +import { useCallback, useEffect, useState, WheelEvent } from 'react'; +import { StockChartUnit } from '../types.ts'; + +export const useMouseWheel = (data: StockChartUnit[] | undefined) => { + const [dataRange, setDataRange] = useState({ + start: 0, + end: 0, + }); + const minDisplayData = 20; + + const handleWheel = useCallback( + (e: WheelEvent) => { + if (!data) return; + + const wheelPower = 0.1; + const curRange = dataRange.end - dataRange.start; + + // 축소 + if (e.deltaY > 0) { + // 늘어나야함 & 데이터 최대 갯수보다 작아야함. + const newRange = Math.min(curRange * (1 + wheelPower), data.length); + const newStart = Math.max(data.length - newRange, 0); + + setDataRange({ + start: newStart, + end: data.length - 1, + }); + } + + // 확대 + if (e.deltaY < 0) { + // 줄어야함 & 최소 데이터 갯수보단 커야함. + const newRange = Math.max(curRange * (1 - wheelPower), minDisplayData); + const newStart = Math.max(data.length - newRange, 0); + + setDataRange({ + start: newStart, + end: data.length - 1, + }); + } + }, + [data, dataRange], + ); + + useEffect(() => { + if (data) { + setDataRange((prev) => ({ + ...prev, + start: 0, + end: data.length - 1, + })); + } + }, [data]); + + return { dataRange, handleWheel }; +}; diff --git a/FE/src/hooks/useSearchHistoryHook.ts b/FE/src/hooks/useSearchHistoryHook.ts index d3f958fb..90fa28d8 100644 --- a/FE/src/hooks/useSearchHistoryHook.ts +++ b/FE/src/hooks/useSearchHistoryHook.ts @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useCallback } from 'react'; import { HistoryType } from '../components/Search/type.ts'; const STORAGE_KEY = import.meta.env.VITE_STORAGE_KEY; @@ -13,7 +13,7 @@ export function useSearchHistory() { } }, []); - const addSearchHistory = (keyword: string) => { + const addSearchHistory = useCallback((keyword: string) => { if (!keyword.trim()) return; setSearchHistory((prev) => { @@ -32,7 +32,7 @@ export function useSearchHistory() { localStorage.setItem(STORAGE_KEY, JSON.stringify(newHistory)); return newHistory; }); - }; + }, []); const deleteSearchHistory = (text: string) => { setSearchHistory((prev) => { diff --git a/FE/src/types.ts b/FE/src/types.ts index 39eb85b1..88b257f7 100644 --- a/FE/src/types.ts +++ b/FE/src/types.ts @@ -93,3 +93,8 @@ export type BookmakredStock = { }; export type PriceSectionViewType = 'today' | 'daily'; + +export type MousePositionType = { + x: number; + y: number; +}; diff --git a/FE/src/utils/chart/drawLowerYAxis.ts b/FE/src/utils/chart/drawLowerYAxis.ts index 22682f83..d76242ff 100644 --- a/FE/src/utils/chart/drawLowerYAxis.ts +++ b/FE/src/utils/chart/drawLowerYAxis.ts @@ -1,6 +1,5 @@ -import { Padding, StockChartUnit } from '../../types.ts'; +import { MousePositionType, Padding, StockChartUnit } from 'types.ts'; import { makeYLabels } from './makeLabels.ts'; -import { MousePositionType } from '../../components/StocksDetail/Chart.tsx'; import { formatNumber } from 'utils/format.ts'; export const drawLowerYAxis = ( diff --git a/FE/src/utils/chart/drawMouseGrid.ts b/FE/src/utils/chart/drawMouseGrid.ts index 89a6b3fd..3d0b0bce 100644 --- a/FE/src/utils/chart/drawMouseGrid.ts +++ b/FE/src/utils/chart/drawMouseGrid.ts @@ -1,5 +1,4 @@ -import { Padding } from 'types.ts'; -import { MousePositionType } from 'components/StocksDetail/Chart.tsx'; +import { MousePositionType, Padding } from 'types.ts'; export const drawMouseGrid = ( upperChartCtx: CanvasRenderingContext2D, diff --git a/FE/src/utils/chart/drawUpperYAxis.ts b/FE/src/utils/chart/drawUpperYAxis.ts index 32982f8d..4aad7e79 100644 --- a/FE/src/utils/chart/drawUpperYAxis.ts +++ b/FE/src/utils/chart/drawUpperYAxis.ts @@ -1,6 +1,5 @@ -import { Padding, StockChartUnit } from 'types.ts'; +import { MousePositionType, Padding, StockChartUnit } from 'types.ts'; import { makeYLabels } from './makeLabels.ts'; -import { MousePositionType } from 'components/StocksDetail/Chart.tsx'; import { makeChartDataFlat } from './makeChartDataFlat.ts'; export const drawUpperYAxis = ( diff --git a/FE/src/utils/chart/drawXAxis.ts b/FE/src/utils/chart/drawXAxis.ts index 465427ac..10c71807 100644 --- a/FE/src/utils/chart/drawXAxis.ts +++ b/FE/src/utils/chart/drawXAxis.ts @@ -1,6 +1,5 @@ -import { Padding, StockChartUnit } from 'types.ts'; +import { MousePositionType, Padding, StockChartUnit } from 'types.ts'; import { makeXLabels } from './makeLabels.ts'; -import { MousePositionType } from '../../components/StocksDetail/Chart.tsx'; import { formatTime } from 'utils/format.ts'; export const drawXAxis = ( diff --git a/FE/src/utils/chart/setCanvasSize.ts b/FE/src/utils/chart/setCanvasSize.ts new file mode 100644 index 00000000..bc291564 --- /dev/null +++ b/FE/src/utils/chart/setCanvasSize.ts @@ -0,0 +1,15 @@ +import { RefObject } from 'react'; + +export const setCanvasSize = ( + canvas: HTMLCanvasElement, + widthConfig: number, + heightConfig: number, + containerRef: RefObject, +) => { + if (!containerRef.current) return; + + canvas.width = containerRef.current.clientWidth * widthConfig * 2; + canvas.height = containerRef.current.clientHeight * heightConfig * 2; + canvas.style.width = `${containerRef.current.clientWidth * widthConfig}px`; + canvas.style.height = `${containerRef.current.clientHeight * heightConfig}px`; +}; From 9f8ca401b0790d0ccab32b106fce30411a426df6 Mon Sep 17 00:00:00 2001 From: Seo San Date: Mon, 2 Dec 2024 20:34:48 +0900 Subject: [PATCH 10/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20renderCh?= =?UTF-8?q?art=20=ED=95=A8=EC=88=98=20=EB=B6=84=EB=A6=AC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/components/StocksDetail/Chart.tsx | 166 ++--------------------- FE/src/utils/renderChart.ts | 142 +++++++++++++++++++ 2 files changed, 156 insertions(+), 152 deletions(-) create mode 100644 FE/src/utils/renderChart.ts diff --git a/FE/src/components/StocksDetail/Chart.tsx b/FE/src/components/StocksDetail/Chart.tsx index ad90dfcd..445bfa2d 100644 --- a/FE/src/components/StocksDetail/Chart.tsx +++ b/FE/src/components/StocksDetail/Chart.tsx @@ -1,23 +1,16 @@ -import { useCallback, useEffect, useRef, useState } from 'react'; -import { MousePositionType, StockChartUnit, TiemCategory } from 'types'; +import { useEffect, useRef, useState } from 'react'; +import { TiemCategory } from 'types'; import { useQuery } from '@tanstack/react-query'; import { getStocksChartDataByCode } from 'service/stocks'; -import { drawLineChart } from 'utils/chart/drawLineChart.ts'; -import { drawCandleChart } from 'utils/chart/drawCandleChart.ts'; -import { drawBarChart } from 'utils/chart/drawBarChart.ts'; -import { drawXAxis } from 'utils/chart/drawXAxis.ts'; -import { drawUpperYAxis } from 'utils/chart/drawUpperYAxis.ts'; -import { drawLowerYAxis } from 'utils/chart/drawLowerYAxis.ts'; -import { drawChartGrid } from 'utils/chart/drawChartGrid.ts'; -import { drawMouseGrid } from 'utils/chart/drawMouseGrid.ts'; import { EyeIcon, EyeSlashIcon } from '@heroicons/react/16/solid'; import { setCanvasSize } from 'utils/chart/setCanvasSize.ts'; import { useMouseMove } from 'hooks/useMouseMove.ts'; import { useMouseUpDown } from 'hooks/useMouseUpDown.ts'; import { useMouseWheel } from 'hooks/useMouseWheel.ts'; -import { categories, padding } from 'constants.ts'; +import { categories } from 'constants.ts'; import { useCanvasRef } from 'hooks/useCanvasRef.ts'; import { useCanvasResize } from 'hooks/useCanvasResize.ts'; +import { renderChart } from 'utils/renderChart.ts'; type StocksDetailChartProps = { code: string; @@ -59,147 +52,6 @@ export default function Chart({ code }: StocksDetailChartProps) { containerRef, ); - const renderChart = useCallback( - ( - upperChartCanvas: HTMLCanvasElement, - lowerChartCanvas: HTMLCanvasElement, - upperChartYCanvas: HTMLCanvasElement, - lowerChartYCanvas: HTMLCanvasElement, - chartXCanvas: HTMLCanvasElement, - chartData: StockChartUnit[], - mousePosition: MousePositionType, - ) => { - const UpperChartCtx = upperChartCanvas.getContext('2d'); - const LowerChartCtx = lowerChartCanvas.getContext('2d'); - const UpperYCtx = upperChartYCanvas.getContext('2d'); - const LowerYCtx = lowerChartYCanvas.getContext('2d'); - const ChartXCtx = chartXCanvas.getContext('2d'); - const displayData = chartData.slice(dataRange.start, dataRange.end + 1); - if ( - !UpperChartCtx || - !LowerChartCtx || - !UpperYCtx || - !LowerYCtx || - !ChartXCtx - ) - return; - - drawChartGrid( - UpperChartCtx, - upperChartCanvas.width - padding.left - padding.right, - upperChartCanvas.height - padding.top - padding.bottom, - upperLabelNum, - LowerChartCtx, - lowerChartCanvas.width - padding.left - padding.right, - lowerChartCanvas.height - padding.top - padding.bottom, - lowerLabelNum, - displayData, - padding, - ); - - if (moveAverageToggle) { - drawLineChart( - UpperChartCtx, - displayData, - 0, - 0, - upperChartCanvas.width - padding.left - padding.right, - upperChartCanvas.height - padding.top - padding.bottom, - padding, - 0.1, - ); - } - - drawCandleChart( - UpperChartCtx, - displayData, - 0, - 0, - upperChartCanvas.width - padding.left - padding.right, - upperChartCanvas.height - padding.top - padding.bottom, - padding, - 0.1, - ); - - drawBarChart( - LowerChartCtx, - displayData, - lowerChartCanvas.width - padding.left - padding.right, - lowerChartCanvas.height - padding.top - padding.bottom, - padding, - ); - - drawUpperYAxis( - UpperYCtx, - displayData, - upperChartYCanvas.width - padding.left - padding.right, - upperChartYCanvas.height - padding.top - padding.bottom, - upperLabelNum, - padding, - 0.1, - mousePosition, - upperChartCanvas.width, - upperChartCanvas.height, - ); - - drawLowerYAxis( - LowerYCtx, - displayData, - lowerChartYCanvas.width - padding.left - padding.right, - lowerChartYCanvas.height - padding.top - padding.bottom, - lowerLabelNum, - padding, - mousePosition, - lowerChartCanvas.width, - lowerChartCanvas.height, - upperChartCanvas.height, - ); - - drawXAxis( - ChartXCtx, - displayData, - chartXCanvas.width - padding.left - padding.right, - chartXCanvas.height, - padding, - mousePosition, - upperChartCanvas.height + lowerChartCanvas.height, - setMouseIndex, - ); - - if ( - mousePosition.x > padding.left && - mousePosition.x < upperChartCanvas.width && - mousePosition.y > padding.top && - mousePosition.y < upperChartCanvas.height + lowerChartCanvas.height - ) { - drawMouseGrid( - UpperChartCtx, - upperChartCanvas.width - padding.left - padding.right, - upperChartCanvas.height - padding.top - padding.bottom, - LowerChartCtx, - lowerChartCanvas.width - padding.left - padding.right, - lowerChartCanvas.height - padding.top - padding.bottom, - padding, - mousePosition, - ); - } - }, - [ - padding, - upperLabelNum, - lowerLabelNum, - drawChartGrid, - drawLineChart, - drawCandleChart, - drawBarChart, - drawUpperYAxis, - drawLowerYAxis, - drawXAxis, - moveAverageToggle, - dataRange, - ], - ); - useEffect(() => { if (isLoading || !data) return; @@ -222,6 +74,11 @@ export default function Chart({ code }: StocksDetailChartProps) { chartX.current, data, mousePosition, + dataRange, + upperLabelNum, + lowerLabelNum, + moveAverageToggle, + setMouseIndex, ); }, [ timeCategory, @@ -231,6 +88,11 @@ export default function Chart({ code }: StocksDetailChartProps) { renderChart, chartSizeConfig, mousePosition, + dataRange, + upperLabelNum, + lowerLabelNum, + moveAverageToggle, + setMouseIndex, ]); return ( diff --git a/FE/src/utils/renderChart.ts b/FE/src/utils/renderChart.ts new file mode 100644 index 00000000..da8fc01c --- /dev/null +++ b/FE/src/utils/renderChart.ts @@ -0,0 +1,142 @@ +import { MousePositionType, StockChartUnit } from 'types.ts'; +import { drawChartGrid } from './chart/drawChartGrid.ts'; +import { padding } from 'constants.ts'; +import { drawLineChart } from './chart/drawLineChart.ts'; +import { drawCandleChart } from './chart/drawCandleChart.ts'; +import { drawBarChart } from './chart/drawBarChart.ts'; +import { drawUpperYAxis } from './chart/drawUpperYAxis.ts'; +import { drawLowerYAxis } from './chart/drawLowerYAxis.ts'; +import { drawXAxis } from './chart/drawXAxis.ts'; +import { drawMouseGrid } from './chart/drawMouseGrid.ts'; + +export const renderChart = ( + upperChartCanvas: HTMLCanvasElement, + lowerChartCanvas: HTMLCanvasElement, + upperChartYCanvas: HTMLCanvasElement, + lowerChartYCanvas: HTMLCanvasElement, + chartXCanvas: HTMLCanvasElement, + chartData: StockChartUnit[], + mousePosition: MousePositionType, + dataRange: { start: number; end: number }, + upperLabelNum: number, + lowerLabelNum: number, + moveAverageToggle: boolean, + setMouseIndex: ( + value: ((prevState: number | null) => number | null) | number | null, + ) => void, +) => { + const UpperChartCtx = upperChartCanvas.getContext('2d'); + const LowerChartCtx = lowerChartCanvas.getContext('2d'); + const UpperYCtx = upperChartYCanvas.getContext('2d'); + const LowerYCtx = lowerChartYCanvas.getContext('2d'); + const ChartXCtx = chartXCanvas.getContext('2d'); + const displayData = chartData.slice(dataRange.start, dataRange.end + 1); + if ( + !UpperChartCtx || + !LowerChartCtx || + !UpperYCtx || + !LowerYCtx || + !ChartXCtx + ) + return; + + drawChartGrid( + UpperChartCtx, + upperChartCanvas.width - padding.left - padding.right, + upperChartCanvas.height - padding.top - padding.bottom, + upperLabelNum, + LowerChartCtx, + lowerChartCanvas.width - padding.left - padding.right, + lowerChartCanvas.height - padding.top - padding.bottom, + lowerLabelNum, + displayData, + padding, + ); + + if (moveAverageToggle) { + drawLineChart( + UpperChartCtx, + displayData, + 0, + 0, + upperChartCanvas.width - padding.left - padding.right, + upperChartCanvas.height - padding.top - padding.bottom, + padding, + 0.1, + ); + } + + drawCandleChart( + UpperChartCtx, + displayData, + 0, + 0, + upperChartCanvas.width - padding.left - padding.right, + upperChartCanvas.height - padding.top - padding.bottom, + padding, + 0.1, + ); + + drawBarChart( + LowerChartCtx, + displayData, + lowerChartCanvas.width - padding.left - padding.right, + lowerChartCanvas.height - padding.top - padding.bottom, + padding, + ); + + drawUpperYAxis( + UpperYCtx, + displayData, + upperChartYCanvas.width - padding.left - padding.right, + upperChartYCanvas.height - padding.top - padding.bottom, + upperLabelNum, + padding, + 0.1, + mousePosition, + upperChartCanvas.width, + upperChartCanvas.height, + ); + + drawLowerYAxis( + LowerYCtx, + displayData, + lowerChartYCanvas.width - padding.left - padding.right, + lowerChartYCanvas.height - padding.top - padding.bottom, + lowerLabelNum, + padding, + mousePosition, + lowerChartCanvas.width, + lowerChartCanvas.height, + upperChartCanvas.height, + ); + + drawXAxis( + ChartXCtx, + displayData, + chartXCanvas.width - padding.left - padding.right, + chartXCanvas.height, + padding, + mousePosition, + upperChartCanvas.height + lowerChartCanvas.height, + setMouseIndex, + ); + + if ( + mousePosition.x > padding.left && + mousePosition.x < upperChartCanvas.width && + mousePosition.y > padding.top && + mousePosition.y < upperChartCanvas.height + lowerChartCanvas.height + ) { + drawMouseGrid( + UpperChartCtx, + upperChartCanvas.width - padding.left - padding.right, + upperChartCanvas.height - padding.top - padding.bottom, + LowerChartCtx, + lowerChartCanvas.width - padding.left - padding.right, + lowerChartCanvas.height - padding.top - padding.bottom, + padding, + mousePosition, + ); + } +}; From f6eeae2329600301d0f3a13d63651e19ff6d6971 Mon Sep 17 00:00:00 2001 From: Seo San Date: Mon, 2 Dec 2024 20:58:36 +0900 Subject: [PATCH 11/34] =?UTF-8?q?=F0=9F=94=A7=20fix:=20import=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/hooks/useMouseMove.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FE/src/hooks/useMouseMove.ts b/FE/src/hooks/useMouseMove.ts index c1e799a8..356919d7 100644 --- a/FE/src/hooks/useMouseMove.ts +++ b/FE/src/hooks/useMouseMove.ts @@ -1,5 +1,5 @@ import { MouseEvent, RefObject, useRef, useState } from 'react'; -import { MousePositionType } from '../components/StocksDetail/Chart.tsx'; +import { MousePositionType } from '../types.ts'; export const useMouseMove = (containerRef: RefObject) => { const rafRef = useRef(); From 1549e840039b73b3ef5fb49aae473d7e0e93bf72 Mon Sep 17 00:00:00 2001 From: Seo San Date: Tue, 3 Dec 2024 13:01:46 +0900 Subject: [PATCH 12/34] =?UTF-8?q?=F0=9F=94=A7=20fix:=20API=20=EC=A3=BC?= =?UTF-8?q?=EC=86=8C=20=EB=B3=80=EA=B2=BD=20&=20=20useEffect=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/components/StocksDetail/Header.tsx | 37 ++++++++++++----------- FE/src/service/stocks.ts | 2 +- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/FE/src/components/StocksDetail/Header.tsx b/FE/src/components/StocksDetail/Header.tsx index 122c8470..ca457b3b 100644 --- a/FE/src/components/StocksDetail/Header.tsx +++ b/FE/src/components/StocksDetail/Header.tsx @@ -35,6 +35,23 @@ export default function Header({ code, data }: StocksDetailHeaderProps) { const { isLogin } = useAuthStore(); const { toggleModal } = useLoginModalStore(); + const stockInfo: { label: string; value: string }[] = [ + { label: '시총', value: `${Number(hts_avls).toLocaleString()}억원` }, + { label: 'PER', value: `${per}배` }, + ]; + + const colorStyleBySign = + currPrdyVrssSign === '3' + ? '' + : currPrdyVrssSign < '3' + ? 'text-juga-red-60' + : 'text-juga-blue-40'; + + const percentAbsolute = Math.abs(Number(currPrdyRate)).toFixed(2); + + const plusOrMinus = + currPrdyVrssSign === '3' ? '' : currPrdyVrssSign < '3' ? '+' : '-'; + // const { debounceValue } = useDebounce(isBookmarked, 1000); // const isInitialMount = useRef(true); @@ -50,6 +67,9 @@ export default function Header({ code, data }: StocksDetailHeaderProps) { // unbookmark(code); // } // }, [code, debounceValue]); + useEffect(() => { + setIsBookmarked(is_bookmarked); + }, [is_bookmarked]); useEffect(() => { setCurrPrice(stck_prpr); @@ -77,23 +97,6 @@ export default function Header({ code, data }: StocksDetailHeaderProps) { }; }, [code]); - const stockInfo: { label: string; value: string }[] = [ - { label: '시총', value: `${Number(hts_avls).toLocaleString()}억원` }, - { label: 'PER', value: `${per}배` }, - ]; - - const colorStyleBySign = - currPrdyVrssSign === '3' - ? '' - : currPrdyVrssSign < '3' - ? 'text-juga-red-60' - : 'text-juga-blue-40'; - - const percentAbsolute = Math.abs(Number(currPrdyRate)).toFixed(2); - - const plusOrMinus = - currPrdyVrssSign === '3' ? '' : currPrdyVrssSign < '3' ? '+' : '-'; - return (
diff --git a/FE/src/service/stocks.ts b/FE/src/service/stocks.ts index f62154fe..23a9f382 100644 --- a/FE/src/service/stocks.ts +++ b/FE/src/service/stocks.ts @@ -30,7 +30,7 @@ export async function getStocksChartDataByCode( export async function unsubscribe(code: string) { return fetch( - `${import.meta.env.VITE_API_URL}/stocks/trade-history/${code}/unsubscribe`, + `${import.meta.env.VITE_API_URL}/stocks/trade-history/unsubscribe?stockCode=${code}`, { headers: { 'Content-Type': 'application/json' }, }, From 219dc86adb36756fa5dd9705ec159f4bd4636370 Mon Sep 17 00:00:00 2001 From: Seo San Date: Tue, 3 Dec 2024 13:29:36 +0900 Subject: [PATCH 13/34] =?UTF-8?q?=E2=9C=A8=20feat:=20SEO=20=EC=B5=9C?= =?UTF-8?q?=EC=A0=81=ED=99=94=20#254=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20alt?= =?UTF-8?q?=20=EC=86=8D=EC=84=B1=20=EC=B6=94=EA=B0=80.=20react-helmet?= =?UTF-8?q?=EC=9D=84=20=EC=9D=B4=EC=9A=A9=ED=95=B4=20meta=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=EC=99=80=20title=20=EC=B6=94=EA=B0=80.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/package-lock.json | 11 +++++++++++ FE/package.json | 1 + FE/public/robots.txt | 2 ++ FE/src/App.tsx | 9 +++++++++ FE/src/components/Header.tsx | 2 +- FE/src/page/Home.tsx | 5 +++++ FE/src/page/MyPage.tsx | 5 +++++ FE/src/page/Rank.tsx | 7 ++++++- FE/src/page/StocksDetail.tsx | 5 +++++ 9 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 FE/public/robots.txt diff --git a/FE/package-lock.json b/FE/package-lock.json index 76198c20..09bb14fb 100644 --- a/FE/package-lock.json +++ b/FE/package-lock.json @@ -26,6 +26,7 @@ "@types/node": "^22.9.1", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", + "@types/react-helmet": "^6.1.11", "@vitejs/plugin-react": "^4.3.3", "autoprefixer": "^10.4.20", "eslint": "^9.13.0", @@ -1384,6 +1385,16 @@ "@types/react": "*" } }, + "node_modules/@types/react-helmet": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@types/react-helmet/-/react-helmet-6.1.11.tgz", + "integrity": "sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.12.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz", diff --git a/FE/package.json b/FE/package.json index cd6f9135..e0a75f6a 100644 --- a/FE/package.json +++ b/FE/package.json @@ -28,6 +28,7 @@ "@types/node": "^22.9.1", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", + "@types/react-helmet": "^6.1.11", "@vitejs/plugin-react": "^4.3.3", "autoprefixer": "^10.4.20", "eslint": "^9.13.0", diff --git a/FE/public/robots.txt b/FE/public/robots.txt new file mode 100644 index 00000000..f5a734c4 --- /dev/null +++ b/FE/public/robots.txt @@ -0,0 +1,2 @@ +user-agent: * +allow: / \ No newline at end of file diff --git a/FE/src/App.tsx b/FE/src/App.tsx index 9939f1f4..d9ca391b 100644 --- a/FE/src/App.tsx +++ b/FE/src/App.tsx @@ -14,6 +14,7 @@ import MyPage from 'page/MyPage'; import Rank from 'page/Rank.tsx'; import { ToastContainer } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; +import { Helmet } from 'react-helmet'; function App() { return ( @@ -35,6 +36,14 @@ export default App; function Layout() { return ( <> + + + + JuGa +
diff --git a/FE/src/components/Header.tsx b/FE/src/components/Header.tsx index a4d65d5f..ae5cec10 100644 --- a/FE/src/components/Header.tsx +++ b/FE/src/components/Header.tsx @@ -49,7 +49,7 @@ export default function Header() { type='image/webp' className={'h-[32px]'} /> - + {'Logo'}

JuGa

diff --git a/FE/src/page/Home.tsx b/FE/src/page/Home.tsx index d62a960a..abd6c99a 100644 --- a/FE/src/page/Home.tsx +++ b/FE/src/page/Home.tsx @@ -1,10 +1,15 @@ import TopFive from 'components/TopFive'; import StockIndex from 'components/StockIndex/index.tsx'; import News from 'components/News/News.tsx'; +import { Helmet } from 'react-helmet'; export default function Home() { return ( <> + + + JuGa + diff --git a/FE/src/page/MyPage.tsx b/FE/src/page/MyPage.tsx index a5e35263..3a230519 100644 --- a/FE/src/page/MyPage.tsx +++ b/FE/src/page/MyPage.tsx @@ -4,6 +4,7 @@ import MyInfo from 'components/Mypage/MyInfo'; import Nav from 'components/Mypage/Nav'; import Order from 'components/Mypage/Order'; import { useSearchParams } from 'react-router-dom'; +import { Helmet } from 'react-helmet'; export default function MyPage() { const [searchParams] = useSearchParams(); @@ -11,6 +12,10 @@ export default function MyPage() { return (
+ + + JuGa | MyPage +
@@ -13,7 +13,6 @@ export default function TableColumn({ viewMode }: Props) { - {/**/} @@ -27,7 +26,6 @@ export default function TableColumn({ viewMode }: Props) { - {/**/} From cf19387b66eaaaaf5a053b7648a42e10a58abeda Mon Sep 17 00:00:00 2001 From: Seo San Date: Tue, 3 Dec 2024 14:21:50 +0900 Subject: [PATCH 17/34] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20useCallb?= =?UTF-8?q?ack=EC=9D=84=20=EC=9D=B4=EC=9A=A9=ED=95=B4=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=ED=95=A8=EC=88=98=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=A0=9C=EC=96=B4=20#254?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/StocksDetail/TradeSection/BuySection.tsx | 9 +++++---- .../components/StocksDetail/TradeSection/SellSection.tsx | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/FE/src/components/StocksDetail/TradeSection/BuySection.tsx b/FE/src/components/StocksDetail/TradeSection/BuySection.tsx index 88cbc29c..ba17325d 100644 --- a/FE/src/components/StocksDetail/TradeSection/BuySection.tsx +++ b/FE/src/components/StocksDetail/TradeSection/BuySection.tsx @@ -2,6 +2,7 @@ import { ChangeEvent, FocusEvent, FormEvent, + useCallback, useEffect, useRef, useState, @@ -44,17 +45,17 @@ export default function BuySection({ code, detailInfo }: BuySectionProps) { const [lackAssetFlag, setLackAssetFlag] = useState(false); const timerRef = useRef(null); - const handlePriceChange = (e: ChangeEvent) => { + const handlePriceChange = useCallback((e: ChangeEvent) => { const s = e.target.value.replace(/,/g, ''); if (!isNumericString(s)) return; setCurrPrice(s); - }; + }, []); - const handleCountChange = (e: ChangeEvent) => { + const handleCountChange = useCallback((e: ChangeEvent) => { const s = e.target.value; if (!isNumericString(s)) return; setCount(+s); - }; + }, []); if (isLoading) return
loading
; if (!data) return
No data
; diff --git a/FE/src/components/StocksDetail/TradeSection/SellSection.tsx b/FE/src/components/StocksDetail/TradeSection/SellSection.tsx index 085b49ce..0cf493a8 100644 --- a/FE/src/components/StocksDetail/TradeSection/SellSection.tsx +++ b/FE/src/components/StocksDetail/TradeSection/SellSection.tsx @@ -6,6 +6,7 @@ import { ChangeEvent, FocusEvent, FormEvent, + useCallback, useEffect, useRef, useState, @@ -56,17 +57,17 @@ export default function SellSection({ code, detailInfo }: SellSectionProps) { const totalPrice = +currPrice * count; const plRate = calcYield(avg_price, +currPrice); - const handlePriceChange = (e: ChangeEvent) => { + const handlePriceChange = useCallback((e: ChangeEvent) => { const s = e.target.value.replace(/,/g, ''); if (!isNumericString(s)) return; setCurrPrice(s); - }; + }, []); - const handleCountChange = (e: ChangeEvent) => { + const handleCountChange = useCallback((e: ChangeEvent) => { const s = e.target.value; if (!isNumericString(s)) return; setCount(+s); - }; + }, []); const handlePriceInputBlur = (e: FocusEvent) => { const n = +e.target.value.replace(/,/g, ''); From 922934656f8a694420fa5723eac2fe4321bd1efb Mon Sep 17 00:00:00 2001 From: dongree Date: Tue, 3 Dec 2024 15:47:12 +0900 Subject: [PATCH 18/34] =?UTF-8?q?=E2=9C=A8=20feat:=20GlobalErrorFallback?= =?UTF-8?q?=20UI=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/components/GlobalErrorFallback.tsx | 31 +++++++++++++++++++++++ FE/src/main.tsx | 21 ++++++++------- 2 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 FE/src/components/GlobalErrorFallback.tsx diff --git a/FE/src/components/GlobalErrorFallback.tsx b/FE/src/components/GlobalErrorFallback.tsx new file mode 100644 index 00000000..b1c99052 --- /dev/null +++ b/FE/src/components/GlobalErrorFallback.tsx @@ -0,0 +1,31 @@ +import { FallbackProps } from 'react-error-boundary'; +import logoPng from 'assets/logo.png'; +import logoWebp from 'assets/logo.webp'; + +export default function GlobalErrorFallback({ error }: FallbackProps) { + return ( +
+
+ + + Logo + +
+

오류가 발생했습니다!

+

+ 문제가 지속적으로 발생하면 관리자에게 문의해주세요. +

+
+        {error.message}
+      
+ +
+ ); +} diff --git a/FE/src/main.tsx b/FE/src/main.tsx index 8e7cbb66..08dac630 100644 --- a/FE/src/main.tsx +++ b/FE/src/main.tsx @@ -8,8 +8,8 @@ import { QueryErrorResetBoundary, } from '@tanstack/react-query'; import { ErrorBoundary } from 'react-error-boundary'; -import FallbackUI from 'components/FallbackUI.tsx'; import { HelmetProvider } from 'react-helmet-async'; +import GlobalErrorFallback from 'components/GlobalErrorFallback.tsx'; const queryClient = new QueryClient({ defaultOptions: { @@ -22,15 +22,18 @@ const queryClient = new QueryClient({ createRoot(document.getElementById('root')!).render( - - {({ reset }) => ( - - + + + {({ reset }) => ( + - - - )} - + + )} + + , ); From 1b9b2c44f030fac6f364908e3209b44b97c4cf01 Mon Sep 17 00:00:00 2001 From: dongree Date: Tue, 3 Dec 2024 15:48:22 +0900 Subject: [PATCH 19/34] =?UTF-8?q?=F0=9F=94=A7=20fix:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=95=88=20=EB=90=98=EC=97=88=EC=9D=84=20=EB=95=8C?= =?UTF-8?q?=20mypage=20=EC=A0=91=EA=B7=BC=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/components/Header.tsx | 8 +++++--- FE/src/components/Mypage/Account.tsx | 11 +++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/FE/src/components/Header.tsx b/FE/src/components/Header.tsx index ae5cec10..b9589508 100644 --- a/FE/src/components/Header.tsx +++ b/FE/src/components/Header.tsx @@ -7,6 +7,7 @@ import logoPng from 'assets/logo.png'; import logoWebp from 'assets/logo.webp'; import { checkAuth, logout } from 'service/auth.ts'; import { useEffect } from 'react'; +import Toast from './Toast'; export default function Header() { const { toggleModal } = useLoginModalStore(); @@ -29,6 +30,7 @@ export default function Header() { const handleLogout = () => { logout().then(() => { setIsLogin(false); + Toast({ message: '로그아웃 되었습니다!', type: 'success' }); }); }; @@ -58,20 +60,20 @@ export default function Header() {
No data available
종가 등락률 거래량(주)거래대금시가 고가 저가체결가 체결량(주) 등락률거래량(주)시간