Skip to content

Commit 0f011da

Browse files
authored
Merge pull request #42 from DeveloperBlog-Devflow/feature/layout
feat: 로그인 안한 상태로 페이지 접근 시 제한
2 parents 2868abd + d771fee commit 0f011da

6 files changed

Lines changed: 93 additions & 4 deletions

File tree

app/(with-sidebar)/layout.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
'use client';
2+
13
import Sidebar from '@/components/common/Sidebar';
4+
import { useAuthGuard } from '@/hooks/useAuthGuard';
25

36
import '@uiw/react-md-editor/markdown-editor.css';
47
import '@uiw/react-markdown-preview/markdown.css';
@@ -8,6 +11,13 @@ export default function SidebarLayout({
811
}: {
912
children: React.ReactNode;
1013
}) {
14+
const { loading, isAuthed } = useAuthGuard({ redirectTo: '/login' });
15+
16+
if (loading) return <div>로딩중...</div>;
17+
if (!isAuthed) {
18+
return null;
19+
}
20+
1121
return (
1222
<div className="flex">
1323
<Sidebar />

components/auth/LoginForm.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,19 @@ const LoginForm = ({ handleOpenModal }: LoginFormProps) => {
2222

2323
const router = useRouter();
2424

25+
// 쿠키 설정 헬퍼 함수
26+
const setLoginCookie = () => {
27+
// 7일간 유지되는 쿠키 설정
28+
document.cookie = 'isLoggedIn=true; path=/; max-age=' + 60 * 60 * 24 * 7;
29+
};
30+
2531
const handleGithubLogin = async () => {
2632
if (isLoading) return;
2733
setIsLoading(true);
2834
setError('');
2935
try {
3036
await signupWithGithub();
37+
setLoginCookie(); // 쿠키 설정
3138
router.push('/');
3239
} catch (err: unknown) {
3340
setError('Github 로그인에 실패했습니다.');
@@ -58,6 +65,7 @@ const LoginForm = ({ handleOpenModal }: LoginFormProps) => {
5865
password
5966
);
6067
// console.log('로그인 성공!', userCredential.user);
68+
setLoginCookie(); // 쿠키 설정
6169
router.push('/');
6270
} catch (err) {
6371
// console.error('로그인 에러:', err);

components/common/Sidebar.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ const Sidebar = () => {
2626
const handleLogout = async (): Promise<void> => {
2727
try {
2828
await signOut(auth);
29+
document.cookie =
30+
'isLoggedIn=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT';
31+
alert('로그아웃 되었습니다.');
2932
router.replace('/landing');
30-
router.refresh(); // 서버 컴포넌트/세션 UI 갱신
3133
} catch (err) {
3234
console.error('로그아웃 실패:', err);
3335
alert('로그아웃에 실패했습니다.');

components/write/PostDetail.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ const Markdown = dynamic(
1515
const PostDetail = ({ tilId }: { tilId: string }) => {
1616
const router = useRouter();
1717
const [data, setData] = useState<Til | null>(null);
18-
const [loading, setLoading] = useState(true);
1918

2019
useEffect(() => {
2120
const unsub = auth.onAuthStateChanged(async (user) => {
@@ -26,13 +25,11 @@ const PostDetail = ({ tilId }: { tilId: string }) => {
2625

2726
const post = await fetchMyTil(user.uid, tilId);
2827
setData(post);
29-
setLoading(false);
3028
});
3129

3230
return () => unsub();
3331
}, [tilId, router]);
3432

35-
if (loading) return <div className="min-h-screen p-6">로딩중...</div>;
3633
if (!data) return <div className="min-h-screen p-6">글이 없습니다.</div>;
3734

3835
return (

hooks/useAuthGuard.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use client';
2+
3+
import { useEffect, useState } from 'react';
4+
import { onAuthStateChanged, type User } from 'firebase/auth';
5+
import { useRouter } from 'next/navigation';
6+
import { auth } from '@/lib/firebase';
7+
8+
type UseAuthGuardOptions = {
9+
/** 로그인 안 됐을 때 보낼 경로 */
10+
redirectTo?: string;
11+
};
12+
13+
type UseAuthGuardResult = {
14+
user: User | null;
15+
loading: boolean;
16+
isAuthed: boolean;
17+
};
18+
19+
export function useAuthGuard(
20+
options: UseAuthGuardOptions = {}
21+
): UseAuthGuardResult {
22+
const { redirectTo = '/login' } = options;
23+
24+
const router = useRouter();
25+
const [user, setUser] = useState<User | null>(null);
26+
const [loading, setLoading] = useState(true);
27+
28+
useEffect(() => {
29+
const unsub = onAuthStateChanged(auth, (u) => {
30+
if (!u) {
31+
setUser(null);
32+
setLoading(true);
33+
router.replace(redirectTo);
34+
return;
35+
}
36+
setUser(u);
37+
setLoading(false);
38+
});
39+
40+
return () => unsub();
41+
}, [router, redirectTo]);
42+
43+
return { user, loading, isAuthed: !!user };
44+
}

middleware.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { NextResponse } from 'next/server';
2+
import type { NextRequest } from 'next/server';
3+
4+
export function middleware(request: NextRequest) {
5+
const { pathname } = request.nextUrl;
6+
7+
// 쿠키에서 isLoggedIn 확인
8+
const isLoggedIn = request.cookies.get('isLoggedIn')?.value === 'true';
9+
10+
if (pathname === '/') {
11+
// 로그인이 안 된 경우에만 랜딩으로 보냄
12+
if (!isLoggedIn) {
13+
return NextResponse.redirect(new URL('/landing', request.url));
14+
}
15+
// 로그인 된 상태라면 그대로 루트('/')를 보여줌
16+
return NextResponse.next();
17+
}
18+
19+
if (pathname === '/landing' && isLoggedIn) {
20+
return NextResponse.redirect(new URL('/', request.url));
21+
}
22+
23+
return NextResponse.next();
24+
}
25+
26+
export const config = {
27+
matcher: ['/', '/landing'],
28+
};

0 commit comments

Comments
 (0)