Skip to content

Commit 2591b69

Browse files
committed
2 parents 42e2438 + 93c9bef commit 2591b69

File tree

10 files changed

+108
-24
lines changed

10 files changed

+108
-24
lines changed
7.95 MB
Binary file not shown.

src/app/layout.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import '@/shared/styles/global.css';
22
import type { Metadata } from 'next';
3-
import '@/shared/styles/global.css';
43
import { Toaster } from 'react-hot-toast';
54
import ScrollTopBtnWrapper from '@/shared/components/scroll-top/ScrollTopBtnWrapper';
65
import KaKaoScript from './api/kakao/KaKaoScript';

src/domains/login/hook/useLoginRedirect.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useAuthStore } from '@/domains/shared/store/auth';
2-
import { useEffect, useState } from 'react';
2+
import { useEffect, useState, useRef } from 'react';
33
import { usePathname, useRouter } from 'next/navigation';
44
import { getCookie, removeCookie } from '@/domains/shared/auth/utils/cookie';
55
import { useToast } from '@/shared/hook/useToast';
@@ -12,6 +12,7 @@ export const useLoginRedirect = () => {
1212

1313
const [loading, setLoading] = useState(true);
1414
const [welcomeModalOpen, setWelcomeModalOpen] = useState(false);
15+
const hasShownToast = useRef(false);
1516

1617
useEffect(() => {
1718
if (!user && loading) {
@@ -39,7 +40,8 @@ export const useLoginRedirect = () => {
3940

4041
if (pathname.startsWith('/login/user/first-user')) {
4142
setWelcomeModalOpen(true);
42-
} else if (pathname.startsWith('/login/user/success')) {
43+
} else if (pathname.startsWith('/login/user/success') && !hasShownToast.current) {
44+
hasShownToast.current = true;
4345
toastSuccess(`${user.nickname}님 \n 로그인 성공 🎉`);
4446
router.replace(preLoginPath);
4547
setTimeout(() => removeCookie('preLoginPath'), 500);
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { useEffect, useRef, useState } from 'react';
2+
import { getApi } from '@/app/api/config/appConfig';
3+
4+
export function useSSENotification(isLoggedIn: boolean) {
5+
const [hasNewNotification, setHasNewNotification] = useState(false);
6+
const eventSourceRef = useRef<EventSource | null>(null);
7+
const isConnectingRef = useRef(false);
8+
9+
useEffect(() => {
10+
// 로그인 안 했으면 연결 안 함
11+
if (!isLoggedIn) {
12+
if (eventSourceRef.current) {
13+
eventSourceRef.current.close();
14+
eventSourceRef.current = null;
15+
isConnectingRef.current = false;
16+
}
17+
return;
18+
}
19+
20+
// 이미 연결 중이거나 연결되어 있으면 중복 방지
21+
if (isConnectingRef.current || eventSourceRef.current) {
22+
return;
23+
}
24+
25+
isConnectingRef.current = true;
26+
27+
const eventSource = new EventSource(`${getApi}/me/subscribe`, {
28+
withCredentials: true,
29+
});
30+
31+
eventSourceRef.current = eventSource;
32+
33+
eventSource.onopen = () => {
34+
isConnectingRef.current = false;
35+
};
36+
37+
eventSource.onmessage = () => {
38+
setHasNewNotification(true);
39+
};
40+
41+
eventSource.onerror = () => {
42+
isConnectingRef.current = false;
43+
44+
if (eventSource.readyState === EventSource.CLOSED) {
45+
eventSourceRef.current = null;
46+
}
47+
};
48+
49+
// cleanup 함수
50+
return () => {
51+
if (eventSourceRef.current) {
52+
eventSourceRef.current.close();
53+
eventSourceRef.current = null;
54+
}
55+
isConnectingRef.current = false;
56+
};
57+
}, [isLoggedIn]); // isLoggedIn만 의존성으로
58+
59+
const clearNotification = () => {
60+
setHasNewNotification(false);
61+
};
62+
63+
return { hasNewNotification, clearNotification };
64+
}

src/domains/main/components/mainSlide/components/mobile/MobileAbv.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ function MobileAbv() {
5353
<h2 className="text-xl sm:text-2xl font-black text-white">내 알콜도수 UP</h2>
5454
<button
5555
type="button"
56-
className={clsx(`block duration-300 sm:hidden`, isClick ? 'rotate-135' : 'rotate-0')}
56+
className={clsx(
57+
`block z-1 duration-300 sm:hidden`,
58+
isClick ? 'rotate-[135deg]' : 'rotate-0'
59+
)}
5760
onClick={() => setIsClick(!isClick)}
5861
>
5962
<Add />

src/domains/main/components/mainSlide/components/mobile/MobileSlideCommunity.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ function MobileSlideCommunity() {
1616
<h2 className="text-xl sm:text-2xl font-black text-white">함께 나누는 칵테일 이야기</h2>
1717
<button
1818
type="button"
19-
className={clsx(`block duration-300 sm:hidden`, isClick ? 'rotate-135' : 'rotate-0')}
19+
className={clsx(
20+
`block duration-300 z-1 sm:hidden`,
21+
isClick ? 'rotate-[135deg]' : 'rotate-0'
22+
)}
2023
onClick={() => setIsClick(!isClick)}
2124
>
2225
<Add />

src/domains/main/components/mainSlide/components/mobile/MobileSlideTest.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,15 @@ function MobileSlideTest() {
2525
<h2 className="text-xl sm:text-2xl font-black text-white">AI기반 취향테스트</h2>
2626
<button
2727
type="button"
28-
className={clsx(`block duration-300 sm:hidden`, isClick ? 'rotate-135' : 'rotate-0')}
29-
onClick={() => setIsClick(!isClick)}
28+
className={clsx(
29+
`block duration-300 z-1 sm:hidden`,
30+
isClick ? 'rotate-[135deg]' : 'rotate-0'
31+
)}
32+
onClick={() => {
33+
setIsClick(!isClick);
34+
}}
3035
>
31-
<Add />
36+
<Add className="pointer-events-none" />
3237
</button>
3338
</header>
3439
<div

src/domains/mypage/components/pages/my-active/MyLike.tsx

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,6 @@ function MyLike() {
2020
fetchLike();
2121
}, []);
2222

23-
useEffect(() => {
24-
console.log(myLike);
25-
}, [myLike]);
26-
27-
return (
28-
<section className="flex justify-center">
29-
{myLike.length > 0 ? (
30-
<PostCard posts={myLike} isLoading={isLoading} />
31-
) : (
32-
<div>아직 좋아요를 누른 글이 없습니다</div>
33-
)}
34-
</section>
35-
);
23+
return <PostCard posts={myLike} isLoading={isLoading} />;
3624
}
3725
export default MyLike;

src/shared/components/header/Header.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ function Header({ className, isMain = false }: Props) {
4646
return;
4747
}
4848

49+
if (y <= 10) {
50+
setVisible(true);
51+
setShowShadow(false);
52+
return;
53+
}
54+
4955
setLastScrollTop(y); // 마지막 위치 갱신
5056
}, [lastScrollTop, y]);
5157

src/shared/components/header/HeaderBtn.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,34 @@ import { useAuthStore } from '@/domains/shared/store/auth';
88
import { setPreLoginPath } from '@/domains/shared/auth/utils/setPreLoginPath';
99
import { useState } from 'react';
1010
import LogoutConfirm from '@/domains/login/components/LogoutConfirm';
11+
import { useSSENotification } from '@/domains/main/api/useSSENotification';
1112

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

18+
const { hasNewNotification, clearNotification } = useSSENotification(isLoggedIn);
19+
1720
const navButtons = [
1821
{
1922
icon: Bell,
2023
label: '알림',
2124
className: pathname === '/mypage/my-alarm' ? 'text-tertiary' : 'text-current',
2225
hiddenMobile: true,
23-
onClick: () => router.push('/mypage/my-alarm'),
26+
onClick: () => {
27+
clearNotification();
28+
router.push('/mypage/my-alarm');
29+
},
30+
showBadge: true,
2431
},
2532
{
2633
icon: User,
2734
label: '마이 페이지',
2835
className: pathname === '/mypage' ? 'text-tertiary' : 'text-current',
2936
hiddenMobile: true,
3037
onClick: () => router.push('/mypage'),
38+
showBadge: false,
3139
},
3240
];
3341

@@ -50,18 +58,24 @@ function HeaderBtn({ pathname }: { pathname: string }) {
5058
{/* 아이콘 버튼들 */}
5159
<div className="flex gap-2">
5260
{isLoggedIn &&
53-
navButtons.map(({ icon: Icon, label, onClick, className, hiddenMobile }) => (
61+
navButtons.map(({ icon: Icon, label, onClick, className, hiddenMobile, showBadge }) => (
5462
<button
5563
key={label}
5664
aria-label={label}
5765
onClick={onClick}
5866
className={tw(
5967
className,
6068
hiddenMobile ? 'hidden sm:flex' : '',
61-
'items-center justify-center rounded-full w-7 h-7 hover:bg-secondary/10 transition-colors duration-200'
69+
'relative items-center justify-center rounded-full w-7 h-7 hover:bg-secondary/10 transition-colors duration-200'
6270
)}
6371
>
6472
<Icon width={24} height={24} className="text-current" />
73+
{showBadge && hasNewNotification && (
74+
<span
75+
className=" absolute items-center justify-center top-1 right-1 w-2 h-2 bg-red-500
76+
rounded-full"
77+
></span>
78+
)}
6579
</button>
6680
))}
6781
</div>

0 commit comments

Comments
 (0)