Skip to content

Commit f824747

Browse files
committed
최다작성자/주간인기글 적용
1 parent 2b84636 commit f824747

3 files changed

Lines changed: 133 additions & 17 deletions

File tree

src/App.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { HashRouter, Routes, Route, useLocation, Navigate, useNavigate } from "react-router-dom";
22
import { useEffect, useState } from "react";
3+
import { promptLogin } from "./utils/auth";
34

45
import Header from "./components/header/Header";
56
import Footer from "./components/footer/Footer"; // Footer 컴포넌트 임포트 유지
@@ -83,6 +84,14 @@ function AppContent() {
8384
localStorage.setItem("theme", isDark ? "dark" : "light");
8485
}, [isDark]);
8586

87+
const MyPageGuard = ({ element }) => {
88+
if (!isLoggedIn) {
89+
promptLogin(undefined, { redirectTo: location.pathname });
90+
return <Navigate to="/" replace />;
91+
}
92+
return element;
93+
};
94+
8695
return (
8796
<>
8897
{!isSignupPage && (
@@ -109,7 +118,12 @@ function AppContent() {
109118
<Route path="/broadcast" element={<Codecast />} />
110119
<Route path="/startbroadcast" element={<StartCodecast />} />
111120
<Route path="/broadcast/live" element={<CodecastLive />} />
112-
<Route path="/mypage" element={<MyPageLayout nickname={nickname} />}>
121+
<Route
122+
path="/mypage"
123+
element={(
124+
<MyPageGuard element={<MyPageLayout nickname={nickname} />} />
125+
)}
126+
>
113127
<Route index element={<Mypage nickname={nickname} />} />
114128
<Route path="project" element={<MyProject />} />
115129
<Route path="community" element={<MyCommunity nickname={nickname} />} />

src/components/community/Community.css

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@
9292
gap: 12px;
9393
}
9494

95+
.top-writers-empty {
96+
margin: 0;
97+
font-size: 13px;
98+
color: var(--community-muted);
99+
}
100+
95101
.top-writers li {
96102
display: flex;
97103
justify-content: space-between;
@@ -457,13 +463,37 @@
457463
gap: 14px;
458464
}
459465

466+
.popular-posts-empty {
467+
margin: 0;
468+
font-size: 13px;
469+
color: var(--community-muted);
470+
}
471+
460472
.popular-posts li {
461473
display: flex;
462474
flex-direction: column;
463475
gap: 4px;
464476
font-size: 13px;
465477
}
466478

479+
.popular-posts li button {
480+
display: flex;
481+
flex-direction: column;
482+
gap: 4px;
483+
align-items: flex-start;
484+
width: 100%;
485+
padding: 0;
486+
border: none;
487+
background: none;
488+
cursor: pointer;
489+
text-align: left;
490+
color: inherit;
491+
}
492+
493+
.popular-posts li button:hover .post-title {
494+
color: var(--community-accent);
495+
}
496+
467497
.popular-posts .post-title {
468498
margin: 0;
469499
font-size: 14px;
@@ -476,6 +506,11 @@
476506
color: var(--community-muted);
477507
}
478508

509+
.popular-posts .post-likes {
510+
font-size: 12px;
511+
color: var(--community-muted);
512+
}
513+
479514
.error {
480515
color: #c2435b;
481516
font-weight: 600;

src/components/community/Community.jsx

Lines changed: 83 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,57 @@ export default function Community() {
219219
return sortedPosts.slice(start, end);
220220
}, [sortedPosts, currentPage]);
221221

222+
const topWriters = useMemo(() => {
223+
if (!posts.length) return [];
224+
225+
const counts = posts.reduce((acc, post) => {
226+
const author = (post.author || "익명").trim();
227+
if (!author) return acc;
228+
acc[author] = (acc[author] ?? 0) + 1;
229+
return acc;
230+
}, {});
231+
232+
return Object.entries(counts)
233+
.sort((a, b) => {
234+
const countDiff = b[1] - a[1];
235+
if (countDiff !== 0) return countDiff;
236+
return a[0].localeCompare(b[0]);
237+
})
238+
.slice(0, 7)
239+
.map(([name, count]) => ({ name, count }));
240+
}, [posts]);
241+
242+
const weeklyPopular = useMemo(() => {
243+
if (!posts.length) return [];
244+
245+
const oneWeekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
246+
const withinWeek = posts.filter((post) =>
247+
Number.isFinite(post.createdAtMs) && post.createdAtMs >= oneWeekAgo
248+
);
249+
250+
const source = withinWeek.length ? withinWeek : posts;
251+
252+
return [...source]
253+
.sort((a, b) => {
254+
const likeDiff = (b.likes ?? 0) - (a.likes ?? 0);
255+
if (likeDiff !== 0) return likeDiff;
256+
const commentDiff = (b.comments ?? 0) - (a.comments ?? 0);
257+
if (commentDiff !== 0) return commentDiff;
258+
const timeA = Number.isFinite(a.createdAtMs) ? a.createdAtMs : 0;
259+
const timeB = Number.isFinite(b.createdAtMs) ? b.createdAtMs : 0;
260+
if (timeA !== timeB) return timeB - timeA;
261+
return (b.id ?? 0) - (a.id ?? 0);
262+
})
263+
.slice(0, 5)
264+
.map((post) => ({
265+
id: post.id,
266+
title: post.title,
267+
author: post.author,
268+
likes: post.likes ?? 0,
269+
createdAt: post.date,
270+
}));
271+
}, [posts]);
272+
222273
const jumpBy = 5;
223274
const goToPage = (page) => {
224275
if (page < 1 || page > totalPages) return;
@@ -250,15 +301,18 @@ export default function Community() {
250301
</ul>
251302
<div className="top-writers">
252303
<h4>Zivorp TOP Writers</h4>
253-
<ol>
254-
<li><span>y2gcoder</span><span>10</span></li>
255-
<li><span>durams</span><span>8</span></li>
256-
<li><span>David</span><span>7</span></li>
257-
<li><span>식빵</span><span>10</span></li>
258-
<li><span>이선희</span><span>10</span></li>
259-
<li><span>찹찹이</span><span>10</span></li>
260-
<li><span>Rio song</span><span>10</span></li>
261-
</ol>
304+
{topWriters.length ? (
305+
<ol>
306+
{topWriters.map(({ name, count }) => (
307+
<li key={name}>
308+
<span>{name}</span>
309+
<span>{count}</span>
310+
</li>
311+
))}
312+
</ol>
313+
) : (
314+
<p className="top-writers-empty">아직 활동 기록이 없어요.</p>
315+
)}
262316
</div>
263317
</aside>
264318

@@ -435,13 +489,26 @@ export default function Community() {
435489
</div>
436490
<div className="popular-posts">
437491
<h4>주간 인기글</h4>
438-
<ul>
439-
<li><div className="post-title">버블 정렬 시각화 프로젝트 공유합니다</div><div className="post-author">김코딩</div></li>
440-
<li><div className="post-title">그래프 탐색 알고리즘 비교: BFS vs DFS</div><div className="post-author">이알고</div></li>
441-
<li><div className="post-title">동적 프로그래밍 문제 해결 가이드</div><div className="post-author">박코딩</div></li>
442-
<li><div className="post-title">백엔드 신입 CS 스터디 3기 모집</div><div className="post-author">김지훈</div></li>
443-
<li><div className="post-title">AI 실전 활용을 위한 4주 집중 스터디, 애사모!</div><div className="post-author">Edun</div></li>
444-
</ul>
492+
{weeklyPopular.length ? (
493+
<ul>
494+
{weeklyPopular.map((post) => (
495+
<li key={post.id}>
496+
<button
497+
type="button"
498+
onClick={() => navigate(`/community/post/${post.id}`)}
499+
>
500+
<div className="post-title">{post.title}</div>
501+
<div className="post-author">{post.author || "익명"}</div>
502+
{typeof post.likes === "number" && (
503+
<div className="post-likes">좋아요 {post.likes}</div>
504+
)}
505+
</button>
506+
</li>
507+
))}
508+
</ul>
509+
) : (
510+
<p className="popular-posts-empty">인기 게시글을 불러오는 중입니다.</p>
511+
)}
445512
</div>
446513
</aside>
447514
</div>

0 commit comments

Comments
 (0)