From 0c1dd965938eaa77e9f9b5c3b675c4e1664619b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=84=9C=EC=A0=9C=EA=B2=BD?= Date: Tue, 5 May 2026 19:08:56 +0900 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20=EC=82=AC=EC=9D=B4=EB=93=9C=EB=B0=94?= =?UTF-8?q?=20=EA=B0=80=EB=A1=9C=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EB=B0=8F?= =?UTF-8?q?=20=EC=8B=A4=EC=8B=9C=EA=B0=84=20=ED=8A=B8=EB=9E=98=ED=94=BD=20?= =?UTF-8?q?=EC=B0=A8=ED=8A=B8=20=EC=8A=A4=EC=BC=88=EB=A0=88=ED=86=A4=20?= =?UTF-8?q?=EA=B3=A0=EC=B0=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사이드바 열림 애니메이션 중 overflow-x-hidden 추가로 가로 스크롤바 노출 방지 - fetchEventSource URL에 VITE_API_BASE_URL 적용하여 Vercel 배포 환경에서 SSE 연결 실패 수정 --- src/components/sidebar/Sidebar.tsx | 4 +- src/hooks/dashboard/useClickStream.ts | 66 ++++++++++++++------------- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/src/components/sidebar/Sidebar.tsx b/src/components/sidebar/Sidebar.tsx index b0567c44..b8a6dfd6 100644 --- a/src/components/sidebar/Sidebar.tsx +++ b/src/components/sidebar/Sidebar.tsx @@ -97,7 +97,9 @@ export default function Sidebar() { aria-label="사이드바 내비게이션" className={twMerge( "flex flex-1 flex-col gap-1 px-2 min-h-0", - isCollapsed ? "overflow-visible" : "overflow-y-auto", + isCollapsed + ? "overflow-visible" + : "overflow-y-auto overflow-x-hidden", )} > {mainNav.map((item) => { diff --git a/src/hooks/dashboard/useClickStream.ts b/src/hooks/dashboard/useClickStream.ts index a63bb8ea..64a85a5e 100644 --- a/src/hooks/dashboard/useClickStream.ts +++ b/src/hooks/dashboard/useClickStream.ts @@ -7,6 +7,7 @@ import useAuthStore from "@/store/useAuthStore"; import useWorkspaceStore from "@/store/useWorkspaceStore"; const MAX_RETRIES = 3; +const BASE_URL = (import.meta.env.VITE_API_BASE_URL ?? "").replace(/\/$/, ""); export function useClickStream(mode: "real" | "dummy" = "dummy") { // TODO: 테스트용 - 더미 데이터가 orgId 1에만 있어서 임시 고정, 테스트 후 원래대로 복구할 예정 @@ -26,40 +27,43 @@ export function useClickStream(mode: "real" | "dummy" = "dummy") { const controller = new AbortController(); retryCountRef.current = 0; - fetchEventSource(`/api/dashboard/${orgId}/clicks/stream?mode=${mode}`, { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - signal: controller.signal, - onopen: async (response) => { - if (response.ok) { - retryCountRef.current = 0; - setIsError(false); - } - }, - onmessage(event) { - if (event.event === "org-click-update") { - try { - const wrapper = JSON.parse(event.data); - const parsed: IClickStreamItem = wrapper.data ?? wrapper; - setData(parsed); - if (parsed.suspectDetail) { - setSuspectDetail(parsed.suspectDetail); + fetchEventSource( + `${BASE_URL}/api/dashboard/${orgId}/clicks/stream?mode=${mode}`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + signal: controller.signal, + onopen: async (response) => { + if (response.ok) { + retryCountRef.current = 0; + setIsError(false); + } + }, + onmessage(event) { + if (event.event === "org-click-update") { + try { + const wrapper = JSON.parse(event.data); + const parsed: IClickStreamItem = wrapper.data ?? wrapper; + setData(parsed); + if (parsed.suspectDetail) { + setSuspectDetail(parsed.suspectDetail); + } + } catch { + // 파싱 실패 시 해당 이벤트 무시하고 스트림 유지 } - } catch { - // 파싱 실패 시 해당 이벤트 무시하고 스트림 유지 } - } - }, - onerror(err) { - retryCountRef.current += 1; - if (retryCountRef.current >= MAX_RETRIES) { - setIsError(true); - throw err; // 재시도 중단 - } - // MAX_RETRIES 미만이면 기본 재시도 동작 유지 + }, + onerror(err) { + retryCountRef.current += 1; + if (retryCountRef.current >= MAX_RETRIES) { + setIsError(true); + throw err; // 재시도 중단 + } + // MAX_RETRIES 미만이면 기본 재시도 동작 유지 + }, }, - }); + ); return () => controller.abort(); }, [orgId, accessToken, mode]); From d8fd5a02fb0418602a150d23cb41ec54c472fe9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=84=9C=EC=A0=9C=EA=B2=BD?= Date: Tue, 5 May 2026 19:14:03 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20=EC=84=A4=EC=A0=95=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EC=A4=91=EB=B3=B5=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/sidebarNav.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/constants/sidebarNav.ts b/src/constants/sidebarNav.ts index 0a36fe91..68a46a62 100644 --- a/src/constants/sidebarNav.ts +++ b/src/constants/sidebarNav.ts @@ -61,12 +61,6 @@ export const mainNav: INavItem[] = [ }, ], }, - { - id: "settings", - label: "설정", - icon: SettingsIcon, - path: "/setting", - }, ]; export const footerNav: INavItem[] = [ From b7d1f9cd9e46e2c59e1e69798afbb20442a47bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=84=9C=EC=A0=9C=EA=B2=BD?= Date: Tue, 5 May 2026 19:21:49 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=ED=94=8C=EB=9E=AB=ED=8F=BC?= =?UTF-8?q?=EB=B3=84=20=EB=8C=80=EC=8B=9C=EB=B3=B4=EB=93=9C=20=EC=8B=A4?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=ED=8A=B8=EB=9E=98=ED=94=BD=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=20Coming=20Soon=20=EB=B0=B0=EC=A7=80=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dashboard/platform/AllPlatformView.tsx | 13 +++++++++++-- .../dashboard/platform/SinglePlatformView.tsx | 9 +++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/components/dashboard/platform/AllPlatformView.tsx b/src/components/dashboard/platform/AllPlatformView.tsx index 5bb66c88..45449951 100644 --- a/src/components/dashboard/platform/AllPlatformView.tsx +++ b/src/components/dashboard/platform/AllPlatformView.tsx @@ -103,8 +103,17 @@ export default function AllPlatformView({ isLoading }: IAllPlatformViewProps) { {/* 실시간 트래픽 변화 */} - - {isLoading ? : null} + + {isLoading ? ( + + ) : ( +
+ Coming Soon +

+ 실시간 클릭 트래픽 모니터링 기능을 준비하고 있습니다. +

+
+ )}
{/* 개별 플랫폼 상세 */} diff --git a/src/components/dashboard/platform/SinglePlatformView.tsx b/src/components/dashboard/platform/SinglePlatformView.tsx index 2cd89011..fc498c16 100644 --- a/src/components/dashboard/platform/SinglePlatformView.tsx +++ b/src/components/dashboard/platform/SinglePlatformView.tsx @@ -171,9 +171,14 @@ export default function SinglePlatformView({
-
+
+ Coming Soon +

+ 실시간 클릭 트래픽 모니터링 기능을 준비하고 있습니다. +

+
Date: Tue, 5 May 2026 19:32:38 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20SSE=20onopen=EC=97=90=EC=84=9C=20con?= =?UTF-8?q?tent-type=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/dashboard/useClickStream.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/hooks/dashboard/useClickStream.ts b/src/hooks/dashboard/useClickStream.ts index 64a85a5e..b3ddd383 100644 --- a/src/hooks/dashboard/useClickStream.ts +++ b/src/hooks/dashboard/useClickStream.ts @@ -35,10 +35,15 @@ export function useClickStream(mode: "real" | "dummy" = "dummy") { }, signal: controller.signal, onopen: async (response) => { - if (response.ok) { - retryCountRef.current = 0; - setIsError(false); + const contentType = response.headers.get("content-type") ?? ""; + const isSse = contentType.includes("text/event-stream"); + if (!response.ok || !isSse) { + throw new Error( + `Invalid SSE response: status=${response.status}, content-type=${contentType}`, + ); } + retryCountRef.current = 0; + setIsError(false); }, onmessage(event) { if (event.event === "org-click-update") {