Skip to content

Conversation

@Dobbymin
Copy link
Owner

@Dobbymin Dobbymin commented Nov 9, 2025

📝 요약 (Summary)

Upbit API를 활용하여 KRW 마켓의 실시간 시세 정보를 조회하고 표시하는 기능을 구현했습니다. BFF(Backend for Frontend) 패턴을 적용하여 서버 사이드에서 데이터를 가공하고, React Query를 통해 클라이언트에서 1초 간격으로 폴링하여 실시간 업데이트를 제공합니다.

✅ 주요 변경 사항 (Key Changes)

  • Upbit API 연동: 마켓 목록(/market/all)과 실시간 시세(/ticker) API 통합
  • BFF 패턴 구현: 서버 사이드 Route Handler를 통한 데이터 집계 및 변환
  • React Query 도입: 클라이언트 데이터 페칭 및 1초 간격 실시간 폴링
  • 타입 안정성 강화: TypeScript 타입 정의 및 API 응답 검증 로직 추가
  • UI 컴포넌트 구현: 실시간 시세 테이블 (코인명, 현재가, 등락률)

💻 상세 구현 내용 (Implementation Details)

1. Upbit API 통합 (src/entities/market/model/apis/)

market-all.api.ts

  • Upbit /market/all API 호출하여 전체 마켓 목록 조회
  • 타입 정의: MarketAllItem (market, korean_name, english_name 등)
  • 60초 revalidation 캐싱 전략 적용

ticker.api.ts

  • Upbit /ticker API 호출하여 실시간 시세 데이터 조회
  • 상세 타입 정의: TickerResponse (52개 필드 포함)
    • 현재가, 등락률, 거래량, 52주 고/저가 등
  • 복수 마켓 동시 조회 지원 (markets 쿼리 파라미터)
export interface TickerResponse {
  market: string;
  trade_price: number;
  signed_change_rate: number;
  change: ChangeState;
  // ... 52개 필드
}

2. BFF Layer - Market Info Handler (src/entities/market/handler/)

market-info.handler.ts

서버 사이드에서 데이터 집계 및 가공 로직:

  1. 필터링: KRW 마켓만 추출 (market.startsWith("KRW-"))
  2. 데이터 병합: 마켓 정보 + 시세 정보 조인
  3. 변환: 클라이언트 친화적 형태로 가공
    • signed_change_rate → 퍼센트 변환 (소수점 둘째자리)
    • korean_name → camelCase 변환
export const marketInfoHandler = async () => {
  const marketData = await marketAllAPI();
  const KRWmarketList = marketData.filter(item => item.market.startsWith("KRW-"));
  const tickerData = await tickerAPI(KRWmarketList.map(item => item.market));
  
  return tickerData.map(ticker => ({
    market: ticker.market,
    koreanName: marketInfo?.korean_name || "",
    tradePrice: ticker.trade_price,
    changeRate: parseFloat((ticker.signed_change_rate * 100).toFixed(2)),
  }));
};

3. API Route Handler (src/app/api/market/route.ts)

Next.js Route Handler를 통한 RESTful API 엔드포인트 제공:

export async function GET() {
  try {
    const data = await marketInfoHandler();
    return NextResponse.json({ status: "success", data });
  } catch (error) {
    console.error("Market API error:", error);
    return NextResponse.json(
      { status: "error", error: "Failed to fetch market data" }, 
      { status: 500 }
    );
  }
}

응답 포맷:

{
  "status": "success",
  "data": [
    {
      "market": "KRW-BTC",
      "koreanName": "비트코인",
      "tradePrice": 123000000,
      "changeRate": -1.10
    }
  ]
}

4. React Query 통합 (src/features/home/)

Client API Wrapper (apis/market.api.ts)

export const marketAPI = async (): Promise<MarketInfoType[]> => {
  const response = await fetch("/api/market", { cache: "no-store" });
  if (!response.ok) {
    throw new Error(`Market API failed: ${response.status}`);
  }
  const json: APIResponse<MarketInfoType[]> = await response.json();
  return json.data;
};

Custom Hook (hooks/useGetMarketInfo.ts)

export const useGetMarketInfo = () => {
  return useQuery({
    queryKey: ["market-info"],
    queryFn: marketAPI,
    refetchInterval: 10000, // 10초 폴링
    refetchOnWindowFocus: false,
  });
};

React Query Provider (src/shared/provider/)

"use client";

export const QueryProvider = ({ children }: Props) => {
  return (
    <QueryClientProvider client={queryClient}>
      {children}
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
};

5. UI 컴포넌트 (src/features/home/components/features/coin-info/)

CoinInfoTable.tsx

실시간 시세 테이블 렌더링:

export const CoinInfoTable = () => {
  const { data: marketInfoData } = useGetMarketInfo();
  
  const rateColor = (rate: number) => {
    return rate >= 0 ? "text-increase" : "text-decrease";
  };
  
  return (
    <Table>
      <TableBody>
        {marketInfoData?.map((ticker) => (
          <TableRow key={ticker.market}>
            <TableCell>
              <p>{ticker.koreanName}</p>
              <p>{ticker.market}</p>
            </TableCell>
            <TableCell>
              {Number(ticker.tradePrice).toLocaleString("ko-KR")}
            </TableCell>
            <TableCell className={rateColor(ticker.changeRate)}>
              {ticker.changeRate.toFixed(2)}%
            </TableCell>
          </TableRow>
        ))}
      </TableBody>
    </Table>
  );
};

주요 기능:

  • 10초마다 자동 업데이트 (React Query 폴링)

6. 타입 정의 및 계약

src/entities/market/model/types/market-info.type.ts

export type MarketInfoType = {
  market: string;
  koreanName: string;
  tradePrice: number;
  changeRate: number;
};

src/shared/types/api.type.ts

export type APIResponse<T> = {
  status: "success" | "error";
  data: T;
  error?: string;
};

7. 프로젝트 구조 (Feature-Sliced Design)

src/
├── app/
│   └── api/market/route.ts          # Route Handler
├── entities/
│   └── market/
│       ├── handler/                  # BFF 로직
│       │   └── market-info.handler.ts
│       └── model/
│           ├── apis/                 # Upbit API 호출
│           │   ├── market-all.api.ts
│           │   └── ticker.api.ts
│           └── types/
│               └── market-info.type.ts
├── features/
│   └── home/
│       ├── apis/                     # Client API 래퍼
│       │   └── market.api.ts
│       ├── hooks/                    # React Query 훅
│       │   └── useGetMarketInfo.ts
│       └── components/
│           └── coin-info/
│               └── CoinInfoTable.tsx
└── shared/
    ├── provider/                     # React Query Provider
    │   └── components/QueryProvider.tsx
    └── types/                        # 공통 타입
        └── api.type.ts

🚀 트러블 슈팅 (Trouble Shooting)

1. React Server Components 직렬화 에러

문제:

Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.

원인:

  • QueryClient 인스턴스(클래스 객체)를 모듈 레벨에서 생성 후 서버 컴포넌트에서 클라이언트 컴포넌트로 전달하려다 발생
  • React Server Components는 클래스 인스턴스를 직렬화할 수 없음

해결:

  1. QueryProvider 컴포넌트에 "use client" 지시어 추가
  2. QueryClient 인스턴스를 전역이 아닌 컴포넌트 내부에서 생성하도록 변경 (초기 시도)
  3. 최종적으로 전역 queryClient를 사용하되, Provider 자체를 Client Component로 명시
// AS-IS (Server Component에서 사용 시도)
import { queryClient } from "../../libs";

// TO-BE (Client Component 명시)
"use client";
import { queryClient } from "../../libs";

2. API 응답 타입 불일치

문제:

  • Route Handler는 { status, data } 형태로 반환
  • Client API는 전체 응답을 그대로 반환하여 컴포넌트에서 data 필드 접근 필요

해결:

// Client API에서 data 필드만 추출하여 반환
const json: APIResponse<MarketInfoType[]> = await response.json();
return json.data;  // 배열만 반환

3. 캐싱 전략

문제:

  • 실시간 데이터임에도 Next.js 기본 캐싱으로 인해 오래된 데이터 표시

해결:

// Client fetch에 no-store 명시
fetch("/api/market", { cache: "no-store" });

// Server API에는 적절한 revalidate 설정
fetch(url, { next: { revalidate: 60 } });

⚠️ 알려진 이슈 및 참고 사항 (Known Issues & Notes)

성능 최적화 고려사항

  1. 폴링 간격: 현재 10초로 설정되어 있어 API 호출 빈도가 높음

    • 추후 WebSocket으로 전환 검토 (Upbit WebSocket API 활용)
    • 추후 supabse 의 realtime 기술 사용 검토
  2. 메모리 누수 방지: React Query는 자동으로 처리하지만, 컴포넌트 언마운트 시 폴링 중단 확인 필요

추후 개선 사항

  1. WebSocket 전환

    • Upbit WebSocket API를 통한 실시간 Push 방식으로 전환
    • 폴링 방식보다 효율적이고 즉각적인 업데이트
  2. 에러 핸들링 강화

    • 네트워크 에러 시 사용자 친화적 에러 메시지 표시
    • Retry 로직 및 Fallback UI 추가
  3. 페이지네이션/가상화

    • 현재 모든 KRW 마켓 (200개 이상) 렌더링
    • react-virtual 등을 활용한 가상 스크롤 도입 검토
  4. 검색/필터 기능

    • 코인 이름/심볼 검색
    • 등락률 정렬, 거래량 정렬 등
  5. 24시간 등락률 추가

    • 현재는 전일 종가 대비 등락률만 표시
    • change_rate_24h 계산 로직 추가 필요 (candles API 활용)
  6. 타입 안정성 더 강화

    • Upbit API 응답에 대한 런타임 검증 (Zod 등 활용)
    • API 스키마 변경 감지 메커니즘

📸 스크린샷 (Screenshots)

실시간 시세 테이블

스크린샷 2025-11-10 오전 12 52 10

표시 정보:

  • 마켓 이름 (한글명 + 심볼)
  • 현재가 (천 단위 콤마 포맷)
  • 등락률 (색상 구분: 빨강/파랑)

주요 기능:

  • 1초마다 자동 업데이트
  • 호버 시 행 강조
  • Sticky 헤더

#️⃣ 관련 이슈 (Related Issues)


🔍 리뷰 포인트 (Review Points)

  1. 아키텍처: BFF 패턴 적용이 적절한가?
  2. 타입 안정성: API 응답 타입 정의가 충분한가?
  3. 성능: 10초 폴링이 과도하지 않은가? WebSocket 전환 시점은?
  4. 코드 구조: Feature-Sliced Design 원칙을 잘 따르고 있는가?

@Dobbymin Dobbymin requested a review from Copilot November 9, 2025 16:00
@Dobbymin Dobbymin self-assigned this Nov 9, 2025
@Dobbymin Dobbymin added the ✨ Feature 새로운 기능 추가 및 구현하는 경우 label Nov 9, 2025
@Dobbymin Dobbymin linked an issue Nov 9, 2025 that may be closed by this pull request
@Dobbymin Dobbymin added 📡 API 비동기 통신 코드를 짜는 경우 🖥️ Server Next.js의 서버 기능 구현 labels Nov 9, 2025
@vercel
Copy link

vercel bot commented Nov 9, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
dobbit Ready Ready Preview Comment Nov 9, 2025 4:35pm

@gemini-code-assist
Copy link

Summary of Changes

Hello @Dobbymin, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 Upbit API를 활용하여 KRW 마켓의 실시간 암호화폐 시세 정보를 조회하고 사용자에게 표시하는 기능을 추가합니다. BFF(Backend for Frontend) 패턴을 적용하여 서버에서 데이터를 효율적으로 처리하고, React Query를 통해 클라이언트에서 주기적으로 데이터를 업데이트하여 실시간성을 확보했습니다. 이를 통해 사용자들은 최신 시세 정보를 편리하게 확인할 수 있습니다.

Highlights

  • Upbit API 연동: 업비트 마켓 목록 및 실시간 시세 API를 통합하여 KRW 마켓 데이터를 가져옵니다.
  • BFF 패턴 구현: 서버 사이드 Route Handler를 통해 데이터를 집계, 필터링(KRW 마켓), 가공하여 클라이언트 친화적인 형태로 제공합니다.
  • React Query 도입: 클라이언트에서 React Query를 사용하여 10초 간격으로 실시간 시세 데이터를 폴링하고 UI를 업데이트합니다.
  • 타입 안정성 강화: TypeScript를 활용하여 API 응답 및 데이터 구조에 대한 명확한 타입 정의를 추가하고, 런타임 검증 로직을 포함했습니다.
  • 실시간 시세 UI: 코인명, 현재가, 등락률을 표시하는 실시간 시세 테이블 컴포넌트를 구현하여 사용자에게 직관적인 정보를 제공합니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

전반적으로 Upbit API 연동 및 실시간 시세 조회 기능 구현을 위한 훌륭한 PR입니다. BFF 패턴을 도입하여 서버와 클라이언트의 역할을 명확히 분리하고, React Query를 활용해 효율적인 데이터 페칭 및 상태 관리를 구현한 점이 인상적입니다. Feature-Sliced Design에 따라 프로젝트 구조를 체계적으로 구성한 것도 유지보수성을 높이는 좋은 시도입니다.

몇 가지 개선점을 제안드립니다.

  1. BFF 핸들러에서 마켓 정보와 시세 정보를 병합하는 로직의 시간 복잡도를 개선할 수 있습니다. 현재 O(N*M) 구조를 O(N+M)으로 최적화하면 더 효율적인 데이터 처리가 가능합니다.
  2. API 응답을 위한 공통 타입 정의가 실제 응답과 일치하지 않는 부분이 있어 수정이 필요합니다.
  3. 코드에 남아있는 주석 처리된 코드 블록들은 정리하는 것이 좋겠습니다.

자세한 내용은 각 파일에 남긴 리뷰 코멘트를 참고해주세요. PR 설명에 언급된 대로, 추후 Polling 방식을 WebSocket으로 전환하면 더욱 실시간에 가까운 사용자 경험을 제공하고 서버 부하도 줄일 수 있을 것입니다. 멋진 작업입니다!

Comment on lines 6 to 28
const KRWmarketList = marketData
.filter((item) => item.market.startsWith("KRW-"))
.map((item) => ({
market: item.market,
korean_name: item.korean_name,
}));
// const marketName = KRWmarketList.map((item) => item.korean_name);

// const marketList = KRWMarkets.map((item) => item.market, item.korean_name);

const tickerData = await tickerAPI(KRWmarketList.map((item) => item.market));
// const tickerData = await tickerAPI(["KRW-WAXP"]);

const result = tickerData.map((ticker) => {
const marketInfo = KRWmarketList.find((m) => m.market === ticker.market);

return {
market: ticker.market,
koreanName: marketInfo?.korean_name || "",
tradePrice: ticker.trade_price,
changeRate: parseFloat((ticker.signed_change_rate * 100).toFixed(2)), // 퍼센트로 변환 & 소수 둘째자리
};
});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

현재 구현은 tickerData.map 내부에서 KRWmarketList.find를 사용하여 O(N*M)의 시간 복잡도를 가집니다. KRW 마켓의 수가 200개가 넘어가면 서버 측에서 성능 저하를 유발할 수 있습니다.

KRWmarketListMap으로 미리 변환하여 조회하면, 시간 복잡도를 O(N+M)으로 개선하여 성능을 크게 향상시킬 수 있습니다. 또한, 불필요한 API 호출을 막기 위해 KRW 마켓 코드가 없을 경우 조기 반환하는 로직을 추가하는 것이 좋습니다.

  const krwMarketMap = new Map<string, { korean_name: string }>();
  marketData.forEach((item) => {
    if (item.market.startsWith("KRW-")) {
      krwMarketMap.set(item.market, { korean_name: item.korean_name });
    }
  });

  const krwMarketCodes = Array.from(krwMarketMap.keys());
  if (krwMarketCodes.length === 0) {
    return [];
  }

  const tickerData = await tickerAPI(krwMarketCodes);

  const result = tickerData.map((ticker) => {
    const marketInfo = krwMarketMap.get(ticker.market);

    return {
      market: ticker.market,
      koreanName: marketInfo?.korean_name || "",
      tradePrice: ticker.trade_price,
      changeRate: parseFloat((ticker.signed_change_rate * 100).toFixed(2)), // 퍼센트로 변환 & 소수 둘째자리
    };
  });

Comment on lines 1 to 4
export type APIResponse<T> = {
status: "success" | "error";
data: T;
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

APIResponse<T> 타입이 API 에러 케이스를 정확하게 반영하지 못하고 있습니다. src/app/api/market/route.ts의 핸들러는 요청 실패 시 status: "error"와 함께 error 프로퍼티를 포함하는 객체를 반환합니다. 현재 타입 정의에는 이 error 필드가 누락되어 있어 클라이언트에서 타입 에러가 발생할 수 있습니다.

Suggested change
export type APIResponse<T> = {
status: "success" | "error";
data: T;
};
export type APIResponse<T> = {
status: "success" | "error";
data: T;
error?: string;
};

Comment on lines +9 to +12
} catch (error) {
console.error("Market API error:", error);
return NextResponse.json({ status: "error", error: "Failed to fetch market data" }, { status: 500 });
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

catch 블록에서 error를 직접 로깅하고 있습니다. errorunknown 타입일 수 있으므로, Error 인스턴스인지 확인하여 더 구체적인 오류 메시지를 로깅하면 프로덕션 환경에서 디버깅할 때 더 유용합니다.

  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    console.error("Market API error:", errorMessage, error);
    return NextResponse.json({ status: "error", error: "Failed to fetch market data" }, { status: 500 });
  }

Comment on lines 4 to 8
// type MarketApiResponse = {
// success: boolean;
// data: MarketInfoType[];
// error?: string;
// };

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

사용되지 않는 MarketApiResponse 타입 정의가 주석으로 남아있습니다. 코드를 깔끔하게 유지하기 위해 이 부분을 제거하는 것이 좋습니다.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the coin info table from server-side rendering to client-side rendering with React Query, introducing real-time data polling and improved data management.

  • Introduces React Query for client-side data fetching with 10-second polling
  • Creates a new API route (/api/market) that aggregates market and ticker data
  • Refactors CoinInfoTable from async server component to client component with hooks

Reviewed Changes

Copilot reviewed 28 out of 31 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/shared/types/api.type.ts Adds generic APIResponse type for consistent API responses
src/shared/provider/* Creates QueryProvider and AppProvider for React Query setup
src/shared/libs/query-client.ts Instantiates QueryClient for React Query
src/features/home/hooks/useGetMarketInfo.ts Custom hook for fetching market info with 10-second polling
src/features/home/components/features/coin-info/CoinInfoTable.tsx Refactored to client component using React Query hook
src/features/home/apis/market.api.ts New API client for fetching market data from internal API
src/entities/market/handler/market-info.handler.ts Server-side handler combining market list and ticker data
src/entities/market/model/apis/* API functions for external Upbit API calls
src/app/api/market/route.ts Next.js API route exposing market info endpoint
src/app/layout.tsx Wraps app with AppProvider for React Query context
src/app/globals.css Adds new color variables for increase/decrease indicators
package.json & pnpm-lock.yaml Adds React Query dependencies
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@@ -0,0 +1,3 @@
import { QueryClient } from "@tanstack/react-query";

export const queryClient = new QueryClient();
Copy link

Copilot AI Nov 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The QueryClient is instantiated without any default configuration options. In production applications, it's recommended to configure error handling, retry logic, and cache behavior. Consider adding default options like defaultOptions: { queries: { retry: 1, staleTime: 5000 } }.

Suggested change
export const queryClient = new QueryClient();
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 1,
staleTime: 5000,
},
},
});

Copilot uses AI. Check for mistakes.
return NextResponse.json({ status: "success", data });
} catch (error) {
console.error("Market API error:", error);
return NextResponse.json({ status: "error", error: "Failed to fetch market data" }, { status: 500 });
Copy link

Copilot AI Nov 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error response structure is inconsistent with the APIResponse<T> type definition which only has status and data fields. The response returns an error field that doesn't exist in the type. Consider updating the type to include an optional error field or adjust the response structure to match the defined type.

Suggested change
return NextResponse.json({ status: "error", error: "Failed to fetch market data" }, { status: 500 });
return NextResponse.json({ status: "error", data: "Failed to fetch market data" }, { status: 500 });

Copilot uses AI. Check for mistakes.
import { useGetMarketInfo } from "../../../hooks";

export const CoinInfoTable = () => {
const { data: marketInfoData } = useGetMarketInfo();
Copy link

Copilot AI Nov 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The component doesn't handle loading and error states from the React Query hook. When marketInfoData is undefined during loading or after an error, the table will render empty. Consider destructuring isLoading and error from the hook and displaying appropriate UI feedback for these states.

Copilot uses AI. Check for mistakes.
market: ticker.market,
koreanName: marketInfo?.korean_name || "",
tradePrice: ticker.trade_price,
changeRate: parseFloat((ticker.signed_change_rate * 100).toFixed(2)), // 퍼센트로 변환 & 소수 둘째자리
Copy link

Copilot AI Nov 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using parseFloat after toFixed is redundant since toFixed returns a string. The value is then parsed back to a number and later used with .toFixed(2) again in the component. Consider returning the number directly as ticker.signed_change_rate * 100 and let the component handle formatting, avoiding unnecessary precision loss.

Suggested change
changeRate: parseFloat((ticker.signed_change_rate * 100).toFixed(2)), // 퍼센트로 변환 & 소수 둘째자리
changeRate: ticker.signed_change_rate * 100, // 퍼센트로 변환, 소수 둘째자리 포맷은 컴포넌트에서 처리

Copilot uses AI. Check for mistakes.
@Dobbymin Dobbymin merged commit 3d85f66 into main Nov 9, 2025
3 checks passed
@Dobbymin Dobbymin deleted the feat#01-market-routes branch November 9, 2025 16:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📡 API 비동기 통신 코드를 짜는 경우 ✨ Feature 새로운 기능 추가 및 구현하는 경우 🖥️ Server Next.js의 서버 기능 구현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

마켓의 코인 정보 정제

2 participants