diff --git a/src/api/core/index.ts b/src/api/core/index.ts
index ec080064..f8a1ff99 100644
--- a/src/api/core/index.ts
+++ b/src/api/core/index.ts
@@ -56,6 +56,7 @@ baseAPI.interceptors.response.use(
await API.authService.refresh();
return baseAPI(originalRequest);
} catch (refreshError) {
+ await API.authService.logout();
if (isServer) {
const { redirect } = await import('next/navigation');
redirect('/login');
diff --git a/src/app/notification/page.tsx b/src/app/notification/page.tsx
index a9ae07ba..53c18d7d 100644
--- a/src/app/notification/page.tsx
+++ b/src/app/notification/page.tsx
@@ -2,26 +2,33 @@
import { EmptyState } from '@/components/layout/empty-state';
import { NotificationCard, NotificationHeader } from '@/components/pages/notification';
+import { useIntersectionObserver } from '@/hooks/use-intersection-observer';
import { useGetNotificationsInfinite } from '@/hooks/use-notification/use-notification-get-list';
export default function NotificationPage() {
- const { data: list, fetchNextPage } = useGetNotificationsInfinite({ size: 1 });
+ const {
+ data: list,
+ fetchNextPage,
+ hasNextPage,
+ isFetchingNextPage,
+ } = useGetNotificationsInfinite({ size: 20 });
+
+ const fetchObserverRef = useIntersectionObserver({
+ onIntersect: () => {
+ if (hasNextPage && !isFetchingNextPage) {
+ fetchNextPage();
+ }
+ },
+ });
if (!list) return;
return (
-
-
{list.length > 0 && list.map((item) => )}
+ {hasNextPage && 다음
}
{list.length === 0 && 아직 받은 알림이 없어요.}
-
);
}
diff --git a/src/components/layout/header/index.tsx b/src/components/layout/header/index.tsx
index 18c72538..69620f4c 100644
--- a/src/components/layout/header/index.tsx
+++ b/src/components/layout/header/index.tsx
@@ -23,15 +23,19 @@ export const Header = () => {
receivedNewNotification && 'animate-ring text-mint-500',
)}
/>
-
-
- {unReadCount}
-
+ {unReadCount > 0 && (
+ <>
+
+
+ {unReadCount}
+
+ >
+ )}
diff --git a/src/components/pages/notification/notification-card/index.tsx b/src/components/pages/notification/notification-card/index.tsx
index a33fa907..62eb90f4 100644
--- a/src/components/pages/notification/notification-card/index.tsx
+++ b/src/components/pages/notification/notification-card/index.tsx
@@ -31,7 +31,7 @@ export const NotificationCard = ({ item }: Props) => {
diff --git a/src/components/pages/notification/notification-header/index.tsx b/src/components/pages/notification/notification-header/index.tsx
index 33e2ae4a..b3831151 100644
--- a/src/components/pages/notification/notification-header/index.tsx
+++ b/src/components/pages/notification/notification-header/index.tsx
@@ -2,14 +2,29 @@
import { useRouter } from 'next/navigation';
import { Icon } from '@/components/icon';
+import { useUpdateNotificationReadAll } from '@/hooks/use-notification';
+import { cn } from '@/lib/utils';
+import { useNotification } from '@/providers';
export const NotificationHeader = () => {
const router = useRouter();
+ const { unReadCount } = useNotification();
+ const { mutateAsync } = useUpdateNotificationReadAll();
+
const handleHistoryBackClick = () => {
router.back();
};
+ const handleReadAllClick = async () => {
+ if (unReadCount === 0) return;
+ try {
+ await mutateAsync();
+ } catch {
+ alert('요청 처리에 실패했습니다.');
+ }
+ };
+
return (
);
};
diff --git a/src/hooks/use-notification/use-notification-connect-sse/index.ts b/src/hooks/use-notification/use-notification-connect-sse/index.ts
index d7ed91fc..50350379 100644
--- a/src/hooks/use-notification/use-notification-connect-sse/index.ts
+++ b/src/hooks/use-notification/use-notification-connect-sse/index.ts
@@ -52,6 +52,7 @@ export const useConnectSSE = () => {
console.log('SSE 수신 성공:', data);
setReceivedNewNotification(true);
queryClient.invalidateQueries({ queryKey: notificationKeys.unReadCount() });
+ queryClient.invalidateQueries({ queryKey: notificationKeys.list() });
// TODO: 알림 타입별 처리 추가 예정
} catch (error) {
console.error('SSE 데이터 파싱 실패:', error);
@@ -62,6 +63,7 @@ export const useConnectSSE = () => {
es.onerror = (_error) => {
console.log('SSE 오류 발생:');
// todo: 재 연결 로직 추가 필요
+ accessToken.value = null;
};
// SSE Cleanup
@@ -70,7 +72,7 @@ export const useConnectSSE = () => {
es.close();
eventSourceRef.current = null;
};
- }, [accessToken.value, queryClient]);
+ }, [accessToken, accessToken.value, queryClient]);
return { receivedNewNotification };
};
diff --git a/src/lib/formatDateTime.ts b/src/lib/formatDateTime.ts
index e171ae37..6c44a524 100644
--- a/src/lib/formatDateTime.ts
+++ b/src/lib/formatDateTime.ts
@@ -10,6 +10,8 @@ export const formatTimeAgo = (isoString: string) => {
const dateInput = new Date(isoString.endsWith('Z') ? isoString : `${isoString}Z`);
const dateNow = new Date();
+ if (dateInput.getTime() >= dateNow.getTime()) return '0초 전';
+
const diffPerSec = Math.floor((dateNow.getTime() - dateInput.getTime()) / 1000);
if (diffPerSec < 60) return `${diffPerSec}초 전`;
diff --git a/src/styles/base.css b/src/styles/base.css
index deab9e0e..d050307c 100644
--- a/src/styles/base.css
+++ b/src/styles/base.css
@@ -5,3 +5,7 @@
button {
cursor: pointer;
}
+
+button:disabled {
+ cursor: not-allowed;
+}