인기글 추가
All checks were successful
deploy-on-main / deploy (push) Successful in 28s

This commit is contained in:
koreacomp5
2025-11-28 06:12:58 +09:00
parent c85450ce37
commit afc714022f
7 changed files with 294 additions and 14 deletions

View File

@@ -0,0 +1,92 @@
import { NextResponse } from "next/server";
import prisma from "@/lib/prisma";
export async function GET(req: Request) {
const { searchParams } = new URL(req.url);
const boardId = searchParams.get("boardId");
const period = searchParams.get("period") || "daily"; // daily | weekly
// 날짜 범위 계산
const now = new Date();
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0);
const endOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
const startOfWeek = new Date(now);
startOfWeek.setDate(now.getDate() - 7);
startOfWeek.setHours(0, 0, 0, 0);
const dateFilter = period === "daily"
? {
date: {
gte: startOfToday,
lte: endOfToday,
}
}
: { date: { gte: startOfWeek } };
// 일일 조회수 테이블에서 조회수 합계 계산
const dailyViews = await prisma.dailyPostView.groupBy({
by: ["postId"],
where: dateFilter,
_sum: {
viewCount: true,
},
orderBy: {
_sum: {
viewCount: "desc",
},
},
take: 20, // 조회수 상위 20개만 가져와서 게시글 정보 조회
});
if (dailyViews.length === 0) {
return NextResponse.json({ items: [], period });
}
const postIds = dailyViews.map((dv) => dv.postId);
// 게시글 정보 조회
const posts = await prisma.post.findMany({
where: {
id: { in: postIds },
status: "published",
...(boardId ? { boardId } : {}),
},
select: {
id: true,
title: true,
createdAt: true,
boardId: true,
board: { select: { id: true, name: true, slug: true } },
isPinned: true,
status: true,
author: { select: { userId: true, nickname: true } },
stat: { select: { recommendCount: true, views: true, commentsCount: true } },
postTags: { select: { tag: { select: { name: true, slug: true } } } },
},
});
// 조회수와 게시글 매핑
const viewCountMap = new Map(
dailyViews.map((dv) => [dv.postId, dv._sum.viewCount ?? 0])
);
// 조회수 순으로 정렬 (조회수가 0보다 큰 것만)
const postsWithViews = posts
.map((post) => ({
...post,
viewCount: viewCountMap.get(post.id) ?? 0,
}))
.filter((post) => post.viewCount > 0) // 조회수가 0보다 큰 것만
.sort((a, b) => {
// 고정글 우선
if (a.isPinned && !b.isPinned) return -1;
if (!a.isPinned && b.isPinned) return 1;
// 조회수 순
if (b.viewCount !== a.viewCount) return b.viewCount - a.viewCount;
// 최신순
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
})
.slice(0, 5); // 상위 5개만
return NextResponse.json({ items: postsWithViews, period });
}