Portfolio 2026년 2월 22일
React 기반 대시보드 - 실시간 데이터 시각화 플랫폼
실시간 데이터 스트리밍 및 인터랙티브 시각화를 위한 React 대시보드
#React
#TypeScript
#D3.js
#WebSocket
#Material-UI
image: “/images/projects/react-dashboard.png”
프로젝트 개요
이 프로젝트는 실시간 데이터 스트리밍과 인터랙티브 시각화 기능을 제공하는 React 기반 대시보드입니다. 금융 데이터를 실시간으로 수집하고, 사용자가 직관적인 UI로 데이터를 분석할 수 있는 플랫폼을 구축했습니다.
주요 목표:
- 초당 100개 이상의 데이터 포인트를 실시간으로 렌더링
- 반응형 디자인으로 다양한 디바이스 지원
- 커스터마이징 가능한 차트 및 위젯 시스템
기술 스택
| 카테고리 | 기술 |
|---|---|
| 프레임워크 | React 18, TypeScript 5.x |
| 상태 관리 | Redux Toolkit, RTK Query |
| 시각화 | D3.js, Recharts |
| UI 라이브러리 | Material-UI v5, Emotion |
| 실시간 통신 | WebSocket, Socket.io |
| 빌드 도구 | Vite 5.x |
| 테스트 | Jest, React Testing Library |
주요 기능
1. 실시간 데이터 시각화
WebSocket을 통해 실시간으로 데이터를 수신하고 D3.js를 사용하여 동적 차트를 렌더링합니다. 가상 스크롤링 기능을 적용하여 대량 데이터를 효율적으로 표시합니다.
// 실시간 데이터 수신 커포넌트
import { useEffect, useState } from 'react';
import { useWebSocket } from '@/hooks/useWebSocket';
const RealTimeChart: React.FC = () => {
const [data, setData] = useState<DataPoint[]>([]);
const { socket, isConnected } = useWebSocket('wss://api.example.com/stream');
useEffect(() => {
socket.on('message', (newData: DataPoint) => {
setData(prev => [...prev.slice(-100), newData]); // 최근 100개만 유지
});
return () => socket.off('message');
}, [socket]);
return <LineChart data={data} />;
};
2. 커스터마이징 위젯 시스템
사용자가 대시보드 레이아웃을 직접 구성할 수 있는 드래그 앤 드롭 기능을 제공합니다.
// 위젯 레이아웃 컨테이너
import { useDroppable } from '@dnd-kit/core';
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
const WidgetContainer: React.FC = () => {
const { setNodeRef } = useDroppable({ id: 'dashboard' });
const { widgets } = useAppSelector(state => state.dashboard);
return (
<div ref={setNodeRef} className="dashboard-container">
<SortableContext items={widgets} strategy={verticalListSortingStrategy}>
{widgets.map(widget => (
<SortableWidget key={widget.id} widget={widget} />
))}
</SortableContext>
</div>
);
};
3. 성능 최적화
- React.memo: 불필요한 리렌더링 방지
- useMemo & useCallback: 계산 비용이 높은 연산 최적화
- Web Workers: 차트 렌더링 계산을 백그라운드로 처리
// 메모이제이션 예시
const memoizedChart = React.memo<ChartProps>(({ data, config }) => {
const chartData = useMemo(() => processData(data, config), [data, config]);
return <D3Chart data={chartData} />;
}, (prev, next) => {
return prev.data.length === next.data.length && prev.config === next.config;
});
개발 과정
1. 초기 설계 (1주)
- 사용자 요구사항 분석 및 와이어프레임 작성
- 컴포넌트 구조 설계 및 상태 관리 전략 수립
- 기술 스택 선정 (React vs Vue, Redux vs Zustand)
2. MVP 개발 (2주)
- 기본 차트 컴포넌트 구현
- WebSocket 연결 및 데이터 파이프라인 구축
- 기본 UI 레이아웃 완성
3. 기능 확장 (3주)
- 위젯 시스템 구현
- 다크 모드 및 테마 기능 추가
- 필터링 및 검색 기능 구현
4. 성능 최적화 (1주)
- 렌더링 성능 프로파일링
- 메모리 누수 수정
- 번들 사이즈 최적화 (12MB → 2.4MB)
문제 해결 과정
문제 1: 대량 데이터 렌더링 성능 저하
문제: 초당 100개 이상의 데이터가 들어올 때 UI가 멈추는 현상 발생
원인 분석:
- 전체 데이터 배열을 매번 순회하며 차트를 다시 그리는 과정에서 병목
- DOM 업데이트가 너무 잦음
해결책:
// 가상 스크롤링 적용
import { useVirtualizer } from '@tanstack/react-virtual';
const VirtualizedChartList: React.FC = () => {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: data.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
overscan: 10,
});
return (
<div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>
{virtualizer.getVirtualItems().map(item => (
<ChartItem key={item.key} data={data[item.index]} />
))}
</div>
);
};
결과: 렌더링 시간 800ms → 16ms로 개선 (FPS 60 유지)
문제 2: WebSocket 연결 끊김 처리
문제: 네트워크 불안정 시 자동 재연결이 되지 않음
해결책:
// 자동 재연결 로직
class WebSocketService {
private ws: WebSocket | null = null;
private retryCount = 0;
private maxRetries = 5;
private retryDelay = 1000;
connect() {
this.ws = new WebSocket('wss://api.example.com/stream');
this.ws.onclose = () => this.handleReconnect();
this.ws.onerror = () => this.handleReconnect();
}
private handleReconnect() {
if (this.retryCount < this.maxRetries) {
setTimeout(() => {
this.retryCount++;
this.connect();
}, this.retryDelay * this.retryCount);
}
}
}
성과와 배운 점
성과
- 사용자 경험: 페이지 로드 시간 3.2초 → 0.8초로 개선 (75% 감소)
- 성능: 1,000개 데이터 포인트 렌더링 시 60 FPS 유지
- 사용자 만족도: 4.5/5점 별점 평가
배운 점
- 성능 최적화의 중요성: 초기 설계 단계에서 성능을 고려해야 함
- TypeScript의 가치: 런타임 에러를 90% 이상 사전에 방지
- 상태 관리 패턴: Redux Toolkit은 복잡한 상태 관리에 적합
- 실시간 데이터 처리: WebSocket과 Polling의 장단점 이해
코드 스니펫
RTK Query를 활용한 서버 상태 관리
// api/dashboardApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const dashboardApi = createApi({
reducerPath: 'dashboardApi',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
tagTypes: ['Dashboard', 'Widget'],
endpoints: (builder) => ({
getDashboardData: builder.query<DashboardData, void>({
query: () => '/dashboard',
providesTags: ['Dashboard'],
}),
updateWidget: builder.mutation<Widget, Partial<Widget>>({
query: (body) => ({
url: '/widgets',
method: 'PUT',
body,
}),
invalidatesTags: ['Dashboard'],
}),
}),
});
export const { useGetDashboardDataQuery, useUpdateWidgetMutation } = dashboardApi;
향후 개선 계획
- 오프라인 모드 지원 (Service Worker)
- 데이터 내보내기 기능 (CSV, PDF)
- 사용자별 커스텀 대시보드 저장
- 머신러닝 기반 이상 탐지 알림
GitHub: https://github.com/username/react-dashboard Demo: https://react-dashboard-demo.vercel.app