diff --git a/src/components/Notification/NotificationList.tsx b/src/components/Notification/NotificationList.tsx
new file mode 100644
index 0000000..d11904e
--- /dev/null
+++ b/src/components/Notification/NotificationList.tsx
@@ -0,0 +1,77 @@
+import { If } from '@/system/utils/If';
+import { useGetNotificationList } from './apis/useGetNotificationList';
+import { Icon } from '@/system/components';
+import { Spacing } from '@/system/utils/Spacing';
+import { dday, formatToYYMMDD } from '@/utils/date';
+import { cn } from '@/utils/tailwind-util';
+import { useRouter } from 'next/navigation';
+import { NotificationType } from '@/types/notification';
+import { useNotificationContext } from './context';
+
+export function NotificationList() {
+ const router = useRouter();
+ const { close } = useNotificationContext();
+
+ const { data: notificationList } = useGetNotificationList();
+
+ const getDateText = (date: string) => {
+ if (dday(date) >= 0) {
+ return '오늘';
+ }
+
+ return formatToYYMMDD(date, { separator: '.' });
+ };
+
+ const handleNotificationClick = (notification: NotificationType) => {
+ router.push(`/my-recruit/${notification.referenceId}`);
+ close();
+ };
+
+ return (
+ <>
+
+
+
+
+
+
지금은 알림이 없어요
+
알림은 30일 뒤에 자동으로 사라져요
+
+
+
+
+
+ 0}>
+
+ {notificationList?.map((notification) => (
+
+
+
+
+
+
+
+ handleNotificationClick(notification)}>
+ {notification.title}
+
+
+
+
+
+
+ {notification.message}
+
+
+
+ {getDateText(notification.createdAt)}
+
+
+
+
+ ))}
+
+ >
+ );
+}
diff --git a/src/components/Notification/NotificationWindow.tsx b/src/components/Notification/NotificationWindow.tsx
index 32f8df6..deed97f 100644
--- a/src/components/Notification/NotificationWindow.tsx
+++ b/src/components/Notification/NotificationWindow.tsx
@@ -5,7 +5,8 @@ import { TouchButton } from '../TouchButton';
import { color } from '@/system/token/color';
import { useNotificationContext } from './context';
import { AnimatePresence, motion } from 'framer-motion';
-import { Spacing } from '@/system/utils/Spacing';
+import { NotificationList } from './NotificationList';
+import { AsyncBoundaryWithQuery } from '@/lib';
export function NotificationWindow() {
const { isOpen, close } = useNotificationContext();
@@ -24,16 +25,11 @@ export function NotificationWindow() {
-
-
-
-
-
지금은 알림이 없어요
-
알림은 30일 뒤에 자동으로 사라져요
-
-
+
-
)}
diff --git a/src/components/Notification/apis/useGetNotificationCount.ts b/src/components/Notification/apis/useGetNotificationCount.ts
new file mode 100644
index 0000000..55f76c6
--- /dev/null
+++ b/src/components/Notification/apis/useGetNotificationCount.ts
@@ -0,0 +1,23 @@
+import { useQuery } from '@tanstack/react-query';
+import { http } from '@/apis/http';
+
+export const GET_NOTIFICATION_COUNT = 'notification-count';
+
+interface GetNotificationCountResponse {
+ number: number;
+}
+
+const getNotificationCount = () => {
+ return http.get
({ url: `/notifications/num` });
+};
+
+export const useGetNotificationCount = () => {
+ return useQuery({
+ queryKey: [GET_NOTIFICATION_COUNT],
+ queryFn: async () => {
+ const res = await getNotificationCount();
+
+ return res.data;
+ },
+ });
+};
diff --git a/src/components/Notification/apis/useGetNotificationList.ts b/src/components/Notification/apis/useGetNotificationList.ts
new file mode 100644
index 0000000..9b4efeb
--- /dev/null
+++ b/src/components/Notification/apis/useGetNotificationList.ts
@@ -0,0 +1,22 @@
+import { useSuspenseQuery } from '@tanstack/react-query';
+import { NotificationType } from '@/types/notification';
+import { http } from '@/apis/http';
+
+export const GET_NOTIFICATION_LIST = 'notification-list';
+
+type GetNotificationListResponse = NotificationType[];
+
+const getNotificationList = () => {
+ return http.get({ url: `/notifications` });
+};
+
+export const useGetNotificationList = () => {
+ return useSuspenseQuery({
+ queryKey: [GET_NOTIFICATION_LIST],
+ queryFn: async () => {
+ const res = await getNotificationList();
+
+ return res.data;
+ },
+ });
+};
diff --git a/src/components/Notification/apis/usePutNotificationRead.ts b/src/components/Notification/apis/usePutNotificationRead.ts
new file mode 100644
index 0000000..b47e618
--- /dev/null
+++ b/src/components/Notification/apis/usePutNotificationRead.ts
@@ -0,0 +1,20 @@
+import { http } from '@/apis/http';
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { GET_NOTIFICATION_COUNT } from './useGetNotificationCount';
+import { GET_NOTIFICATION_LIST } from './useGetNotificationList';
+
+const putNotificationRead = () => {
+ return http.put({ url: `/notifications` });
+};
+
+export const usePutNotificationRead = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: putNotificationRead,
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: [GET_NOTIFICATION_COUNT] });
+ queryClient.invalidateQueries({ queryKey: [GET_NOTIFICATION_LIST] });
+ },
+ });
+};
diff --git a/src/components/Notification/context.tsx b/src/components/Notification/context.tsx
index 76fcd9a..d049f00 100644
--- a/src/components/Notification/context.tsx
+++ b/src/components/Notification/context.tsx
@@ -2,6 +2,7 @@
import { generateContext } from '@/lib';
import { useState } from 'react';
+import { usePutNotificationRead } from './apis/usePutNotificationRead';
interface NotificationContext {
isOpen: boolean;
@@ -17,11 +18,16 @@ const [NotificationWrapper, useNotificationContext] = generateContext setIsOpen(true);
- const close = () => setIsOpen(false);
+ const close = () => {
+ setIsOpen(false);
+ readNotification();
+ };
- const toggle = () => setIsOpen((prev) => !prev);
+ const toggle = () => (isOpen ? close() : open());
return (
diff --git a/src/container/Sidebar/Sidebar.tsx b/src/container/Sidebar/Sidebar.tsx
index 546c244..90012c1 100644
--- a/src/container/Sidebar/Sidebar.tsx
+++ b/src/container/Sidebar/Sidebar.tsx
@@ -18,6 +18,7 @@ import { PropsWithChildren, useState } from 'react';
import { Collapsible } from './Collapsible/Collapsible';
import { useNotificationContext } from '@/components/Notification/context';
import { LogoOnlyLeaf } from '@/components/LogoOnlyLeaf';
+import { useGetNotificationCount } from '@/components/Notification/apis/useGetNotificationCount';
export function Sidebar() {
const router = useRouter();
@@ -28,6 +29,7 @@ export function Sidebar() {
const { isOpen: isNotificationOpen, toggle: toggleNotification } = useNotificationContext();
+ const { data: notificationCount } = useGetNotificationCount();
const { data: typeCounts } = useGetCardTypeCount();
const { data: recruiteTitles } = useGetRecruitTitles();
@@ -86,10 +88,15 @@ export function Sidebar() {
+ {notificationCount?.number || '0'}
+
+ }
onClick={toggleNotification}
/>
{/* */}
diff --git a/src/system/components/Icon/Icon.tsx b/src/system/components/Icon/Icon.tsx
index 55b0131..0ef40b9 100644
--- a/src/system/components/Icon/Icon.tsx
+++ b/src/system/components/Icon/Icon.tsx
@@ -50,6 +50,8 @@ import Warning from './SVG/Warning';
import { WorkFill } from './SVG/WorkFill';
import { X } from './SVG/X';
import { IllustAlarm } from './SVG/IllustAlarm';
+import { PageOpen } from './SVG/PageOpen';
+import { BellWithRedDot } from './SVG/BellWithRedDot';
import { Backspace } from './SVG/Backspace';
import { SavingSuccess } from './SVG/SavingSuccess';
@@ -105,6 +107,8 @@ const iconMap = {
announcementFolder: AnnouncementFolder,
IllustAlarm: IllustAlarm,
warning: Warning,
+ pageOpen: PageOpen,
+ bellWithRedDot: BellWithRedDot,
backspace: Backspace,
savingSuccess: SavingSuccess,
} as const;
diff --git a/src/system/components/Icon/SVG/BellWithRedDot.tsx b/src/system/components/Icon/SVG/BellWithRedDot.tsx
new file mode 100644
index 0000000..5d58b2e
--- /dev/null
+++ b/src/system/components/Icon/SVG/BellWithRedDot.tsx
@@ -0,0 +1,20 @@
+import { IconBaseType } from './type';
+
+export function BellWithRedDot({ size, color }: IconBaseType) {
+ return (
+
+ );
+}
diff --git a/src/system/components/Icon/SVG/PageOpen.tsx b/src/system/components/Icon/SVG/PageOpen.tsx
new file mode 100644
index 0000000..e97564e
--- /dev/null
+++ b/src/system/components/Icon/SVG/PageOpen.tsx
@@ -0,0 +1,8 @@
+export function PageOpen() {
+ return (
+
+ );
+}
diff --git a/src/types/notification.ts b/src/types/notification.ts
new file mode 100644
index 0000000..e92f362
--- /dev/null
+++ b/src/types/notification.ts
@@ -0,0 +1,9 @@
+export interface NotificationType {
+ id: number;
+ title: string;
+ message: string;
+ isRead: boolean;
+ type: string;
+ referenceId: number;
+ createdAt: string;
+}