6.4 검색 바 및 결과 페이지 라우팅 o

This commit is contained in:
koreacomp5
2025-10-09 16:35:19 +09:00
parent e7edb87269
commit c16b9c1a51
5 changed files with 60 additions and 5 deletions

View File

@@ -1,12 +1,14 @@
import { ThemeToggle } from "@/app/components/ThemeToggle";
import { SearchBar } from "@/app/components/SearchBar";
export function AppHeader() {
return (
<header style={{ display: "flex", justifyContent: "space-between", padding: 12 }}>
<div>msg App</div>
<nav style={{ display: "flex", gap: 12 }}>
<nav style={{ display: "flex", gap: 12, alignItems: "center" }}>
<a href="/"></a>
<a href="/boards"></a>
<SearchBar />
<ThemeToggle />
</nav>
</header>

View File

@@ -20,13 +20,14 @@ type Resp = {
const fetcher = (url: string) => fetch(url).then((r) => r.json());
export function PostList({ boardId, sort = "recent" }: { boardId?: string; sort?: "recent" | "popular" }) {
export function PostList({ boardId, sort = "recent", q }: { boardId?: string; sort?: "recent" | "popular"; q?: string }) {
const pageSize = 10;
const getKey = (index: number, prev: Resp | null) => {
if (prev && prev.items.length === 0) return null;
const page = index + 1;
const sp = new URLSearchParams({ page: String(page), pageSize: String(pageSize), sort });
if (boardId) sp.set("boardId", boardId);
if (q) sp.set("q", q);
return `/api/posts?${sp.toString()}`;
};
const { data, size, setSize, isLoading } = useSWRInfinite<Resp>(getKey, fetcher);
@@ -37,8 +38,18 @@ export function PostList({ boardId, sort = "recent" }: { boardId?: string; sort?
<div>
<div style={{ display: "flex", gap: 8, marginBottom: 8 }}>
<span>:</span>
<a href={`/?sort=recent`} style={{ textDecoration: sort === "recent" ? "underline" : "none" }}></a>
<a href={`/?sort=popular`} style={{ textDecoration: sort === "popular" ? "underline" : "none" }}></a>
<a
href={`${q ? "/search" : "/"}?${(() => { const p = new URLSearchParams(); if (q) p.set("q", q); if (boardId) p.set("boardId", boardId); p.set("sort", "recent"); return p.toString(); })()}`}
style={{ textDecoration: sort === "recent" ? "underline" : "none" }}
>
</a>
<a
href={`${q ? "/search" : "/"}?${(() => { const p = new URLSearchParams(); if (q) p.set("q", q); if (boardId) p.set("boardId", boardId); p.set("sort", "popular"); return p.toString(); })()}`}
style={{ textDecoration: sort === "popular" ? "underline" : "none" }}
>
</a>
</div>
<ul style={{ display: "flex", flexDirection: "column", gap: 8 }}>
{items.map((p) => (

View File

@@ -0,0 +1,28 @@
"use client";
import { useRouter } from "next/navigation";
import { useState } from "react";
export function SearchBar() {
const router = useRouter();
const [term, setTerm] = useState("");
return (
<form
onSubmit={(e) => {
e.preventDefault();
const q = term.trim();
router.push(q ? `/search?q=${encodeURIComponent(q)}` : "/search");
}}
style={{ display: "flex", gap: 8, alignItems: "center" }}
>
<input
value={term}
onChange={(e) => setTerm(e.target.value)}
placeholder="검색어 입력"
style={{ padding: "6px 8px", border: "1px solid #ddd", borderRadius: 6 }}
/>
<button type="submit"></button>
</form>
);
}

14
src/app/search/page.tsx Normal file
View File

@@ -0,0 +1,14 @@
import { PostList } from "@/app/components/PostList";
export default function SearchPage({ searchParams }: { searchParams?: { q?: string; sort?: "recent" | "popular" } }) {
const q = searchParams?.q ?? "";
const sort = searchParams?.sort ?? "recent";
return (
<div>
<h2 style={{ marginBottom: 12 }}>: {q || "전체"}</h2>
<PostList q={q} sort={sort} />
</div>
);
}