"use client"; // /watchlist — localStorage 의 관심종목 코드들을 카드 그리드로. // SEED 타일과 같은 톤. 각 카드는 /api/chart 2일치를 받아 현재가/전일대비를 채움. import Link from "next/link"; import { useEffect, useState } from "react"; import { api } from "../../lib/api"; import { watchlist } from "../../lib/watchlist"; type Tile = { code: string; name: string; market: string; current: number | null; prev: number | null; }; export default function WatchlistPage() { const [codes, setCodes] = useState([]); const [tiles, setTiles] = useState([]); const [loading, setLoading] = useState(true); const [hydrated, setHydrated] = useState(false); // localStorage 구독 — 다른 탭/페이지에서 추가/제거해도 즉시 반영. useEffect(() => { setHydrated(true); const refresh = () => setCodes(watchlist.get()); refresh(); return watchlist.subscribe(refresh); }, []); useEffect(() => { if (!hydrated) return; if (codes.length === 0) { setTiles([]); setLoading(false); return; } let alive = true; setLoading(true); Promise.allSettled( codes.map(async (code) => { const c = await api.getChart(code, 5, "1d"); const valid = c.ohlcv.filter((p) => p.close != null); const last = valid[valid.length - 1] ?? null; const prev = valid.length >= 2 ? valid[valid.length - 2] : null; return { code, name: c.name, market: c.market, current: last?.close ?? null, prev: prev?.close ?? null, }; }), ).then((settled) => { if (!alive) return; const next: Tile[] = settled.map((r, i) => { if (r.status === "fulfilled") return r.value; return { code: codes[i], name: codes[i], market: "—", current: null, prev: null, }; }); setTiles(next); setLoading(false); }); return () => { alive = false; }; }, [codes, hydrated]); return (
← 홈 {codes.length} 종목

관심종목

브라우저 저장소에 보관됩니다. 로그인 없이 같은 브라우저에서 유지.

{!hydrated || loading ? (
관심종목 로딩 중…
) : codes.length === 0 ? (
아직 관심종목이 없습니다.
종목 페이지에서 ☆ 관심 버튼을 눌러 추가하세요.
) : (
{tiles.map((t) => ( ))}
)}
); } function TileCard({ t }: { t: Tile }) { const change = t.current != null && t.prev != null ? t.current - t.prev : null; const pct = change != null && t.prev != null && t.prev !== 0 ? (change / t.prev) * 100 : null; const tone = change == null || change === 0 ? "text-zinc-400" : change > 0 ? "text-rose-400" : "text-sky-400"; return (
{t.name}
{t.code} · {t.market}
{t.current != null ? t.current.toLocaleString(undefined, { maximumFractionDigits: 0 }) : "—"}
{change == null ? " " : `${change > 0 ? "+" : ""}${change.toLocaleString(undefined, { maximumFractionDigits: 0, })}${pct != null ? ` (${pct > 0 ? "+" : ""}${pct.toFixed(2)}%)` : ""}`}
); }