Skip to content

Commit 6f448e6

Browse files
Merge branch 'dev' into feature/plan-management-page
2 parents adb2196 + b759e96 commit 6f448e6

20 files changed

Lines changed: 670 additions & 60 deletions

File tree

app/(with-sidebar)/(home)/page.tsx

Lines changed: 25 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import ProfileSection from '@/components/home/ProfileSection';
77
import ButtonSection from '@/components/home/ButtonSection';
88

99
import { useEffect, useState } from 'react';
10+
import { useAuthUser } from '@/lib/auth/useAuthUser';
1011

1112
import {
1213
Todo,
@@ -16,18 +17,14 @@ import {
1617
} from '@/lib/home/todoService';
1718
import { getProfile, Profile } from '@/lib/home/profileService';
1819

19-
import { User, onAuthStateChanged } from 'firebase/auth';
20-
import { auth } from '@/lib/firebase';
21-
2220
const Page = () => {
21+
// 현재 사용자 정보
22+
const { user: currentUser, authLoading } = useAuthUser();
23+
2324
const [profile, setProfile] = useState<Profile | null>(null);
2425
const [todos, setTodos] = useState<Todo[]>([]);
25-
2626
const [error, setError] = useState<string | null>(null);
2727

28-
// 현재 사용자 정보
29-
const [currentUser, setCurrentUser] = useState<User | null>(null);
30-
3128
// 1. 할 일 목록 불러오기
3229
const loadTodos = async (uid: string) => {
3330
try {
@@ -41,25 +38,9 @@ const Page = () => {
4138
}
4239
};
4340

44-
// 2. 할 일 추가
45-
const handleAddTodo = async (text: string) => {
46-
if (!currentUser) return;
47-
48-
try {
49-
setError(null);
50-
51-
await AddTodo(currentUser.uid, text);
52-
await loadTodos(currentUser.uid);
53-
} catch (err) {
54-
console.error(err);
55-
setError('할 일을 추가하는 데 실패하였습니다');
56-
}
57-
};
58-
5941
// 3. 할 일 상태 토글
6042
const handleToggleTodo = async (id: string, currentStatus: boolean) => {
6143
if (!currentUser) return;
62-
6344
try {
6445
setError(null);
6546

@@ -71,35 +52,30 @@ const Page = () => {
7152
}
7253
};
7354

74-
// 사용자 로그인 상태 감지
75-
useEffect(() => {
76-
const unsubscribe = onAuthStateChanged(auth, (user) => {
77-
setCurrentUser(user);
78-
});
79-
80-
return () => unsubscribe();
81-
}, []);
82-
8355
// 사용자가 존재하면 데이터 불러옴
8456
useEffect(() => {
85-
if (currentUser) {
86-
const loadData = async () => {
87-
try {
88-
const userProfile = await getProfile(currentUser.uid);
89-
setProfile(userProfile);
90-
await loadTodos(currentUser.uid);
91-
} catch (err) {
92-
console.error(err);
93-
setError('프로필 정보를 불러오는 데 실패하였습니다');
94-
}
95-
};
96-
loadData();
97-
} else {
98-
setTodos([]);
99-
setProfile(null);
100-
}
57+
if (!currentUser) return;
58+
(async () => {
59+
try {
60+
const userProfile = await getProfile(currentUser.uid);
61+
setProfile(userProfile);
62+
await loadTodos(currentUser.uid);
63+
} catch (err) {
64+
console.error(err);
65+
setError('프로필 정보를 불러오는 데 실패하였습니다');
66+
}
67+
})();
10168
}, [currentUser]);
10269

70+
// 유저 정보 로딩 처리
71+
if (authLoading) {
72+
return (
73+
<div className="flex min-h-screen items-center justify-center">
74+
<p>로그인 확인 중...</p>
75+
</div>
76+
);
77+
}
78+
10379
// 유저 정보가 없을 시
10480
if (!currentUser) {
10581
return (
@@ -133,7 +109,7 @@ const Page = () => {
133109
/>
134110

135111
{/* 3. ButtonSection */}
136-
<ButtonSection onAddTodo={handleAddTodo} />
112+
<ButtonSection />
137113
</div>
138114
);
139115
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import Editor from '@/components/write/Editor';
2+
3+
type Props = {
4+
params: Promise<{ tilId: string }>;
5+
};
6+
7+
const Page = async ({ params }: Props) => {
8+
const { tilId } = await params;
9+
return (
10+
<div className="bg-background flex min-h-screen flex-col gap-8 p-4">
11+
<Editor tilId={tilId} />
12+
</div>
13+
);
14+
};
15+
16+
export default Page;

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 />

app/(with-sidebar)/plans/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ const Page = () => {
9999
<PageHeader
100100
title="플랜"
101101
highlight="관리하기"
102-
description="학습 주제를 만들고 세부 과제를 관리하세요"
102+
description="학습 주제를 만들고 세부 과제를 관리해보세요"
103103
/>
104104

105105
{/* 상단 통계 카드 (Grid) */}

app/(with-sidebar)/tils/page.tsx

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
'use client';
2+
3+
import { useEffect, useState } from 'react';
4+
5+
import PageHeader from '@/components/common/PageHeader';
6+
import ToolBar from '@/components/tils/ToolBar';
7+
import TilList from '@/components/tils/TilList';
8+
9+
import { useAuthUser } from '@/lib/auth/useAuthUser';
10+
import { fetchTilList } from '@/lib/tils/tilListService';
11+
import { deleteTil } from '@/services/write/til.service';
12+
13+
import type { TilItem } from '@/types/til';
14+
15+
const Page = () => {
16+
const { user, authLoading } = useAuthUser();
17+
18+
const [items, setItems] = useState<TilItem[]>([]);
19+
const [loading, setLoading] = useState(true);
20+
const [error, setError] = useState<string | null>(null);
21+
const [sort, setSort] = useState<'latest' | 'oldest'>('latest');
22+
const [sortedItems, setSortedItems] = useState<TilItem[]>([]);
23+
24+
const [searchTerm, setSearchTerm] = useState('');
25+
26+
// 데이터 가져오기
27+
useEffect(() => {
28+
if (!user) {
29+
setItems([]);
30+
setLoading(false);
31+
return;
32+
}
33+
34+
const loadData = async () => {
35+
try {
36+
setError(null);
37+
setLoading(true);
38+
const list = await fetchTilList(user.uid);
39+
setItems(list);
40+
} catch (e) {
41+
console.error(e);
42+
setError('일지 목록을 불러오지 못했습니다.');
43+
} finally {
44+
setLoading(false);
45+
}
46+
};
47+
48+
loadData();
49+
}, [user]);
50+
51+
// 정렬 처리
52+
useEffect(() => {
53+
const filtered = items.filter((item) =>
54+
// 제목에서 검색어 포함 여부 확인
55+
item.title.toLowerCase().includes(searchTerm.toLowerCase())
56+
);
57+
58+
// 필터링된 결과를 정렬
59+
const sorted = filtered.sort((a, b) => {
60+
return sort === 'latest'
61+
? b.createdAt - a.createdAt
62+
: a.createdAt - b.createdAt;
63+
});
64+
setSortedItems(sorted);
65+
}, [items, sort, searchTerm]);
66+
67+
const handleDelete = async (id: string) => {
68+
if (!user) return;
69+
70+
await deleteTil(user.uid, id);
71+
72+
setItems((prev) => prev.filter((item) => item.id !== id));
73+
};
74+
75+
const handleSortChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
76+
setSort(e.target.value as 'latest' | 'oldest');
77+
};
78+
79+
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
80+
setSearchTerm(e.target.value);
81+
};
82+
83+
if (authLoading) {
84+
return (
85+
<div className="flex min-h-screen items-center justify-center">
86+
<p>로그인 확인 중...</p>
87+
</div>
88+
);
89+
}
90+
91+
if (!user) {
92+
return (
93+
<div className="flex min-h-screen items-center justify-center">
94+
<p>로그인이 필요합니다.</p>
95+
</div>
96+
);
97+
}
98+
return (
99+
<div className="bg-background flex min-h-screen flex-col gap-5 px-30 py-11">
100+
<PageHeader
101+
title="일지"
102+
highlight="관리하기"
103+
description="작성한 일지를 관리해보세요"
104+
/>
105+
106+
<ToolBar
107+
items={sortedItems}
108+
onChangeSort={handleSortChange}
109+
searchTerm={searchTerm}
110+
onSearch={handleSearch}
111+
/>
112+
113+
{error && <p className="text-sm text-red-500">{error}</p>}
114+
115+
{loading ? (
116+
<p className="text-sm text-slate-400">불러오는 중...</p>
117+
) : (
118+
<TilList items={sortedItems} onDelete={handleDelete} />
119+
)}
120+
</div>
121+
);
122+
};
123+
124+
export default Page;

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: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { auth } from '@/lib/firebase';
1414

1515
const navItems = [
1616
{ label: '홈', href: '/', icon: Home },
17-
{ label: '개발 일지', href: '/logs', icon: ClipboardList },
17+
{ label: '개발 일지', href: '/tils', icon: ClipboardList },
1818
{ label: '계획 관리', href: '/plans', icon: CalendarPlus },
1919
{ label: '새 페이지 만들기', href: '/write', icon: CopyPlus },
2020
];
@@ -26,16 +26,18 @@ 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('로그아웃에 실패했습니다.');
3436
}
3537
};
3638

3739
return (
38-
<div className="fixed flex h-screen w-48 flex-col bg-white">
40+
<div className="fixed z-100 flex h-screen w-48 flex-col bg-white">
3941
<div className="flex items-center gap-2 px-6 py-5">
4042
<span className="text-primary text-lg font-bold">&lt;/&gt;</span>
4143
<span>DevFlow</span>

0 commit comments

Comments
 (0)