Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
248f260
[refactor] 무한스크롤 tanstack
mtm-git1018 Oct 14, 2025
2960ca0
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 14, 2025
e7b7e3b
[refactor] 레시피페이지 리팩토링
mtm-git1018 Oct 14, 2025
6945950
[refactor] 아코디언박스
mtm-git1018 Oct 14, 2025
3e5306e
[feat]필터링 뒤로가기 스크롤 저장
mtm-git1018 Oct 15, 2025
311548b
[chore] 머지 전 커밋
mtm-git1018 Oct 15, 2025
d134c55
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 15, 2025
0e5ac99
[chore]머지 전 커밋 누락 내용 커밋
mtm-git1018 Oct 15, 2025
7dacb80
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 15, 2025
b38afe6
[refactor]리팩토링 커밋
mtm-git1018 Oct 15, 2025
f02f555
[refactor] 정렬 중복아이템문제
mtm-git1018 Oct 15, 2025
2b825cb
[feat] 칵테일 정렬기능
mtm-git1018 Oct 15, 2025
d8caccc
[fix]댓글 알림 수정
mtm-git1018 Oct 15, 2025
bc6d1a9
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 15, 2025
ff02efa
[chore] 충돌사항 수정
mtm-git1018 Oct 15, 2025
c3452c4
Merge branch 'dev' into refactor/recipeFetch
mtm-git1018 Oct 15, 2025
0befdd8
[docs] 필요없는 폰트파일 정리
mtm-git1018 Oct 15, 2025
a864f98
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 15, 2025
86fb7f6
[feat]sse설정
mtm-git1018 Oct 15, 2025
84384ef
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 15, 2025
a629822
[feat]sse알림설정
mtm-git1018 Oct 15, 2025
deefde9
[chore]머지후 커밋
mtm-git1018 Oct 15, 2025
8de2150
[style] 메인페이지 아코디언 오류 수정
mtm-git1018 Oct 15, 2025
2052704
[feat]알림 SSE기능
mtm-git1018 Oct 15, 2025
44ca357
Merge remote-tracking branch 'origin/dev' into refactor/recipeFetch
mtm-git1018 Oct 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import '@/shared/styles/global.css';
import type { Metadata } from 'next';
import '@/shared/styles/global.css';
import { Toaster } from 'react-hot-toast';
import ScrollTopBtnWrapper from '@/shared/components/scroll-top/ScrollTopBtnWrapper';
import KaKaoScript from './api/kakao/KaKaoScript';
Expand Down
64 changes: 64 additions & 0 deletions src/domains/main/api/useSSENotification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { useEffect, useRef, useState } from 'react';
import { getApi } from '@/app/api/config/appConfig';

export function useSSENotification(isLoggedIn: boolean) {
const [hasNewNotification, setHasNewNotification] = useState(false);
const eventSourceRef = useRef<EventSource | null>(null);
const isConnectingRef = useRef(false);

useEffect(() => {
// 로그인 안 했으면 연결 안 함
if (!isLoggedIn) {
if (eventSourceRef.current) {
eventSourceRef.current.close();
eventSourceRef.current = null;
isConnectingRef.current = false;
}
return;
}

// 이미 연결 중이거나 연결되어 있으면 중복 방지
if (isConnectingRef.current || eventSourceRef.current) {
return;
}

isConnectingRef.current = true;

const eventSource = new EventSource(`${getApi}/me/subscribe`, {
withCredentials: true,
});

eventSourceRef.current = eventSource;

eventSource.onopen = () => {
isConnectingRef.current = false;
};

eventSource.onmessage = () => {
setHasNewNotification(true);
};

eventSource.onerror = () => {
isConnectingRef.current = false;

if (eventSource.readyState === EventSource.CLOSED) {
eventSourceRef.current = null;
}
};

// cleanup 함수
return () => {
if (eventSourceRef.current) {
eventSourceRef.current.close();
eventSourceRef.current = null;
}
isConnectingRef.current = false;
};
}, [isLoggedIn]); // isLoggedIn만 의존성으로

const clearNotification = () => {
setHasNewNotification(false);
};

return { hasNewNotification, clearNotification };
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ function MobileAbv() {
<h2 className="text-xl sm:text-2xl font-black text-white">내 알콜도수 UP</h2>
<button
type="button"
className={clsx(`block duration-300 sm:hidden`, isClick ? 'rotate-135' : 'rotate-0')}
className={clsx(
`block z-1 duration-300 sm:hidden`,
isClick ? 'rotate-[135deg]' : 'rotate-0'
)}
onClick={() => setIsClick(!isClick)}
>
<Add />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ function MobileSlideCommunity() {
<h2 className="text-xl sm:text-2xl font-black text-white">함께 나누는 칵테일 이야기</h2>
<button
type="button"
className={clsx(`block duration-300 sm:hidden`, isClick ? 'rotate-135' : 'rotate-0')}
className={clsx(
`block duration-300 z-1 sm:hidden`,
isClick ? 'rotate-[135deg]' : 'rotate-0'
)}
onClick={() => setIsClick(!isClick)}
>
<Add />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,15 @@ function MobileSlideTest() {
<h2 className="text-xl sm:text-2xl font-black text-white">AI기반 취향테스트</h2>
<button
type="button"
className={clsx(`block duration-300 sm:hidden`, isClick ? 'rotate-135' : 'rotate-0')}
onClick={() => setIsClick(!isClick)}
className={clsx(
`block duration-300 z-1 sm:hidden`,
isClick ? 'rotate-[135deg]' : 'rotate-0'
)}
onClick={() => {
setIsClick(!isClick);
}}
>
<Add />
<Add className="pointer-events-none" />
</button>
</header>
<div
Expand Down
14 changes: 1 addition & 13 deletions src/domains/mypage/components/pages/my-active/MyLike.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,6 @@ function MyLike() {
fetchLike();
}, []);

useEffect(() => {
console.log(myLike);
}, [myLike]);

return (
<section className="flex justify-center">
{myLike.length > 0 ? (
<PostCard posts={myLike} isLoading={isLoading} />
) : (
<div>아직 좋아요를 누른 글이 없습니다</div>
)}
</section>
);
return <PostCard posts={myLike} isLoading={isLoading} />;
}
export default MyLike;
20 changes: 17 additions & 3 deletions src/shared/components/header/HeaderBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,34 @@ import { useAuthStore } from '@/domains/shared/store/auth';
import { setPreLoginPath } from '@/domains/shared/auth/utils/setPreLoginPath';
import { useState } from 'react';
import LogoutConfirm from '@/domains/login/components/LogoutConfirm';
import { useSSENotification } from '@/domains/main/api/useSSENotification';

function HeaderBtn({ pathname }: { pathname: string }) {
const { isLoggedIn } = useAuthStore();
const router = useRouter();
const [logoutModalOpen, setLogoutModalOpen] = useState(false);

const { hasNewNotification, clearNotification } = useSSENotification(isLoggedIn);

const navButtons = [
{
icon: Bell,
label: '알림',
className: pathname === '/mypage/my-alarm' ? 'text-tertiary' : 'text-current',
hiddenMobile: true,
onClick: () => router.push('/mypage/my-alarm'),
onClick: () => {
clearNotification();
router.push('/mypage/my-alarm');
},
showBadge: true,
},
{
icon: User,
label: '마이 페이지',
className: pathname === '/mypage' ? 'text-tertiary' : 'text-current',
hiddenMobile: true,
onClick: () => router.push('/mypage'),
showBadge: false,
},
];

Expand All @@ -50,18 +58,24 @@ function HeaderBtn({ pathname }: { pathname: string }) {
{/* 아이콘 버튼들 */}
<div className="flex gap-2">
{isLoggedIn &&
navButtons.map(({ icon: Icon, label, onClick, className, hiddenMobile }) => (
navButtons.map(({ icon: Icon, label, onClick, className, hiddenMobile, showBadge }) => (
<button
key={label}
aria-label={label}
onClick={onClick}
className={tw(
className,
hiddenMobile ? 'hidden sm:flex' : '',
'items-center justify-center rounded-full w-7 h-7 hover:bg-secondary/10 transition-colors duration-200'
'relative items-center justify-center rounded-full w-7 h-7 hover:bg-secondary/10 transition-colors duration-200'
)}
>
<Icon width={24} height={24} className="text-current" />
{showBadge && hasNewNotification && (
<span
className=" absolute items-center justify-center top-1 right-1 w-2 h-2 bg-red-500
rounded-full"
></span>
)}
</button>
))}
</div>
Expand Down