주식 주요 지수 가져오기 추가

This commit is contained in:
2026-01-26 03:14:16 +09:00
parent 07b43c48c1
commit b559eeda58
3 changed files with 109 additions and 9 deletions

View File

@@ -69,3 +69,7 @@ export function triggerStockScrap() {
export function getStockHealth() { export function getStockHealth() {
return apiGet("/api/stock/health"); return apiGet("/api/stock/health");
} }
export function getStockIndices() {
return apiGet("/api/stock/indices");
}

View File

@@ -176,6 +176,12 @@
background: rgba(0, 0, 0, 0.2); background: rgba(0, 0, 0, 0.2);
} }
.stock-snapshot__card.is-highlight {
border-color: rgba(255, 255, 255, 0.4);
background: rgba(255, 255, 255, 0.05);
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08);
}
.stock-snapshot__card p { .stock-snapshot__card p {
margin: 0; margin: 0;
font-size: 12px; font-size: 12px;
@@ -193,6 +199,19 @@
font-size: 12px; font-size: 12px;
} }
.stock-snapshot__change {
font-size: 12px;
color: var(--muted);
}
.stock-snapshot__change.is-up {
color: #f3a7a7;
}
.stock-snapshot__change.is-down {
color: #9fc5ff;
}
.stock-schedule { .stock-schedule {
display: grid; display: grid;
gap: 12px; gap: 12px;

View File

@@ -1,6 +1,7 @@
import React, { useEffect, useMemo, useState } from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import { import {
getStockHealth, getStockHealth,
getStockIndices,
getStockNews, getStockNews,
triggerStockScrap, triggerStockScrap,
} from '../../api'; } from '../../api';
@@ -34,6 +35,10 @@ const Stock = () => {
status: 'unknown', status: 'unknown',
message: '', message: '',
}); });
const [indices, setIndices] = useState([]);
const [indicesLoading, setIndicesLoading] = useState(false);
const [indicesAt, setIndicesAt] = useState('');
const [autoRefreshMs] = useState(180000);
const latestCrawled = useMemo( const latestCrawled = useMemo(
() => getLatestBy(news, 'crawled_at'), () => getLatestBy(news, 'crawled_at'),
@@ -72,6 +77,20 @@ const Stock = () => {
} }
}; };
const loadIndices = async () => {
setIndicesLoading(true);
setError('');
try {
const data = await getStockIndices();
setIndices(Array.isArray(data?.indices) ? data.indices : []);
setIndicesAt(data?.crawled_at ?? '');
} catch (err) {
setError(err?.message ?? String(err));
} finally {
setIndicesLoading(false);
}
};
const onScrap = async () => { const onScrap = async () => {
setScraping(true); setScraping(true);
setError(''); setError('');
@@ -94,7 +113,12 @@ const Stock = () => {
useEffect(() => { useEffect(() => {
loadHealth(); loadHealth();
}, []); loadIndices();
const timer = window.setInterval(() => {
loadIndices();
}, autoRefreshMs);
return () => window.clearInterval(timer);
}, [autoRefreshMs]);
return ( return (
<div className="stock"> <div className="stock">
@@ -160,18 +184,71 @@ const Stock = () => {
<p className="stock-panel__eyebrow">Snapshot</p> <p className="stock-panel__eyebrow">Snapshot</p>
<h3>시장 스냅샷</h3> <h3>시장 스냅샷</h3>
<p className="stock-panel__sub"> <p className="stock-panel__sub">
지수/가격 API 연동을 위한 준비 구간입니다. 주요 지표의 현재가와 등락을 빠르게 확인합니다.
</p> </p>
</div> </div>
<div className="stock-panel__actions">
{indicesLoading ? (
<span className="stock-chip">갱신 </span>
) : null}
{indicesAt ? (
<span className="stock-chip">{indicesAt}</span>
) : null}
<button
className="button ghost small"
onClick={loadIndices}
disabled={indicesLoading}
>
지표 새로고침
</button>
</div>
</div> </div>
<div className="stock-snapshot"> <div className="stock-snapshot">
{['KOSPI', 'KOSDAQ', 'NASDAQ'].map((label) => ( {indices.length === 0 ? (
<div key={label} className="stock-snapshot__card"> <p className="stock-empty">지표 데이터를 불러오지 못했습니다.</p>
<p>{label}</p> ) : (
<strong>--</strong> [...indices]
<span>연동 예정</span> .sort((a, b) => {
</div> const order = ['KOSPI', 'KOSDAQ', 'KOSPI200'];
))} const aIdx = order.indexOf(a.name);
const bIdx = order.indexOf(b.name);
if (aIdx === -1 && bIdx === -1) return 0;
if (aIdx === -1) return 1;
if (bIdx === -1) return -1;
return aIdx - bIdx;
})
.map((item) => (
<div
key={item.name}
className={`stock-snapshot__card ${
item.name?.toUpperCase?.() ===
'KOSPI200'
? 'is-highlight'
: ''
}`}
>
<p>{item.name}</p>
<strong>{item.value ?? '--'}</strong>
<span
className={`stock-snapshot__change ${
item.direction === 'red'
? 'is-up'
: item.direction === 'blue'
? 'is-down'
: ''
}`}
>
{item.direction === 'red'
? '▲'
: item.direction === 'blue'
? '▼'
: '■'}{' '}
{item.change_value ?? '--'}{' '}
{item.change_percent ?? ''}
</span>
</div>
))
)}
</div> </div>
</div> </div>