From b10d41532ba70b1eed387ceb4351ecfa8188f1a0 Mon Sep 17 00:00:00 2001 From: mota Date: Sun, 2 Nov 2025 13:32:19 +0900 Subject: [PATCH] 123 --- prisma/seed.js | 10 +- public/svgs/01_bronze.svg | 9 + public/svgs/02_silver.svg.svg | 9 + public/svgs/03_gold.svg | 9 + public/svgs/04_platinum.svg | 9 + public/svgs/05_diamond.svg | 9 + public/svgs/06_master.svg | 9 + public/svgs/07_grandmaster.svg | 9 + public/svgs/08_god.svg | 9 + public/uploads/1762052938422-has0h33j4x6.webp | Bin 0 -> 34842 bytes public/uploads/1762053004600-0ewlk5af03i.webp | Bin 0 -> 22566 bytes src/app/api/posts/route.ts | 9 +- src/app/boards/[id]/page.tsx | 67 ++++-- src/app/components/AutoLoginAdmin.tsx | 47 ++++ src/app/components/BoardPanelClient.tsx | 40 +++- src/app/components/GradeIcon.tsx | 45 ++++ src/app/components/PostList.tsx | 79 ++++-- src/app/components/RankIcon1st.tsx | 4 +- src/app/components/RankIcon2nd.tsx | 4 +- src/app/components/RankIcon3rd.tsx | 4 +- src/app/globals.css | 30 +++ src/app/layout.tsx | 2 + src/app/my-page/page.tsx | 226 ++++++++++++++++++ src/app/page.tsx | 70 +++++- src/app/posts/[id]/page.tsx | 76 +++++- src/app/svgs/01_bronze.svg | 9 + src/app/svgs/02_silver.svg.svg | 9 + src/app/svgs/03_gold.svg | 9 + src/app/svgs/04_platinum.svg | 9 + src/app/svgs/05_diamond.svg | 9 + src/app/svgs/06_master.svg | 9 + src/app/svgs/07_grandmaster.svg | 9 + src/app/svgs/08_god.svg | 9 + src/middleware.ts | 30 ++- 34 files changed, 818 insertions(+), 69 deletions(-) create mode 100644 public/svgs/01_bronze.svg create mode 100644 public/svgs/02_silver.svg.svg create mode 100644 public/svgs/03_gold.svg create mode 100644 public/svgs/04_platinum.svg create mode 100644 public/svgs/05_diamond.svg create mode 100644 public/svgs/06_master.svg create mode 100644 public/svgs/07_grandmaster.svg create mode 100644 public/svgs/08_god.svg create mode 100644 public/uploads/1762052938422-has0h33j4x6.webp create mode 100644 public/uploads/1762053004600-0ewlk5af03i.webp create mode 100644 src/app/components/AutoLoginAdmin.tsx create mode 100644 src/app/components/GradeIcon.tsx create mode 100644 src/app/my-page/page.tsx create mode 100644 src/app/svgs/01_bronze.svg create mode 100644 src/app/svgs/02_silver.svg.svg create mode 100644 src/app/svgs/03_gold.svg create mode 100644 src/app/svgs/04_platinum.svg create mode 100644 src/app/svgs/05_diamond.svg create mode 100644 src/app/svgs/06_master.svg create mode 100644 src/app/svgs/07_grandmaster.svg create mode 100644 src/app/svgs/08_god.svg diff --git a/prisma/seed.js b/prisma/seed.js index eba5007..f89f2af 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -168,7 +168,12 @@ async function upsertRoles() { async function upsertAdmin() { const admin = await prisma.user.upsert({ where: { nickname: "admin" }, - update: { passwordHash: hashPassword("1234") }, + update: { + passwordHash: hashPassword("1234"), + grade: 7, + points: 1650000, + level: 200, + }, create: { nickname: "admin", name: "Administrator", @@ -177,6 +182,9 @@ async function upsertAdmin() { passwordHash: hashPassword("1234"), agreementTermsAt: new Date(), authLevel: "ADMIN", + grade: 7, + points: 1650000, + level: 200, }, }); diff --git a/public/svgs/01_bronze.svg b/public/svgs/01_bronze.svg new file mode 100644 index 0000000..35258e7 --- /dev/null +++ b/public/svgs/01_bronze.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/svgs/02_silver.svg.svg b/public/svgs/02_silver.svg.svg new file mode 100644 index 0000000..1c8cf00 --- /dev/null +++ b/public/svgs/02_silver.svg.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/svgs/03_gold.svg b/public/svgs/03_gold.svg new file mode 100644 index 0000000..b22c300 --- /dev/null +++ b/public/svgs/03_gold.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/svgs/04_platinum.svg b/public/svgs/04_platinum.svg new file mode 100644 index 0000000..9f5a33c --- /dev/null +++ b/public/svgs/04_platinum.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/svgs/05_diamond.svg b/public/svgs/05_diamond.svg new file mode 100644 index 0000000..2ed7671 --- /dev/null +++ b/public/svgs/05_diamond.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/svgs/06_master.svg b/public/svgs/06_master.svg new file mode 100644 index 0000000..c74b612 --- /dev/null +++ b/public/svgs/06_master.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/svgs/07_grandmaster.svg b/public/svgs/07_grandmaster.svg new file mode 100644 index 0000000..6e58412 --- /dev/null +++ b/public/svgs/07_grandmaster.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/svgs/08_god.svg b/public/svgs/08_god.svg new file mode 100644 index 0000000..ec4544b --- /dev/null +++ b/public/svgs/08_god.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/uploads/1762052938422-has0h33j4x6.webp b/public/uploads/1762052938422-has0h33j4x6.webp new file mode 100644 index 0000000000000000000000000000000000000000..a02680e427ed3e650221c1b2148f9a2985e18f62 GIT binary patch literal 34842 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 start end: z.coerce.date().optional(), // createdAt <= end }); @@ -58,7 +59,7 @@ export async function GET(req: Request) { if (!parsed.success) { return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 }); } - const { page, pageSize, boardId, q, sort = "recent", tag, author, start, end } = parsed.data; + const { page, pageSize, boardId, q, sort = "recent", tag, author, authorId, start, end } = parsed.data; const where = { NOT: { status: "deleted" as const }, ...(boardId ? { boardId } : {}), @@ -75,7 +76,11 @@ export async function GET(req: Request) { postTags: { some: { tag: { slug: tag } } }, } : {}), - ...(author + ...(authorId + ? { + authorId, + } + : author ? { author: { nickname: { contains: author } }, } diff --git a/src/app/boards/[id]/page.tsx b/src/app/boards/[id]/page.tsx index 0605df8..50aa719 100644 --- a/src/app/boards/[id]/page.tsx +++ b/src/app/boards/[id]/page.tsx @@ -3,6 +3,11 @@ import { HeroBanner } from "@/app/components/HeroBanner"; import { BoardToolbar } from "@/app/components/BoardToolbar"; import { headers } from "next/headers"; import prisma from "@/lib/prisma"; +import { UserAvatar } from "@/app/components/UserAvatar"; +import { RankIcon1st } from "@/app/components/RankIcon1st"; +import { RankIcon2nd } from "@/app/components/RankIcon2nd"; +import { RankIcon3rd } from "@/app/components/RankIcon3rd"; +import { GradeIcon } from "@/app/components/GradeIcon"; // Next 15: params/searchParams가 Promise가 될 수 있어 안전 언랩 처리합니다. export default async function BoardDetail({ params, searchParams }: { params: any; searchParams: any }) { @@ -29,15 +34,15 @@ export default async function BoardDetail({ params, searchParams }: { params: an }); const isSpecialRanking = boardView?.listViewType?.key === "list_special_rank"; - let rankingItems: { userId: string; nickname: string; points: number }[] = []; + let rankingItems: { userId: string; nickname: string; points: number; profileImage: string | null; grade: number }[] = []; if (isSpecialRanking) { const topUsers = await prisma.user.findMany({ - select: { userId: true, nickname: true, points: true }, + select: { userId: true, nickname: true, points: true, profileImage: true, grade: true }, where: { status: "active" }, orderBy: { points: "desc" }, take: 100, }); - rankingItems = topUsers.map((u) => ({ userId: u.userId, nickname: u.nickname, points: u.points })); + rankingItems = topUsers.map((u) => ({ userId: u.userId, nickname: u.nickname, points: u.points, profileImage: u.profileImage, grade: u.grade })); } return (

@@ -54,24 +59,46 @@ export default async function BoardDetail({ params, searchParams }: { params: an {!isSpecialRanking && }
{isSpecialRanking ? ( -
-
-

포인트 랭킹

-
-
    - {rankingItems.map((i, idx) => ( -
  1. -
    - {idx + 1} - {i.nickname || "회원"} +
    +
    + {rankingItems.map((i, idx) => { + const rank = idx + 1; + return ( +
    +
    +
    + {(rank === 1 || rank === 2 || rank === 3) && ( +
    + {rank === 1 && } + {rank === 2 && } + {rank === 3 && } +
    + )} +
    + {rank}위 +
    +
    + + + {i.nickname || "회원"} + + +
    +
    +
    + + + + {i.points.toLocaleString()} +
    +
    -
    {i.points}점
    -
  2. - ))} - {rankingItems.length === 0 && ( -
  3. 랭킹 데이터가 없습니다.
  4. - )} -
+ ); + })} +
+ {rankingItems.length === 0 && ( +
랭킹 데이터가 없습니다.
+ )}
) : ( { + // 쿠키에 uid가 없으면 어드민으로 자동 로그인 + const checkCookie = () => { + const cookies = document.cookie.split(";"); + const uidCookie = cookies.find((cookie) => cookie.trim().startsWith("uid=")); + + if (!uidCookie) { + // 어드민 사용자 정보 가져오기 + fetch("/api/auth/session") + .then((res) => res.json()) + .then((data) => { + if (!data.ok || !data.user) { + // 어드민으로 로그인 시도 + fetch("/api/auth/session", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ nickname: "admin", password: "1234" }), + }) + .then((res) => res.json()) + .then((loginData) => { + if (loginData.ok) { + // 페이지 새로고침하여 적용 + window.location.reload(); + } + }) + .catch(() => { + // 에러 무시 + }); + } + }) + .catch(() => { + // 에러 무시 + }); + } + }; + + checkCookie(); + }, []); + + return null; +} + diff --git a/src/app/components/BoardPanelClient.tsx b/src/app/components/BoardPanelClient.tsx index a9bad6c..5bebc63 100644 --- a/src/app/components/BoardPanelClient.tsx +++ b/src/app/components/BoardPanelClient.tsx @@ -8,6 +8,7 @@ import { RankIcon3rd } from "./RankIcon3rd"; import { UserAvatar } from "./UserAvatar"; import { ImagePlaceholderIcon } from "./ImagePlaceholderIcon"; import { PostList } from "./PostList"; +import { GradeIcon } from "./GradeIcon"; type BoardMeta = { id: string; @@ -21,12 +22,14 @@ type UserData = { nickname: string | null; points: number; profileImage: string | null; + grade: number; }; type PostData = { id: string; title: string; createdAt: Date; + content?: string | null; attachments?: { url: string }[]; stat?: { recommendCount: number | null }; }; @@ -61,6 +64,27 @@ export function BoardPanelClient({ return `${yyyy}.${mm}.${dd}`; } + function stripHtml(html: string | null | undefined): string { + if (!html) return ""; + // HTML 태그 제거 + return html.replace(/<[^>]*>/g, "").trim(); + } + + function extractImageFromContent(content: string | null | undefined): string | null { + if (!content) return null; + // img 태그에서 src 속성 추출 + const imgMatch = content.match(/]+src=["']([^"']+)["'][^>]*>/i); + if (imgMatch && imgMatch[1]) { + return imgMatch[1]; + } + // figure 안의 img 태그도 확인 + const figureMatch = content.match(/]*>.*?]+src=["']([^"']+)["'][^>]*>/is); + if (figureMatch && figureMatch[1]) { + return figureMatch[1]; + } + return null; + } + const isTextMain = board.mainTypeKey === "main_text"; const isSpecialRank = board.mainTypeKey === "main_special_rank"; const isPreview = board.mainTypeKey === "main_preview"; @@ -103,6 +127,9 @@ export function BoardPanelClient({ height={150} className="w-full h-full object-cover rounded-none" /> +
+ +
@@ -165,14 +192,15 @@ export function BoardPanelClient({
{selectedBoardData.previewPosts.map((post) => { - const firstImage = post.attachments?.[0]?.url; + // attachments에서 이미지를 먼저 찾고, 없으면 content에서 추출 + const firstImage = post.attachments?.[0]?.url || extractImageFromContent(post.content); return (
{firstImage ? ( {post.title} ) : ( @@ -191,7 +219,7 @@ export function BoardPanelClient({
n
- {post.title} + {stripHtml(post.title)}
@@ -244,14 +272,14 @@ export function BoardPanelClient({ {selectedBoardData.textPosts.map((p) => (
  • -
    +
    n
    - {p.title} + {stripHtml(p.title)} +{p.stat?.recommendCount ?? 0} -
    + {formatDateYmd(p.createdAt)}
  • diff --git a/src/app/components/GradeIcon.tsx b/src/app/components/GradeIcon.tsx new file mode 100644 index 0000000..6e2c975 --- /dev/null +++ b/src/app/components/GradeIcon.tsx @@ -0,0 +1,45 @@ +export function GradeIcon({ grade, width = 32, height = 32, className }: { grade: number; width?: number; height?: number; className?: string }) { + // grade: 0~7 (bronze, silver, gold, platinum, diamond, master, grandmaster, god) + const gradeImages = [ + "/svgs/01_bronze.svg", + "/svgs/02_silver.svg.svg", + "/svgs/03_gold.svg", + "/svgs/04_platinum.svg", + "/svgs/05_diamond.svg", + "/svgs/06_master.svg", + "/svgs/07_grandmaster.svg", + "/svgs/08_god.svg", + ]; + + const gradeIndex = Math.min(7, Math.max(0, grade)); + const imageSrc = gradeImages[gradeIndex]; + + return ( + {`등급 + ); +} + +// 등급 숫자를 등급 이름으로 변환하는 함수 +export function getGradeName(grade: number): string { + const gradeNames = [ + "Bronze", + "Silver", + "Gold", + "Platinum", + "Diamond", + "Master", + "Grandmaster", + "God", + ]; + + const gradeIndex = Math.min(7, Math.max(0, grade)); + return gradeNames[gradeIndex]; +} + diff --git a/src/app/components/PostList.tsx b/src/app/components/PostList.tsx index 42c055f..274d67c 100644 --- a/src/app/components/PostList.tsx +++ b/src/app/components/PostList.tsx @@ -29,20 +29,37 @@ type Resp = { const fetcher = (url: string) => fetch(url).then((r) => r.json()); -export function PostList({ boardId, sort = "recent", q, tag, author, start, end, variant = "default", newPostHref }: { boardId?: string; sort?: "recent" | "popular"; q?: string; tag?: string; author?: string; start?: string; end?: string; variant?: "default" | "board"; newPostHref?: string }) { - const pageSize = 10; +function stripHtml(html: string | null | undefined): string { + if (!html) return ""; + // HTML 태그 제거 + return html.replace(/<[^>]*>/g, "").trim(); +} + +export function PostList({ boardId, sort = "recent", q, tag, author, authorId, start, end, variant = "default", newPostHref }: { boardId?: string; sort?: "recent" | "popular"; q?: string; tag?: string; author?: string; authorId?: string; start?: string; end?: string; variant?: "default" | "board"; newPostHref?: string }) { const sp = useSearchParams(); const listContainerRef = useRef(null); const [lockedMinHeight, setLockedMinHeight] = useState(null); + + // board 변형에서는 URL에서 pageSize를 읽고, 기본값은 20 + const defaultPageSize = variant === "board" ? 20 : 10; + const pageSizeParam = sp.get("pageSize"); + const pageSize = pageSizeParam ? Math.min(50, Math.max(10, parseInt(pageSizeParam, 10))) : defaultPageSize; + // board 변형: 번호 페이지네이션 + const initialPage = useMemo(() => Math.max(1, parseInt(sp.get("page") || "1", 10)), [sp]); + const [page, setPage] = useState(initialPage); + const [currentPageSize, setCurrentPageSize] = useState(pageSize); + const getKey = (index: number, prev: Resp | null) => { if (prev && prev.items.length === 0) return null; const page = index + 1; + // 무한 스크롤은 board variant가 아닐 때 사용되므로 pageSize 사용 const sp = new URLSearchParams({ page: String(page), pageSize: String(pageSize), sort }); if (boardId) sp.set("boardId", boardId); if (q) sp.set("q", q); if (tag) sp.set("tag", tag); if (author) sp.set("author", author); + if (authorId) sp.set("authorId", authorId); if (start) sp.set("start", start); if (end) sp.set("end", end); return `/api/posts?${sp.toString()}`; @@ -50,29 +67,39 @@ export function PostList({ boardId, sort = "recent", q, tag, author, start, end, // default(무한 스크롤 형태) const { data, size, setSize, isLoading } = useSWRInfinite(getKey, fetcher, { revalidateFirstPage: false }); const itemsInfinite = data?.flatMap((d) => d.items) ?? []; - const canLoadMore = (data?.at(-1)?.items.length ?? 0) === pageSize; + const effectivePageSize = variant === "board" ? currentPageSize : pageSize; + const canLoadMore = (data?.at(-1)?.items.length ?? 0) === effectivePageSize; const isEmptyInfinite = !isLoading && itemsInfinite.length === 0; - - // board 변형: 번호 페이지네이션 - const initialPage = useMemo(() => Math.max(1, parseInt(sp.get("page") || "1", 10)), [sp]); - const [page, setPage] = useState(initialPage); + useEffect(() => { setPage(initialPage); }, [initialPage]); + useEffect(() => { setCurrentPageSize(pageSize); }, [pageSize]); + const singleKey = useMemo(() => { if (variant !== "board") return null; - const usp = new URLSearchParams({ page: String(page), pageSize: String(pageSize), sort }); + const usp = new URLSearchParams({ page: String(page), pageSize: String(currentPageSize), sort }); if (boardId) usp.set("boardId", boardId); if (q) usp.set("q", q); if (tag) usp.set("tag", tag); if (author) usp.set("author", author); + if (authorId) usp.set("authorId", authorId); if (start) usp.set("start", start); if (end) usp.set("end", end); return `/api/posts?${usp.toString()}`; - }, [variant, page, pageSize, sort, boardId, q, tag, author, start, end]); + }, [variant, page, currentPageSize, sort, boardId, q, tag, author, authorId, start, end]); const { data: singlePageResp, isLoading: isLoadingSingle } = useSWR(singleKey, fetcher); - const itemsSingle = singlePageResp?.items ?? []; - const totalSingle = singlePageResp?.total ?? 0; - const totalPages = Math.max(1, Math.ceil(totalSingle / pageSize)); - const isEmptySingle = !isLoadingSingle && itemsSingle.length === 0; + + // 이전 데이터를 유지하여 깜빡임 방지 + const [stableData, setStableData] = useState(null); + useEffect(() => { + if (singlePageResp) { + setStableData(singlePageResp); + } + }, [singlePageResp]); + + const itemsSingle = stableData?.items ?? []; + const totalSingle = stableData?.total ?? singlePageResp?.total ?? 0; + const totalPages = Math.max(1, Math.ceil(totalSingle / currentPageSize)); + const isEmptySingle = !isLoadingSingle && itemsSingle.length === 0 && !stableData; const items = variant === "board" ? itemsSingle : itemsInfinite; const isEmpty = variant === "board" ? isEmptySingle : isEmptyInfinite; @@ -149,7 +176,7 @@ export function PostList({ boardId, sort = "recent", q, tag, author, start, end,
    {p.isPinned && 공지} - {p.title} + {stripHtml(p.title)} {!!p.postTags?.length && (
    @@ -254,6 +281,30 @@ export function PostList({ boardId, sort = "recent", q, tag, author, start, end, Next
    +
    + 표시 개수 + +
    {newPostHref && ( diff --git a/src/app/components/RankIcon1st.tsx b/src/app/components/RankIcon1st.tsx index 507c642..7840e07 100644 --- a/src/app/components/RankIcon1st.tsx +++ b/src/app/components/RankIcon1st.tsx @@ -1,6 +1,6 @@ -export function RankIcon1st() { +export function RankIcon1st({ width = 20, height = 21 }: { width?: number; height?: number }) { return ( - + diff --git a/src/app/components/RankIcon2nd.tsx b/src/app/components/RankIcon2nd.tsx index 03ca799..d4c0c67 100644 --- a/src/app/components/RankIcon2nd.tsx +++ b/src/app/components/RankIcon2nd.tsx @@ -1,6 +1,6 @@ -export function RankIcon2nd() { +export function RankIcon2nd({ width = 20, height = 21 }: { width?: number; height?: number }) { return ( - + diff --git a/src/app/components/RankIcon3rd.tsx b/src/app/components/RankIcon3rd.tsx index 46687bb..b45f2fd 100644 --- a/src/app/components/RankIcon3rd.tsx +++ b/src/app/components/RankIcon3rd.tsx @@ -1,6 +1,6 @@ -export function RankIcon3rd() { +export function RankIcon3rd({ width = 20, height = 20 }: { width?: number; height?: number }) { return ( - + diff --git a/src/app/globals.css b/src/app/globals.css index a1bb6df..9ab0af2 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -70,4 +70,34 @@ html { scrollbar-gutter: stable both-edges; } .scrollbar-overlay { margin-bottom: -6px; /* 스크롤바 높이만큼 아래 마진 조정 */ padding-bottom: 6px; /* 스크롤바 공간 확보 */ +} + +/* 게시글 내용 스타일 */ +.prose figure { + margin: 1rem 0; + position: relative; +} + +.prose figure img { + max-width: 100%; + height: auto; + display: block; + margin: 0 auto; +} + +.prose figure .resize-handle, +.prose figure .delete-image-btn { + display: none; /* 읽기 전용에서는 리사이즈 핸들과 삭제 버튼 숨김 */ +} + +.prose div[style*="text-align"] { + margin: 0.5rem 0; +} + +.prose b { + font-weight: bold; +} + +.prose br { + line-height: 1.5; } \ No newline at end of file diff --git a/src/app/layout.tsx b/src/app/layout.tsx index ea24eb1..6cea40a 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,6 +4,7 @@ import QueryProvider from "@/app/QueryProvider"; import { AppHeader } from "@/app/components/AppHeader"; import { AppFooter } from "@/app/components/AppFooter"; import { ToastProvider } from "@/app/components/ui/ToastProvider"; +import { AutoLoginAdmin } from "@/app/components/AutoLoginAdmin"; export const metadata: Metadata = { @@ -21,6 +22,7 @@ export default function RootLayout({ +
    diff --git a/src/app/my-page/page.tsx b/src/app/my-page/page.tsx new file mode 100644 index 0000000..603cdfe --- /dev/null +++ b/src/app/my-page/page.tsx @@ -0,0 +1,226 @@ +import { headers } from "next/headers"; +import prisma from "@/lib/prisma"; +import Link from "next/link"; +import { UserAvatar } from "@/app/components/UserAvatar"; +import { GradeIcon, getGradeName } from "@/app/components/GradeIcon"; +import { HeroBanner } from "@/app/components/HeroBanner"; +import { PostList } from "@/app/components/PostList"; +import ProfileLabelIcon from "@/app/svgs/profilelableicon"; +export default async function MyPage({ searchParams }: { searchParams: Promise<{ tab?: string; page?: string; sort?: string; q?: string }> }) { + const sp = await searchParams; + const activeTab = sp?.tab || "posts"; + const page = parseInt(sp?.page || "1", 10); + const sort = sp?.sort || "recent"; + const q = sp?.q || ""; + + // 현재 로그인한 사용자 정보 가져오기 + let currentUser: { + userId: string; + nickname: string; + profileImage: string | null; + points: number; + level: number; + grade: number; + } | null = null; + + try { + const h = await headers(); + const cookieHeader = h.get("cookie") || ""; + const uid = cookieHeader + .split(";") + .map((s) => s.trim()) + .find((pair) => pair.startsWith("uid=")) + ?.split("=")[1]; + + if (uid) { + const user = await prisma.user.findUnique({ + where: { userId: decodeURIComponent(uid) }, + select: { userId: true, nickname: true, profileImage: true, points: true, level: true, grade: true }, + }); + if (user) currentUser = user; + } + } catch (e) { + // 에러 무시 + } + + // 로그인되지 않은 경우 어드민 사용자 가져오기 + if (!currentUser) { + const admin = await prisma.user.findUnique({ + where: { nickname: "admin" }, + select: { userId: true, nickname: true, profileImage: true, points: true, level: true, grade: true }, + }); + if (admin) currentUser = admin; + } + + if (!currentUser) { + return
    로그인이 필요합니다.
    ; + } + + // 통계 정보 가져오기 + const [postsCount, commentsCount, receivedMessagesCount, sentMessagesCount] = await Promise.all([ + prisma.post.count({ where: { authorId: currentUser.userId } }), + prisma.comment.count({ where: { authorId: currentUser.userId } }), + prisma.message.count({ where: { receiverId: currentUser.userId } }), + prisma.message.count({ where: { senderId: currentUser.userId } }), + ]); + + // 등급 업그레이드에 필요한 포인트 계산 (예시: 다음 등급까지) + const nextGradePoints = (() => { + // 등급별 포인트 기준을 여기에 설정 (임시로 2M) + const gradeThresholds = [0, 200000, 400000, 800000, 1600000, 3200000, 6400000, 10000000]; + const currentGradeIndex = Math.min(7, Math.max(0, currentUser.grade)); + return gradeThresholds[currentGradeIndex + 1] || 2000000; + })(); + + const tabs = [ + { key: "posts", label: "내가 쓴 게시글", count: postsCount }, + { key: "comments", label: "내가 쓴 댓글", count: commentsCount }, + { key: "messages-received", label: "받은 쪽지함", count: receivedMessagesCount }, + { key: "messages-sent", label: "보낸 쪽지함", count: sentMessagesCount }, + ]; + + return ( +
    + {/* 히어로 배너 */} +
    + +
    + + {/* 프로필 섹션 */} +
    +
    + {/* 좌측: 프로필 이미지, 닉네임, 레벨/등급/포인트 */} +
    +
    + {/* 프로필 이미지 */} +
    +
    + + + +
    +
    + +
    +
    + {/* 닉네임 */} +
    + {currentUser.nickname || "사용자"} + +
    +
    + {/* 레벨/등급/포인트 정보 - 가로 배치 */} +
    +
    + + 레벨 + Lv.{currentUser.level || 1} +
    +
    + + 등급 + {getGradeName(currentUser.grade)} +
    +
    + + 포인트 + {currentUser.points.toLocaleString()} +
    +
    +
    + + {/* 구분선 */} +
    + + {/* 우측: 등급 배지 및 포인트 진행 상황 */} +
    +
    + +
    +
    +
    {getGradeName(currentUser.grade)}
    +
    + {(currentUser.points / 1000000).toFixed(1)}M + / {(nextGradePoints / 1000000).toFixed(1)}M +
    +
    +
    +
    +
    + + {/* 탭 버튼 */} +
    +
    + {tabs.map((tab) => ( + + + {tab.label} + +
    + + {tab.count.toLocaleString()} + +
    + + ))} +
    +
    + + {/* 컨텐츠 영역 */} +
    + {activeTab === "posts" && ( + + )} + {activeTab === "comments" && ( +
    +
    댓글 기능은 준비 중입니다.
    +
    + )} + {activeTab === "messages-received" && ( +
    +
    받은 쪽지함 기능은 준비 중입니다.
    +
    + )} + {activeTab === "messages-sent" && ( +
    +
    보낸 쪽지함 기능은 준비 중입니다.
    +
    + )} +
    +
    + ); +} + diff --git a/src/app/page.tsx b/src/app/page.tsx index f434d96..12aec2e 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -10,12 +10,53 @@ import { RankIcon3rd } from "@/app/components/RankIcon3rd"; import { UserAvatar } from "@/app/components/UserAvatar"; import { ImagePlaceholderIcon } from "@/app/components/ImagePlaceholderIcon"; import { BoardPanelClient } from "@/app/components/BoardPanelClient"; +import { GradeIcon, getGradeName } from "@/app/components/GradeIcon"; import prisma from "@/lib/prisma"; +import { headers } from "next/headers"; export default async function Home({ searchParams }: { searchParams: Promise<{ sort?: "recent" | "popular" } | undefined> }) { const sp = await searchParams; const sort = sp?.sort ?? "recent"; + // 로그인된 사용자 정보 가져오기 (기본값: 어드민) + let currentUser: { + userId: string; + nickname: string; + profileImage: string | null; + points: number; + level: number; + grade: number; + } | null = null; + + try { + const h = await headers(); + const cookieHeader = h.get("cookie") || ""; + const uid = cookieHeader + .split(";") + .map((s) => s.trim()) + .find((pair) => pair.startsWith("uid=")) + ?.split("=")[1]; + + if (uid) { + const user = await prisma.user.findUnique({ + where: { userId: decodeURIComponent(uid) }, + select: { userId: true, nickname: true, profileImage: true, points: true, level: true, grade: true }, + }); + if (user) currentUser = user; + } + } catch (e) { + // 에러 무시 + } + + // 로그인되지 않은 경우 어드민 사용자 가져오기 + if (!currentUser) { + const admin = await prisma.user.findUnique({ + where: { nickname: "admin" }, + select: { userId: true, nickname: true, profileImage: true, points: true, level: true, grade: true }, + }); + if (admin) currentUser = admin; + } + // 메인페이지 설정 불러오기 const SETTINGS_KEY = "mainpage_settings" as const; const settingRow = await prisma.setting.findUnique({ where: { key: SETTINGS_KEY } }); @@ -94,7 +135,7 @@ export default async function Home({ searchParams }: { searchParams: Promise<{ s if (isSpecialRank) { specialRankUsers = await prisma.user.findMany({ - select: { userId: true, nickname: true, points: true, profileImage: true }, + select: { userId: true, nickname: true, points: true, profileImage: true, grade: true }, where: { status: "active" }, orderBy: { points: "desc" }, take: 3, @@ -106,6 +147,7 @@ export default async function Home({ searchParams }: { searchParams: Promise<{ s id: true, title: true, createdAt: true, + content: true, attachments: { where: { type: "image" }, orderBy: { sortOrder: "asc" }, @@ -187,50 +229,52 @@ export default async function Home({ searchParams }: { searchParams: Promise<{ s
    -
    - Lv -
    + {currentUser && ( +
    + +
    + )}
    -
    홍길동
    +
    {currentUser?.nickname || "사용자"}
    레벨
    -
    Lv. 79
    +
    Lv. {currentUser?.level || 1}
    등급
    -
    Iron
    +
    {getGradeName(currentUser?.grade || 0)}
    포인트
    -
    1,600,000
    +
    {(currentUser?.points || 0).toLocaleString()}
    - +