주식 즉시 스크래핑 추가
This commit is contained in:
@@ -105,6 +105,15 @@
|
|||||||
background: rgba(249, 182, 177, 0.1);
|
background: rgba(249, 182, 177, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stock-success {
|
||||||
|
margin: 0;
|
||||||
|
color: #b5f0dd;
|
||||||
|
border: 1px solid rgba(106, 220, 187, 0.4);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 12px;
|
||||||
|
background: rgba(106, 220, 187, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
.stock-grid {
|
.stock-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ const Stock = () => {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [scraping, setScraping] = useState(false);
|
const [scraping, setScraping] = useState(false);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
const [newsError, setNewsError] = useState('');
|
||||||
|
const [indicesError, setIndicesError] = useState('');
|
||||||
|
const [scrapMessage, setScrapMessage] = useState('');
|
||||||
const [health, setHealth] = useState({
|
const [health, setHealth] = useState({
|
||||||
status: 'unknown',
|
status: 'unknown',
|
||||||
message: '',
|
message: '',
|
||||||
@@ -51,12 +54,12 @@ const Stock = () => {
|
|||||||
|
|
||||||
const loadNews = async () => {
|
const loadNews = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError('');
|
setNewsError('');
|
||||||
try {
|
try {
|
||||||
const data = await getStockNews(limit);
|
const data = await getStockNews(limit);
|
||||||
setNews(Array.isArray(data) ? data : []);
|
setNews(Array.isArray(data) ? data : []);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err?.message ?? String(err));
|
setNewsError(err?.message ?? String(err));
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -70,22 +73,26 @@ const Stock = () => {
|
|||||||
message: data?.message ?? '',
|
message: data?.message ?? '',
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
const rawMessage = err?.message ?? String(err);
|
||||||
|
const message = rawMessage.includes('404')
|
||||||
|
? '헬스 체크 엔드포인트가 아직 준비되지 않았습니다.'
|
||||||
|
: rawMessage;
|
||||||
setHealth({
|
setHealth({
|
||||||
status: 'unknown',
|
status: 'unknown',
|
||||||
message: err?.message ?? '',
|
message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadIndices = async () => {
|
const loadIndices = async () => {
|
||||||
setIndicesLoading(true);
|
setIndicesLoading(true);
|
||||||
setError('');
|
setIndicesError('');
|
||||||
try {
|
try {
|
||||||
const data = await getStockIndices();
|
const data = await getStockIndices();
|
||||||
setIndices(Array.isArray(data?.indices) ? data.indices : []);
|
setIndices(Array.isArray(data?.indices) ? data.indices : []);
|
||||||
setIndicesAt(data?.crawled_at ?? '');
|
setIndicesAt(data?.crawled_at ?? '');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err?.message ?? String(err));
|
setIndicesError(err?.message ?? String(err));
|
||||||
} finally {
|
} finally {
|
||||||
setIndicesLoading(false);
|
setIndicesLoading(false);
|
||||||
}
|
}
|
||||||
@@ -94,11 +101,13 @@ const Stock = () => {
|
|||||||
const onScrap = async () => {
|
const onScrap = async () => {
|
||||||
setScraping(true);
|
setScraping(true);
|
||||||
setError('');
|
setError('');
|
||||||
|
setScrapMessage('');
|
||||||
try {
|
try {
|
||||||
const result = await triggerStockScrap();
|
const result = await triggerStockScrap();
|
||||||
if (!result?.ok) {
|
if (!result?.ok) {
|
||||||
throw new Error('스크랩 요청이 실패했습니다.');
|
throw new Error('스크랩 요청이 실패했습니다.');
|
||||||
}
|
}
|
||||||
|
setScrapMessage('스크랩 요청 완료');
|
||||||
await loadNews();
|
await loadNews();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err?.message ?? String(err));
|
setError(err?.message ?? String(err));
|
||||||
@@ -176,6 +185,9 @@ const Stock = () => {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
{error ? <p className="stock-error">{error}</p> : null}
|
{error ? <p className="stock-error">{error}</p> : null}
|
||||||
|
{scrapMessage ? (
|
||||||
|
<p className="stock-success">{scrapMessage}</p>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<section className="stock-grid">
|
<section className="stock-grid">
|
||||||
<div className="stock-panel">
|
<div className="stock-panel">
|
||||||
@@ -204,7 +216,9 @@ const Stock = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="stock-snapshot">
|
<div className="stock-snapshot">
|
||||||
{indices.length === 0 ? (
|
{indicesError ? (
|
||||||
|
<p className="stock-empty">{indicesError}</p>
|
||||||
|
) : indices.length === 0 ? (
|
||||||
<p className="stock-empty">지표 데이터를 불러오지 못했습니다.</p>
|
<p className="stock-empty">지표 데이터를 불러오지 못했습니다.</p>
|
||||||
) : (
|
) : (
|
||||||
[...indices]
|
[...indices]
|
||||||
@@ -218,36 +232,36 @@ const Stock = () => {
|
|||||||
return aIdx - bIdx;
|
return aIdx - bIdx;
|
||||||
})
|
})
|
||||||
.map((item) => (
|
.map((item) => (
|
||||||
<div
|
<div
|
||||||
key={item.name}
|
key={item.name}
|
||||||
className={`stock-snapshot__card ${
|
className={`stock-snapshot__card ${
|
||||||
item.name?.toUpperCase?.() ===
|
item.name?.toUpperCase?.() ===
|
||||||
'KOSPI200'
|
'KOSPI200'
|
||||||
? 'is-highlight'
|
? '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'
|
<p>{item.name}</p>
|
||||||
? '▲'
|
<strong>{item.value ?? '--'}</strong>
|
||||||
: item.direction === 'blue'
|
<span
|
||||||
? '▼'
|
className={`stock-snapshot__change ${
|
||||||
: '■'}{' '}
|
item.direction === 'red'
|
||||||
{item.change_value ?? '--'}{' '}
|
? 'is-up'
|
||||||
{item.change_percent ?? ''}
|
: item.direction === 'blue'
|
||||||
</span>
|
? 'is-down'
|
||||||
</div>
|
: ''
|
||||||
))
|
}`}
|
||||||
|
>
|
||||||
|
{item.direction === 'red'
|
||||||
|
? '▲'
|
||||||
|
: item.direction === 'blue'
|
||||||
|
? '▼'
|
||||||
|
: '■'}{' '}
|
||||||
|
{item.change_value ?? '--'}{' '}
|
||||||
|
{item.change_percent ?? ''}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -330,6 +344,8 @@ const Stock = () => {
|
|||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<p className="stock-empty">뉴스를 불러오는 중...</p>
|
<p className="stock-empty">뉴스를 불러오는 중...</p>
|
||||||
|
) : newsError ? (
|
||||||
|
<p className="stock-empty">{newsError}</p>
|
||||||
) : news.length === 0 ? (
|
) : news.length === 0 ? (
|
||||||
<p className="stock-empty">표시할 뉴스가 없습니다.</p>
|
<p className="stock-empty">표시할 뉴스가 없습니다.</p>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
Reference in New Issue
Block a user