서버오류 수정
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 { /* 부분 실패 무시 */ }
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user