From aced76d213edef4148ea86bcc46c1e48ab00a19f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B3=BD=EC=98=88=EA=B2=BD?= Date: Mon, 28 Jul 2025 09:04:19 +0900 Subject: [PATCH 01/20] =?UTF-8?q?[feat]=20=EC=8B=A4=EC=A0=84=ED=88=AC?= =?UTF-8?q?=EC=9E=90=20->=20=EC=8B=A4=EC=A0=84=EC=98=88=EC=B8=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Navbar/navbar.client.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Navbar/navbar.client.tsx b/src/components/Navbar/navbar.client.tsx index b885844..506b2d6 100644 --- a/src/components/Navbar/navbar.client.tsx +++ b/src/components/Navbar/navbar.client.tsx @@ -10,7 +10,7 @@ import Image from "next/image"; const menuItems = [ { label: "홈", href: "/" }, { label: "연습문제", href: "/practice" }, - { label: "실전투자", href: "/investment", dynamic: true }, + { label: "실전예측", href: "/investment", dynamic: true }, { label: "랭킹", href: "/ranking" }, { label: "마이페이지", href: "/mypage" }, ]; @@ -52,7 +52,7 @@ export default function Navbar() { router.push("/investment"); } } catch (err) { - console.error("실전투자 이동 중 오류:", err); + console.error("실전예측 이동 중 오류:", err); router.push("/investment"); } }; From 5ffd800db933bc1b1eebeb986b63417199a588b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B3=BD=EC=98=88=EA=B2=BD?= Date: Mon, 28 Jul 2025 09:05:32 +0900 Subject: [PATCH 02/20] =?UTF-8?q?[feat]=20=EC=8B=A4=EC=A0=84=ED=88=AC?= =?UTF-8?q?=EC=9E=90=20->=20=EC=8B=A4=EC=A0=84=EC=98=88=EC=B8=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/blocks/MainHome/MainHome.client.tsx | 2 +- .../blocks/MyPageInvestment/MyPageInvestment.client.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/blocks/MainHome/MainHome.client.tsx b/src/components/blocks/MainHome/MainHome.client.tsx index e5e5175..53a518d 100644 --- a/src/components/blocks/MainHome/MainHome.client.tsx +++ b/src/components/blocks/MainHome/MainHome.client.tsx @@ -36,7 +36,7 @@ export default function MainHomeClient() { router.push("/investment"); } } catch (err) { - console.error("실전투자 이동 중 오류:", err); + console.error("실전예측 이동 중 오류:", err); router.push("/investment"); } }; diff --git a/src/components/blocks/MyPageInvestment/MyPageInvestment.client.tsx b/src/components/blocks/MyPageInvestment/MyPageInvestment.client.tsx index 3a59787..0917a3a 100644 --- a/src/components/blocks/MyPageInvestment/MyPageInvestment.client.tsx +++ b/src/components/blocks/MyPageInvestment/MyPageInvestment.client.tsx @@ -124,7 +124,7 @@ export default function MyPageInvestmentClient() { return (
-

실전투자 히스토리

+

실전예측 히스토리

From e7d2b06f6096fd72d2b997be8f55faed7a35f4b6 Mon Sep 17 00:00:00 2001 From: jiminseon <20201020@dongduk.ac.kr> Date: Mon, 28 Jul 2025 14:06:44 +0900 Subject: [PATCH 03/20] =?UTF-8?q?[refactor]=20=ED=88=B4=ED=8C=81=EC=83=89?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/charts/InvestCandleChart.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/charts/InvestCandleChart.tsx b/src/components/charts/InvestCandleChart.tsx index 4304324..61ce3b9 100644 --- a/src/components/charts/InvestCandleChart.tsx +++ b/src/components/charts/InvestCandleChart.tsx @@ -1011,7 +1011,7 @@ export default function InvestCandleChart({ : {todayPrice.toLocaleString()}
- + 예측값 : {dot.close.toLocaleString()} @@ -1027,7 +1027,7 @@ export default function InvestCandleChart({ ) { return (
- + 예측값 : {dot.close.toLocaleString()} From d4af097cfa74202a843f5777e0bb1d75d84cc51a Mon Sep 17 00:00:00 2001 From: Jihwan Lee Date: Mon, 28 Jul 2025 14:28:16 +0900 Subject: [PATCH 04/20] =?UTF-8?q?=EC=A0=81=EC=A4=91=EB=A5=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyPageInvestment/MyPageInvestment.client.tsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/components/blocks/MyPageInvestment/MyPageInvestment.client.tsx b/src/components/blocks/MyPageInvestment/MyPageInvestment.client.tsx index 3a59787..d5fccec 100644 --- a/src/components/blocks/MyPageInvestment/MyPageInvestment.client.tsx +++ b/src/components/blocks/MyPageInvestment/MyPageInvestment.client.tsx @@ -101,8 +101,20 @@ export default function MyPageInvestmentClient() { }); const validScores = stockResult.stocks - .map((item: any) => item.cumulative_score) - .filter((score: any) => typeof score === "number"); + .map((item: any) => { + const predictCount = + investResult.find( + (s: any) => + s.stock_code === item.stock_code?._id || + s.stock_id === item._id + )?.scores?.length ?? 0; + + return predictCount > 0 && typeof item.cumulative_score === "number" + ? item.cumulative_score + : null; + }) + .filter((score: any) => score !== null); + const avg = validScores.length > 0 ? Math.round( From 6da60f6f6e6b25593ba8a346646a787f08ae4135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B3=BD=EC=98=88=EA=B2=BD?= Date: Mon, 28 Jul 2025 15:00:59 +0900 Subject: [PATCH 05/20] [feat] chart error --- .../InvestmentStock.client.tsx | 2 +- .../PracticePage/PracticePage.client.tsx | 2 +- src/components/charts/InvestCandleChart.tsx | 188 ++++++++++++------ 3 files changed, 129 insertions(+), 63 deletions(-) diff --git a/src/components/blocks/Investment/InvestmentStock/InvestmentStock.client.tsx b/src/components/blocks/Investment/InvestmentStock/InvestmentStock.client.tsx index 8b480bf..21cc373 100644 --- a/src/components/blocks/Investment/InvestmentStock/InvestmentStock.client.tsx +++ b/src/components/blocks/Investment/InvestmentStock/InvestmentStock.client.tsx @@ -393,7 +393,7 @@ export default function InvestmentStockClient() { )} setShowIndicators((prev) => !prev)} > {showIndicators ? "– 보조지표 접기" : "+ 보조지표 설정"} diff --git a/src/components/blocks/PracticePage/PracticePage.client.tsx b/src/components/blocks/PracticePage/PracticePage.client.tsx index 7f82e91..b30867c 100644 --- a/src/components/blocks/PracticePage/PracticePage.client.tsx +++ b/src/components/blocks/PracticePage/PracticePage.client.tsx @@ -307,7 +307,7 @@ export default function PracticeClient() { )} setShowIndicators((prev) => !prev)} > {showIndicators ? "– 보조지표 접기" : "+ 보조지표 설정"} diff --git a/src/components/charts/InvestCandleChart.tsx b/src/components/charts/InvestCandleChart.tsx index 4304324..bcf645c 100644 --- a/src/components/charts/InvestCandleChart.tsx +++ b/src/components/charts/InvestCandleChart.tsx @@ -385,14 +385,32 @@ export default function InvestCandleChart({ visibleCandles > 1 ? chartWidth / (visibleCandles - 1) : chartWidth; const candleWidth = Math.min(40, candleSpacing * 0.7, 24); + // function getLinePoints( + // arr: (number | null)[], + // candleSpacing: number, + // getY: (v: number) => number + // ) { + // return arr + // .map((val, i) => + // typeof val === "number" && !isNaN(val) + // ? `${i * candleSpacing},${getY(val)}` + // : null + // ) + // .filter(Boolean) + // .join(" "); + // } function getLinePoints( arr: (number | null)[], candleSpacing: number, - getY: (v: number) => number + getY: (v: number) => number, + slicedData: Candle[] ) { return arr .map((val, i) => - typeof val === "number" && !isNaN(val) + typeof val === "number" && + !isNaN(val) && + slicedData[i] && + !isDotOnlyCandle(slicedData[i]) ? `${i * candleSpacing},${getY(val)}` : null ) @@ -400,24 +418,72 @@ export default function InvestCandleChart({ .join(" "); } - const ma5Points = getLinePoints(ma5_visible, candleSpacing, getY); - const ma20Points = getLinePoints(ma20_visible, candleSpacing, getY); - const ma60Points = getLinePoints(ma60_visible, candleSpacing, getY); - const ma120Points = getLinePoints(ma120_visible, candleSpacing, getY); + // 캔들이 정상적인 날인지 확인하는 유틸 함수 + function isDotOnlyCandle(candle: Candle) { + return ( + candle.open === candle.close && + candle.high === candle.close && + candle.low === candle.close && + candle.volume === 0 + ); + } + + // const ma5Points = getLinePoints(ma5_visible, candleSpacing, getY); + // const ma20Points = getLinePoints(ma20_visible, candleSpacing, getY); + // const ma60Points = getLinePoints(ma60_visible, candleSpacing, getY); + // const ma120Points = getLinePoints(ma120_visible, candleSpacing, getY); + // const bb_upper_points = getLinePoints( + // bb_visible.map((b) => b?.upper), + // candleSpacing, + // getY + // ); + // const bb_middle_points = getLinePoints( + // bb_visible.map((b) => b?.middle), + // candleSpacing, + // getY + // ); + // const bb_lower_points = getLinePoints( + // bb_visible.map((b) => b?.lower), + // candleSpacing, + // getY + // ); + const ma5Points = getLinePoints(ma5_visible, candleSpacing, getY, slicedData); + const ma20Points = getLinePoints( + ma20_visible, + candleSpacing, + getY, + slicedData + ); + const ma60Points = getLinePoints( + ma60_visible, + candleSpacing, + getY, + slicedData + ); + const ma120Points = getLinePoints( + ma120_visible, + candleSpacing, + getY, + slicedData + ); + const bb_upper_points = getLinePoints( bb_visible.map((b) => b?.upper), candleSpacing, - getY + getY, + slicedData ); const bb_middle_points = getLinePoints( bb_visible.map((b) => b?.middle), candleSpacing, - getY + getY, + slicedData ); const bb_lower_points = getLinePoints( bb_visible.map((b) => b?.lower), candleSpacing, - getY + getY, + slicedData ); const chartRef = useRef(null); @@ -481,7 +547,13 @@ export default function InvestCandleChart({ const lowerPoints: string[] = []; bb_visible.forEach((bb, i) => { - if (bb?.upper && bb?.lower) { + const candle = slicedData[i]; + if ( + bb?.upper && + bb?.lower && + candle && // candle 존재 확인 + !isDotOnlyCandle(candle) // dot 전용 데이터 제외 + ) { const x = i * candleSpacing; upperPoints.push(`${x},${getY(bb.upper)}`); lowerPoints.push(`${x},${getY(bb.lower)}`); @@ -490,12 +562,11 @@ export default function InvestCandleChart({ if (upperPoints.length === 0) return ""; - // 상단선을 그리고, 하단선을 역순으로 연결해서 닫힌 영역 만들기 const pathData = [ - `M ${upperPoints[0]}`, // 시작점으로 이동 - `L ${upperPoints.slice(1).join(" L ")}`, // 상단선 그리기 - `L ${lowerPoints.slice().reverse().join(" L ")}`, // 하단선을 역순으로 그리기 - "Z", // path 닫기 + `M ${upperPoints[0]}`, + `L ${upperPoints.slice(1).join(" L ")}`, + `L ${lowerPoints.slice().reverse().join(" L ")}`, + "Z", ].join(" "); return pathData; @@ -504,7 +575,7 @@ export default function InvestCandleChart({ // --- 렌더 --- return (
- typeof val === "number" && isFinite(val) + typeof val === "number" && + isFinite(val) && + !isDotOnlyCandle(slicedData[i]) ? `${i * candleSpacing},${(1 - val / 100) * RSI_HEIGHT}` : null ) .filter((v): v is string => v !== null) - .join(" ")} opacity={0.96} /> @@ -995,13 +1067,15 @@ export default function InvestCandleChart({
{/* 일반 candle 값 or dot 전용 candle 값 구분 */} {(() => { + // dotData도 있는 날짜면 const dot = dotData?.find((d) => dayjs(d.date).isSame(tooltip.data!.date, "day") ); + // "오늘" 날짜인지 확인 const isToday = dayjs(tooltip.data!.date).isSame(dayjs(), "day"); - // 실시간 시세 + 예측값만 있는 경우 (오늘) - if (isToday && todayPrice && dot?.close) { + // 오늘이고 todayPrice가 있을 때 (실시간) + if (isToday && todayPrice && dot && dot.close) { return ( <>
@@ -1011,7 +1085,7 @@ export default function InvestCandleChart({ : {todayPrice.toLocaleString()}
- + 예측값 : {dot.close.toLocaleString()} @@ -1020,55 +1094,47 @@ export default function InvestCandleChart({ ); } - // 예측값만 있는 경우 - if ( - dot?.close && - (!tooltip.data?.open || tooltip.data.volume === 0) - ) { + // dotData(예측값)가 있는 경우 + if (dot) { return (
예측값 - + {" "} : {dot.close.toLocaleString()}
); } - - // 일반 캔들값 (or 예측값도 있는 경우 같이 표시) - const rows = []; - - rows.push( -
시: {tooltip.data.open.toLocaleString()}
, -
고: {tooltip.data.high.toLocaleString()}
, -
저: {tooltip.data.low.toLocaleString()}
, -
종: {tooltip.data.close.toLocaleString()}
, -
- 거래량: {tooltip.data.volume.toLocaleString()} -
, -
RSI: {rsi}
- ); - - if (dot?.close) { - rows.push( -
- - 예측값 - - : {dot.close.toLocaleString()} -
, -
- 오차: {(dot.close - tooltip.data.close).toFixed(2)} ( - {( - ((dot.close - tooltip.data.close) / tooltip.data.close) * - 100 - ).toFixed(2)} - %) + // 일반 candle 값 표기 + return ( + <> +
시: {tooltip.data.open.toLocaleString()}
+
고: {tooltip.data.high.toLocaleString()}
+
저: {tooltip.data.low.toLocaleString()}
+
종: {tooltip.data.close.toLocaleString()}
+
거래량: {tooltip.data.volume.toLocaleString()}
+
+ {/* RSI:{" "} + {typeof rsi_visible[tooltip.idx] === "number" + ? rsi_visible[tooltip.idx].toFixed(2) + : "-"} */} + RSI: {rsi}
- ); - } - - return rows; + {/* dot값이 겹치는 경우 오차 등도 표시 */} + {dot && (dot as any).close !== undefined && ( +
+ 오차: {((dot as any).close - tooltip.data.close).toFixed(2)}{" "} + ( + {( + (((dot as any).close - tooltip.data.close) / + tooltip.data.close) * + 100 + ).toFixed(2)} + %) +
+ )} + + ); })()} {/* 뉴스 영역 그대로 */} {tooltipNews.length > 0 && ( From f24c6a854633fa37db37ded522df661db0695ee5 Mon Sep 17 00:00:00 2001 From: Jihwan Lee Date: Mon, 28 Jul 2025 15:51:37 +0900 Subject: [PATCH 06/20] =?UTF-8?q?=EB=A7=88=EC=9D=B4=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EB=B2=84=ED=8A=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/blocks/MyPagePractice/MyPagePractice.client.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/blocks/MyPagePractice/MyPagePractice.client.tsx b/src/components/blocks/MyPagePractice/MyPagePractice.client.tsx index 1b49a68..e91d084 100644 --- a/src/components/blocks/MyPagePractice/MyPagePractice.client.tsx +++ b/src/components/blocks/MyPagePractice/MyPagePractice.client.tsx @@ -136,7 +136,7 @@ export default function MyPagePracticeClient() {
From 4eb4b0ac0740da1624b98798b006eee1cff00c13 Mon Sep 17 00:00:00 2001 From: Jihwan Lee Date: Mon, 28 Jul 2025 16:39:10 +0900 Subject: [PATCH 07/20] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2081322..0a8daa2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# UpAndDown-Client -About [오르락내리락] 과거의 차트로 공부하고! 나만의 종목으로 예측까지 해보는 주식 차트 학습 서비스 +# Candly-Client +About [Candly] 과거의 차트로 공부하고! 나만의 종목으로 예측까지 해보는 주식 차트 학습 서비스 From ffd639834b59effc702955fc6317cddf9a4a6d6d Mon Sep 17 00:00:00 2001 From: jiminseon <20201020@dongduk.ac.kr> Date: Mon, 28 Jul 2025 16:55:11 +0900 Subject: [PATCH 08/20] =?UTF-8?q?[modify]=20=EB=8B=B5=EB=B3=80=20disabled?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/blocks/PracticePage/PracticePage.client.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/blocks/PracticePage/PracticePage.client.tsx b/src/components/blocks/PracticePage/PracticePage.client.tsx index b30867c..a769e8f 100644 --- a/src/components/blocks/PracticePage/PracticePage.client.tsx +++ b/src/components/blocks/PracticePage/PracticePage.client.tsx @@ -416,6 +416,7 @@ export default function PracticeClient() { onChange={(e) => setInput(e.target.value)} placeholder="답변을 입력하세요" maxLength={300} + disabled={isAnswered} className="w-full h-32 p-4 rounded border border-gray-600 bg-transparent resize-none focus:outline-none" />
@@ -425,7 +426,7 @@ export default function PracticeClient() { From 2237655d569362943ba40b6be9e8544209491b4b Mon Sep 17 00:00:00 2001 From: EunseoJun Date: Mon, 28 Jul 2025 17:13:04 +0900 Subject: [PATCH 09/20] =?UTF-8?q?[Fix]=EB=8D=A7=EC=85=88=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PracticePage/PracticePage.client.tsx | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/components/blocks/PracticePage/PracticePage.client.tsx b/src/components/blocks/PracticePage/PracticePage.client.tsx index 7f82e91..442c676 100644 --- a/src/components/blocks/PracticePage/PracticePage.client.tsx +++ b/src/components/blocks/PracticePage/PracticePage.client.tsx @@ -56,7 +56,7 @@ export default function PracticeClient() { // if (!params.problemId) return; // fetchMyPracticeAnswer(params.problemId).then((result) => { // if (result) { - // console.log("이미 푼 문제!", result); // 🔥 여기에 찍힘! + // console.log("이미 푼 문제!", result); // } else { // console.log("아직 푼 적 없는 문제입니다."); // } @@ -111,22 +111,33 @@ export default function PracticeClient() { setLoading(false); return; } + setGradeResult(data); try { const token = sessionStorage.getItem("token") || localStorage.getItem("accessToken"); + // breakdown 점수 직접 합산 + const breakdown = data.breakdown || {}; + const logic = Number(breakdown.logic ?? 0); + const technical = Number(breakdown.technical ?? 0); + const macroEconomy = Number(breakdown.macroEconomy ?? 0); + const marketIssues = Number(breakdown.marketIssues ?? 0); + const quantEvidence = Number(breakdown.quantEvidence ?? 0); + const score = + logic + technical + macroEconomy + marketIssues + quantEvidence; + const practiceScoreData = { problem_id: params.problemId, answer: input, - score: data.score, + score, // breakdown 다섯 항목 합산 점수! feedback: data.feedback, - logic: data.breakdown?.logic, - technical: data.breakdown?.technical, - macroEconomy: data.breakdown?.macroEconomy, - marketIssues: data.breakdown?.marketIssues, - quantEvidence: data.breakdown?.quantEvidence, + logic, + technical, + macroEconomy, + marketIssues, + quantEvidence, date: new Date().toISOString(), }; //console.log("채점 결과", data); From 9dcec700ce2b538f25fc4fded3c0137c4b816414 Mon Sep 17 00:00:00 2001 From: EunseoJun Date: Mon, 28 Jul 2025 17:16:56 +0900 Subject: [PATCH 10/20] =?UTF-8?q?[Fix]=EB=8D=A7=EC=85=88=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/blocks/PracticePage/PracticePage.client.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/blocks/PracticePage/PracticePage.client.tsx b/src/components/blocks/PracticePage/PracticePage.client.tsx index 442c676..9fe798e 100644 --- a/src/components/blocks/PracticePage/PracticePage.client.tsx +++ b/src/components/blocks/PracticePage/PracticePage.client.tsx @@ -140,7 +140,6 @@ export default function PracticeClient() { quantEvidence, date: new Date().toISOString(), }; - //console.log("채점 결과", data); await postPracticeScore(token, practiceScoreData); toast.success("채점 및 저장 완료!"); setFeedback(data.feedback || "피드백 없음."); From 00969ce941e2ae97b95e31140b94451cb4f77286 Mon Sep 17 00:00:00 2001 From: Jihwan Lee Date: Mon, 28 Jul 2025 21:52:51 +0900 Subject: [PATCH 11/20] =?UTF-8?q?avg=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/blocks/MyPagePractice/MyPagePractice.client.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/blocks/MyPagePractice/MyPagePractice.client.tsx b/src/components/blocks/MyPagePractice/MyPagePractice.client.tsx index e91d084..3b3b38a 100644 --- a/src/components/blocks/MyPagePractice/MyPagePractice.client.tsx +++ b/src/components/blocks/MyPagePractice/MyPagePractice.client.tsx @@ -52,7 +52,7 @@ export default function MyPagePracticeClient() { (acc, cur) => acc + (cur.score ?? 0), 0 ); - setAvgScore(scoreList.length == 0 ? 0 : sum / scoreList.length); + setAvgScore(scoreList.length === 0 ? 0 : parseFloat((sum / scoreList.length).toFixed(1))); // 항목별 만점 기준 const maxScores = { logic: 15, From 514074644c51879f459c489a8d387f27db76872c Mon Sep 17 00:00:00 2001 From: jiminseon <20201020@dongduk.ac.kr> Date: Tue, 29 Jul 2025 08:00:51 +0900 Subject: [PATCH 12/20] =?UTF-8?q?[modify]=20=ED=88=B4=ED=8C=81=20=EB=8B=A4?= =?UTF-8?q?=EC=8B=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/charts/InvestCandleChart.tsx | 79 +++++++++++---------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/src/components/charts/InvestCandleChart.tsx b/src/components/charts/InvestCandleChart.tsx index ae49855..7daac28 100644 --- a/src/components/charts/InvestCandleChart.tsx +++ b/src/components/charts/InvestCandleChart.tsx @@ -1067,15 +1067,13 @@ export default function InvestCandleChart({
{/* 일반 candle 값 or dot 전용 candle 값 구분 */} {(() => { - // dotData도 있는 날짜면 const dot = dotData?.find((d) => dayjs(d.date).isSame(tooltip.data!.date, "day") ); - // "오늘" 날짜인지 확인 const isToday = dayjs(tooltip.data!.date).isSame(dayjs(), "day"); - // 오늘이고 todayPrice가 있을 때 (실시간) - if (isToday && todayPrice && dot && dot.close) { + // 실시간 시세 + 예측값만 있는 경우 (오늘) + if (isToday && todayPrice && dot?.close) { return ( <>
@@ -1086,7 +1084,6 @@ export default function InvestCandleChart({
- 예측값 : {dot.close.toLocaleString()} @@ -1095,47 +1092,55 @@ export default function InvestCandleChart({ ); } - // dotData(예측값)가 있는 경우 - if (dot) { + // 예측값만 있는 경우 + if ( + dot?.close && + (!tooltip.data?.open || tooltip.data.volume === 0) + ) { return (
예측값 - {" "} + : {dot.close.toLocaleString()}
); } - // 일반 candle 값 표기 - return ( - <> -
시: {tooltip.data.open.toLocaleString()}
-
고: {tooltip.data.high.toLocaleString()}
-
저: {tooltip.data.low.toLocaleString()}
-
종: {tooltip.data.close.toLocaleString()}
-
거래량: {tooltip.data.volume.toLocaleString()}
-
- {/* RSI:{" "} - {typeof rsi_visible[tooltip.idx] === "number" - ? rsi_visible[tooltip.idx].toFixed(2) - : "-"} */} - RSI: {rsi} -
- {/* dot값이 겹치는 경우 오차 등도 표시 */} - {dot && (dot as any).close !== undefined && ( -
- 오차: {((dot as any).close - tooltip.data.close).toFixed(2)}{" "} - ( - {( - (((dot as any).close - tooltip.data.close) / - tooltip.data.close) * - 100 - ).toFixed(2)} - %) -
- )} - + + // 일반 캔들값 (or 예측값도 있는 경우 같이 표시) + const rows = []; + + rows.push( +
시: {tooltip.data.open.toLocaleString()}
, +
고: {tooltip.data.high.toLocaleString()}
, +
저: {tooltip.data.low.toLocaleString()}
, +
종: {tooltip.data.close.toLocaleString()}
, +
+ 거래량: {tooltip.data.volume.toLocaleString()} +
, +
RSI: {rsi}
); + + if (dot?.close) { + rows.push( +
+ + 예측값 + + : {dot.close.toLocaleString()} +
, +
+ 오차: {(dot.close - tooltip.data.close).toFixed(2)} ( + {( + ((dot.close - tooltip.data.close) / tooltip.data.close) * + 100 + ).toFixed(2)} + %) +
+ ); + } + + return rows; })()} {/* 뉴스 영역 그대로 */} {tooltipNews.length > 0 && ( From a77b46a54d6447767f902f52028e85b52175e6c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B3=BD=EC=98=88=EA=B2=BD?= Date: Tue, 29 Jul 2025 08:28:30 +0900 Subject: [PATCH 13/20] [feat] minor css #1 --- src/components/Navbar/navbar.client.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Navbar/navbar.client.tsx b/src/components/Navbar/navbar.client.tsx index 41283a7..44ab879 100644 --- a/src/components/Navbar/navbar.client.tsx +++ b/src/components/Navbar/navbar.client.tsx @@ -168,7 +168,7 @@ export default function Navbar() { auth?.token ? ( From 148accc6bb640959e6fb419801eac5ea29681cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B3=BD=EC=98=88=EA=B2=BD?= Date: Tue, 29 Jul 2025 08:47:42 +0900 Subject: [PATCH 14/20] [feat] minor css #1 --- src/components/Navbar/navbar.client.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Navbar/navbar.client.tsx b/src/components/Navbar/navbar.client.tsx index 44ab879..7a06bb3 100644 --- a/src/components/Navbar/navbar.client.tsx +++ b/src/components/Navbar/navbar.client.tsx @@ -163,12 +163,13 @@ export default function Navbar() { {/* 로그인/로그아웃 버튼 */} +
{mounted ? ( auth?.token ? ( From 13c5b878d91153af1c4341ade8ac664466b82df8 Mon Sep 17 00:00:00 2001 From: jiminseon <20201020@dongduk.ac.kr> Date: Tue, 29 Jul 2025 09:06:06 +0900 Subject: [PATCH 15/20] =?UTF-8?q?[modify]=20navbar=20=ED=96=84=EB=B2=84?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Navbar/navbar.client.tsx | 199 ++++++++++++------------ 1 file changed, 99 insertions(+), 100 deletions(-) diff --git a/src/components/Navbar/navbar.client.tsx b/src/components/Navbar/navbar.client.tsx index 41283a7..33073c3 100644 --- a/src/components/Navbar/navbar.client.tsx +++ b/src/components/Navbar/navbar.client.tsx @@ -22,8 +22,9 @@ export default function Navbar() { const clearAuth = useAuthStore((s) => s.clearAuth); const loginRequiredPaths = ["/", "/practice", "/ranking", "/mypage"]; - // hydration mismatch 방지용 const [mounted, setMounted] = useState(false); + const [menuOpen, setMenuOpen] = useState(false); + useEffect(() => { setMounted(true); }, []); @@ -31,28 +32,13 @@ export default function Navbar() { const handleInvestClick = async () => { try { - if (!auth?.token) { - console.warn("로그인 필요"); - return router.push("/auth/login"); - } + if (!auth?.token) return router.push("/auth/login"); const status = await checkUserStatus(auth.token); - //("이미 연동 완료된 user: ", status.hasHoldings); - - if (status.hasHoldings) { - const stockData = await getStock(auth.token); - const firstCode = stockData.stocks[0]?.stock_code._id; - //console.log("주식 조회", firstCode); - if (firstCode) { - router.push(`/investment/${firstCode}`); - } else { - router.push("/investment"); - } - } else { - router.push("/investment"); - } + const stockData = await getStock(auth.token); + const firstCode = stockData.stocks[0]?.stock_code._id; + router.push(firstCode ? `/investment/${firstCode}` : "/investment"); } catch (err) { - console.error("실전예측 이동 중 오류:", err); router.push("/investment"); } }; @@ -60,9 +46,7 @@ export default function Navbar() { const handleLogout = async () => { try { await fetch("/api/auth/logout", { method: "POST" }); - } catch (e) { - console.warn("서버 로그아웃 실패"); - } + } catch {} sessionStorage.removeItem("token"); clearAuth(); router.replace("/auth/login"); @@ -72,16 +56,23 @@ export default function Navbar() { router.push("/auth/login"); }; + const handleTutorialOpen = () => { + localStorage.setItem("hideTutorial", "false"); + window.dispatchEvent(new Event("open-tutorial")); + setMenuOpen(false); // 모바일일 경우 닫아줌 + }; + + const navButtonClass = (isActive: boolean) => + "text-base transition-colors cursor-pointer " + + (isActive + ? "text-[#396FFB] hover:text-blue-500 font-semibold" + : "text-[#E2E2E2] hover:text-white"); + return ( - ); } diff --git a/src/components/blocks/Tutorial/TutorialPopup.client.tsx b/src/components/blocks/Tutorial/TutorialPopup.client.tsx index edd8bbe..b397fbb 100644 --- a/src/components/blocks/Tutorial/TutorialPopup.client.tsx +++ b/src/components/blocks/Tutorial/TutorialPopup.client.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import MainHomeClient from "../MainHome/MainHome.client"; import { useRouter } from "next/navigation"; -function TutorialOverlay({ onClose }) { +export function TutorialOverlay({ onClose }) { const [index, setIndex] = useState(0); const images = [ "/tuto1.png", From 122403216637451205add5ef387391ac84e9b4d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B3=BD=EC=98=88=EA=B2=BD?= Date: Tue, 29 Jul 2025 09:43:45 +0900 Subject: [PATCH 18/20] =?UTF-8?q?[feat]=20navbar=20=EC=B6=A9=EB=8F=8C?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Navbar/navbar.client.tsx | 208 ++++++++++++------------ 1 file changed, 107 insertions(+), 101 deletions(-) diff --git a/src/components/Navbar/navbar.client.tsx b/src/components/Navbar/navbar.client.tsx index b892fc2..e4efb08 100644 --- a/src/components/Navbar/navbar.client.tsx +++ b/src/components/Navbar/navbar.client.tsx @@ -28,8 +28,9 @@ export default function Navbar() { setShowTutorial(false); }; - // hydration mismatch 방지용 const [mounted, setMounted] = useState(false); + const [menuOpen, setMenuOpen] = useState(false); + useEffect(() => { setMounted(true); }, []); @@ -37,28 +38,13 @@ export default function Navbar() { const handleInvestClick = async () => { try { - if (!auth?.token) { - console.warn("로그인 필요"); - return router.push("/auth/login"); - } + if (!auth?.token) return router.push("/auth/login"); const status = await checkUserStatus(auth.token); - //("이미 연동 완료된 user: ", status.hasHoldings); - - if (status.hasHoldings) { - const stockData = await getStock(auth.token); - const firstCode = stockData.stocks[0]?.stock_code._id; - //console.log("주식 조회", firstCode); - if (firstCode) { - router.push(`/investment/${firstCode}`); - } else { - router.push("/investment"); - } - } else { - router.push("/investment"); - } + const stockData = await getStock(auth.token); + const firstCode = stockData.stocks[0]?.stock_code._id; + router.push(firstCode ? `/investment/${firstCode}` : "/investment"); } catch (err) { - console.error("실전예측 이동 중 오류:", err); router.push("/investment"); } }; @@ -66,9 +52,7 @@ export default function Navbar() { const handleLogout = async () => { try { await fetch("/api/auth/logout", { method: "POST" }); - } catch (e) { - console.warn("서버 로그아웃 실패"); - } + } catch {} sessionStorage.removeItem("token"); clearAuth(); router.replace("/auth/login"); @@ -78,16 +62,23 @@ export default function Navbar() { router.push("/auth/login"); }; + const handleTutorialOpen = () => { + localStorage.setItem("hideTutorial", "false"); + window.dispatchEvent(new Event("open-tutorial")); + setMenuOpen(false); // 모바일일 경우 닫아줌 + }; + + const navButtonClass = (isActive: boolean) => + "text-base transition-colors cursor-pointer " + + (isActive + ? "text-[#396FFB] hover:text-blue-500 font-semibold" + : "text-[#E2E2E2] hover:text-white"); + return ( -