콘텐츠로 건너뛰기

10-2. 대시보드 및 자산 상세 페이지 구성

  • 기준

프론트엔드 개별 화면 조각들을 모아서, 사용자가 서비스를 처음 방문했을 때 맞이하는 통합 대시보드(Dashboard)와 특정 자산을 클릭했을 때 해당 자산의 모든 정보를 제공하는 자산 상세 페이지를 설계할 차례입니다.

금융 웹사이트의 전문성을 극대화하기 위해 반응형 레이아웃 그리드와 Next.js의 동적 라우팅(Dynamic Routing)을 조합하여, 마치 트레이딩 데스크에 앉아 있는 듯한 세련된 UX를 구축합니다.

전체화면 대시보드 설계 기법

프로급 대시보드는 정보의 강약을 잘 조절해야 합니다.

  1. 상단 정보 보드(Cards Grid): 현재 가장 변동폭이 크거나 중요한 자산의 가격, 전일 대비 등락률을 3~4개의 요약 카드(Sparkline 포함)로 시각화합니다.
  2. 중앙 분석 레이아웃(Split Layout): 좌측 또는 중앙 넓은 화면에는 Highcharts Advanced 차트를 배치하여 시세 분석을 돕고, 우측에는 실시간으로 가격이 변동하는 자산 리스트 테이블(AssetsList)을 놓아 상호작용하도록 합니다.
  3. 하단 기록 보드(Ag-Grid History Table): 하단부에는 해당 자산의 과거 상세 시가, 종가 및 거래 기록을 일목요연하게 표기하는 고성능 데이터 테이블을 깔끔하게 정렬합니다.

Next.js 동적 라우팅을 이용한 상세 페이지 구현

자산 목록에서 비트코인(BTC)이나 애플(AAPL)을 클릭했을 때, 각 자산의 세부 시세 분석 페이지인 /assets/[id]로 자연스럽게 라우팅 처리가 되어야 합니다. Next.js App Router의 Dynamic Route 세팅과 서버 사이드 또는 클라이언트 사이드 데이터 fetching을 이용해 개별 상세 정보를 실시간으로 뿌려주는 패턴을 적용합니다.

🤖 실전! 대시보드 및 상세 페이지 결합(2-18)

🧠 이번 실습은 Next.js의 app/dashboard/page.tsxapp/assets/[id]/page.tsx 경로를 구성하여 대시보드 및 자산별 통합 상세 페이지를 구축하는 과정입니다.

✨ 사용자의 의도(Vibe)

대시보드와 상세 페이지를 연결하여 완성도 높은 금융 모니터링 시스템을 설계해줘.

  1. 파일명 1: frontend/src/app/dashboard/page.tsx -> 상단 요약 카드 그리드, 중앙 메인 차트, 우측 자산 리스트 테이블을 모아 하나의 페이지로 구성해줘.
  2. 파일명 2: frontend/src/app/assets/[id]/page.tsx -> 자산 리스트에서 항목 클릭 시 이동할 상세 페이지로, URL 파라미터(id)를 받아 해당 자산의 이름, 캔들 차트, 그리고 하단에 AG-Grid 과거 히스토리 테이블을 연동해서 표시해줘.
  3. 반응형 디자인: Tailwind CSS의 grid-cols-1 lg:grid-cols-3 등을 적극 활용해 모바일과 데스크톱 화면에 완벽 대응해줘.

💻 AI가 생성한 코드 (Syntax) 및 결과

  1. frontend/src/app/assets/[id]/page.tsx
"use client";  import React, { useParams } from "react"; import useSWR from "swr"; import HighChartAdvanced from "@/components/charts/highchart/HighChartAdvanced"; import AgGridBaseTable from "@/components/tables/AgGridBaseTable";  const fetcher = (url: string) => fetch(url).then(res => res.json());  export default
function AssetDetailPage() {   const { id } = useParams();    // API를 통한 자산 상세 정보 및 시세 데이터 조회   const { data: asset, error: assetErr } = useSWR(`/api/v1/assets/${id}`, fetcher);   const { data: ohlcv, error: ohlcvErr } = useSWR(`/api/v1/assets/ohlcv/${id}`, fetcher);    if (assetErr || ohlcvErr)
return <div className="p-8 text-red-500">데이터를 로드하는 중 오류가 발생했습니다.</div>;   if (!asset || !ohlcv)
return <div className="p-8 text-center text-gray-500">자산 데이터를 불러오는 중...</div>;    const columnDefs = [     { field: "date", headerName: "날짜", valueFormatter: (params: any) => params.value.split("T")[0] },     { field: "open", headerName: "시가", valueFormatter: (params: any) => params.value.toLocaleString() },     { field: "high", headerName: "고가", valueFormatter: (params: any) => params.value.toLocaleString() },     { field: "low", headerName: "저가", valueFormatter: (params: any) => params.value.toLocaleString() },     { field: "close", headerName: "종가", valueFormatter: (params: any) => params.value.toLocaleString() }   ];
return (     <div className="container mx-auto p-6 space-y-6">       <header className="flex justify-between items-center bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md">         <div>           <h1 className="text-3xl font-bold text-gray-900 dark:text-white">{asset.name} ({asset.symbol})</h1>           <p className="text-gray-500 dark:text-gray-400">자산 상세 분석 및 히스토리 레코드</p>         </div>         <div className="text-right">           <span className="text-2xl font-semibold text-gray-900 dark:text-white">${asset.price.toLocaleString()}</span>           <p className={`text-sm ${asset.changePercent >= 0 ? "text-red-500" : "text-blue-500"}`}>             {asset.changePercent >= 0 ? "+" : ""}{asset.changePercent}%           </p>         </div>       </header>        <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">         <div className="lg:col-span-2">           <HighChartAdvanced data={ohlcv.chartData} symbol={asset.symbol} />         </div>         <div className="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md space-y-4">           <h2 className="text-xl font-bold dark:text-white">자산 요약 정보</h2>           <div className="divide-y dark:divide-gray-700">             <div className="py-2 flex justify-between"><span className="text-gray-500">시가총액</span><span className="font-semibold dark:text-white">{asset.marketCap}</span></div>             <div className="py-2 flex justify-between"><span className="text-gray-500">거래량 (24H)</span><span className="font-semibold dark:text-white">{asset.volume24h}</span></div>             <div className="py-2 flex justify-between"><span className="text-gray-500">52주 최고가</span><span className="font-semibold dark:text-white">${asset.high52w}</span></div>             <div className="py-2 flex justify-between"><span className="text-gray-500">52주 최저가</span><span className="font-semibold dark:text-white">${asset.low52w}</span></div>           </div>         </div>       </div>        <div className="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md">         <h2 className="text-xl font-bold mb-4 dark:text-white">과거 시세 히스토리 (OHLCV)</h2>         <AgGridBaseTable rows={ohlcv.historyData} columns={columnDefs} height={400} />       </div>     </div>   ); }
  1. 자산 리스트에서 특정 종목을 누르면 /assets/1과 같은 상세 페이지로 매끄럽게 넘어갑니다. 로드된 데이터와 차트, 그리고 하단의 페이징 처리가 완료된 AgGrid 테이블이 고급스럽게 렌더링되는 것을 볼 수 있습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다