이 프로젝트는 페이먼트 웹 애플리케이션을 구현한 것입니다. 실시간 거래 내역 조회, 주/월별 집계 데이터 시각화, 실시간 알림 기능 등을 제공합니다.
-
최신 거래 데이터 제공 API (api/transactions/recent)
-
주간/월간 집계 데이터 제공 API (api/transactions/aggregate)
-
Server-Sent Events (SSE)를 통한 알림 기능
SSE(Server-Sent Events)는 서버에서 클라이언트로 단방향 실시간 데이터 전송을 가능하게 하는 웹 기술입니다. 새로운 입출금 발생 시 서버에서 클라이언트로 알림을 보내기 위해서 사용했습니다.
- TanStack Query를 사용해서 데이터 페칭 및 캐싱
- 실시간 데이터 업데이트 및 토스트 알림
- 서버로부터 SSE를 통해 새 데이터 알림을 받으면 refetch를 수행
- 단, SSE 연결에 실패한 경우 클라이언트에서 풀링을 통해 유사한 기능을 구현 (Local 환경에서는 작동하나, Vercel 배포 시 SSE 연결 실패하여 대안으로 구성했습니다.)
- 실제로 새 데이터가 있는 경우 사용자에게 토스트 메시지로 알림
- 차트 시각화
- 주간/월간 데이터를 차트로 시각화
- 동일 타임스탬프의 데이터에 대해 2개의 툴팁 표시 (디자인에는 다른 시간대의 데이터에 툴팁에 표시되어 있으나, 유저가 그래프를 클릭했을 때 하나의 시간대만 선택할 수 있기에 변경했습니다.)
새로운 입출금 알림과 동시에 새로운 입출금 내역임을 확인할 수 있도록 애니메이션 효과를 줬습니다.
UTC기준으로 저장된 mock data의 timestamp에서 시간 단위를 맞추기 위해서 계산하는 로직을 추가했습니다.
Local 환경과 배포된 환경에서 동일하게 작동하기 위함입니다.
function getDateRange(period: AggregateType): {
startDate: Date;
endDate: Date;
} {
const now = new Date();
const localTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const localTime = toZonedTime(now, TIME_ZONE);
const endDate = endOfDay(subDays(localTime, 1));
let startDate: Date;
if (period === "Week") {
startDate = startOfDay(subDays(endDate, 6));
} else if (period === "Month") {
startDate = startOfDay(subMonths(endDate, 1));
} else {
throw new Error("Invalid period");
}
return {
startDate: localTimeZone === "UTC" ? addHours(startDate, -9) : startDate,
endDate: localTimeZone === "UTC" ? addHours(endDate, -9) : endDate,
};
}useEffect(() => {
if (data && previousDataRef.current) {
const typeChanged = previousTypeRef.current !== transactionType;
if (!typeChanged) {
const hasNewData = data.some(
(newItem) =>
!previousDataRef.current!.some(
(oldItem) =>
oldItem.timestamp === newItem.timestamp &&
oldItem.name === newItem.name,
),
);
if (hasNewData) {
toast({
title: "새로운 입출금 내역이 있습니다!",
});
}
}
}
previousDataRef.current = data || null;
previousTypeRef.current = transactionType;
}, [data, toast, transactionType]);이 로직은 새로운 데이터가 있을 때만 토스트 알림을 표시하며, 트랜잭션 타입 변경 시에는 알림을 표시하지 않습니다.