서버오류 수정

This commit is contained in:
Macbook
2026-05-25 06:56:26 +09:00
parent 8b373b11e3
commit 4cac4ed555
3 changed files with 32 additions and 15 deletions
+27 -12
View File
@@ -5,6 +5,7 @@ import {
toggleFavorite,
FAVORITES_CHANGED_EVENT,
} from '../utils/marketStorage';
import { UPBIT_API } from '../utils/upbitApi';
// ─── Types ───────────────────────────────────────────────────────────────────
interface MarketInfo {
@@ -21,14 +22,21 @@ interface TickerInfo {
}
// ─── Constants ───────────────────────────────────────────────────────────────
const UPBIT_API = import.meta.env.DEV ? '/upbit-api/v1' : 'https://api.upbit.com/v1';
const TICKER_TTL = 3000; // 3초마다 시세 갱신
function formatVol(vol: number): string {
if (vol >= 1e12) return `${(vol / 1e12).toFixed(1)}`;
if (vol >= 1e8) return `${(vol / 1e8).toFixed(0)}`;
if (vol >= 1e4) return `${(vol / 1e4).toFixed(0)}`;
return vol.toLocaleString();
function formatVol(vol: number | string | null | undefined): string {
const v = Number(vol);
if (!Number.isFinite(v)) return '-';
if (v >= 1e12) return `${(v / 1e12).toFixed(1)}`;
if (v >= 1e8) return `${(v / 1e8).toFixed(0)}`;
if (v >= 1e4) return `${(v / 1e4).toFixed(0)}`;
return v.toLocaleString();
}
function formatChangeRate(raw: number | string | null | undefined): number | null {
const rate = Number(raw);
if (!Number.isFinite(rate)) return null;
return rate * 100;
}
// ─── Component ───────────────────────────────────────────────────────────────
@@ -86,7 +94,10 @@ export const MarketSearchPanel: React.FC<MarketSearchPanelProps> = ({
useEffect(() => {
setLoading(true);
fetch(`${UPBIT_API}/market/all?isDetails=false`)
.then(r => r.json())
.then(r => {
if (!r.ok) throw new Error(`market/all ${r.status}`);
return r.json();
})
.then((data: Array<{ market: string; korean_name: string; english_name: string }>) => {
const krw = data
.filter(m => m.market.startsWith('KRW-'))
@@ -129,9 +140,11 @@ export const MarketSearchPanel: React.FC<MarketSearchPanelProps> = ({
for (let i = 0; i < allCodes.length; i += BATCH) {
const batch = allCodes.slice(i, i + BATCH).join(',');
try {
const data: Array<{ market: string } & TickerInfo> =
await fetch(`${UPBIT_API}/ticker?markets=${batch}`).then(r => r.json());
results.push(...data);
const res = await fetch(`${UPBIT_API}/ticker?markets=${batch}`);
if (!res.ok) continue;
const data: unknown = await res.json();
if (!Array.isArray(data)) continue;
results.push(...(data as Array<{ market: string } & TickerInfo>));
} catch { /* ignore partial failures */ }
}
@@ -328,7 +341,7 @@ export const MarketSearchPanel: React.FC<MarketSearchPanelProps> = ({
const isFav = favs.has(m.market);
const isAdded = addedMarkets?.has(m.market) ?? false;
const isActive = m.market === currentMarket;
const rate = tk ? tk.signed_change_rate * 100 : null;
const rate = tk ? formatChangeRate(tk.signed_change_rate) : null;
const isUp = rate !== null && rate >= 0;
return (
@@ -373,7 +386,9 @@ export const MarketSearchPanel: React.FC<MarketSearchPanelProps> = ({
{!embedded && (
<>
<span className="msp-col msp-col-price">
{tk ? tk.trade_price.toLocaleString() : '-'}
{tk && Number.isFinite(Number(tk.trade_price))
? Number(tk.trade_price).toLocaleString()
: '-'}
</span>
<span className={`msp-col msp-col-change ${rate === null ? '' : isUp ? 'up' : 'down'}`}>
{rate === null
+3 -2
View File
@@ -10,6 +10,7 @@
import { useState, useEffect, useRef, useCallback } from 'react';
import { setMarketNames } from '../utils/marketNameCache';
import { UPBIT_API } from '../utils/upbitApi';
export type ChangeDir = 'RISE' | 'FALL' | 'EVEN';
@@ -69,7 +70,7 @@ function getMarketCode(raw: UpbitTickerRaw): string {
}
async function fetchAllMarkets(): Promise<MarketInfo[]> {
const res = await fetch('/upbit-api/v1/market/all?isDetails=true');
const res = await fetch(`${UPBIT_API}/market/all?isDetails=true`);
if (!res.ok) throw new Error(`market/all ${res.status}`);
const data: UpbitMarketAll[] = await res.json();
const krw = data.filter(m => m.market.startsWith('KRW-'));
@@ -88,7 +89,7 @@ async function fetchAllTickers(markets: string[]): Promise<UpbitTickerRaw[]> {
for (let i = 0; i < markets.length; i += 100) {
const chunk = markets.slice(i, i + 100);
try {
const res = await fetch(`/upbit-api/v1/ticker?markets=${chunk.join(',')}`);
const res = await fetch(`${UPBIT_API}/ticker?markets=${chunk.join(',')}`);
if (res.ok) results.push(...(await res.json()));
} catch { /* 부분 실패 무시 */ }
}
+2 -1
View File
@@ -4,7 +4,8 @@ import type { OHLCVBar, Timeframe } from '../types';
// 개발: Vite proxy (/upbit-api) → https://api.upbit.com
// 프로덕션: nginx proxy (/upbit-api) → https://api.upbit.com
// 항상 동일한 상대 경로를 사용하므로 CORS 없이 동일 오리진으로 요청됨
const UPBIT_API = '/upbit-api/v1';
/** dev: Vite proxy, prod: nginx proxy — 항상 동일 오리진 상대 경로 */
export const UPBIT_API = '/upbit-api/v1';
export const UPBIT_MARKETS = [
'KRW-BTC', 'KRW-ETH', 'KRW-XRP', 'KRW-SOL', 'KRW-DOGE',