Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 15 additions & 18 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import Header from "@/widgets/header/ui/Header";
import { ModalContainer } from "@/shared/ui/ModalContainer/ModalContainer";
import ChatContainer from "@/features/chat/ui/ChatContainer";
import { ReactQueryProvider } from "@/shared/lib/provider";
import { ClientLayout } from "@/shared/lib/ClientLayout";

const myFont = localFont({
src: "../shared/fonts/PretendardVariable.woff2",
Expand All @@ -26,27 +25,25 @@ export default function RootLayout({
<html lang="kr" className={myFont.className}>
<body className="mx-auto max-w-[1200px] bg-[#1c1c22] pt-[70px] md:pt-[80px] xl:pt-[100px]">
<ReactQueryProvider>
<ClientLayout>
<Suspense fallback={null}>
<Header />
</Suspense>
<Suspense fallback={null}>
<Header />
</Suspense>

<Suspense
fallback={<div className="p-4 text-gray-400">Loading...</div>}
>
{children}
</Suspense>
<Suspense
fallback={<div className="p-4 text-gray-400">Loading...</div>}
>
{children}
</Suspense>

<div id="modal-root" />
<div id="modal-root" />

<Suspense fallback={null}>
<ChatContainer />
</Suspense>
<Suspense fallback={null}>
<ChatContainer />
</Suspense>

<Suspense fallback={null}>
<ModalContainer />
</Suspense>
</ClientLayout>
<Suspense fallback={null}>
<ModalContainer />
</Suspense>
</ReactQueryProvider>
</body>
</html>
Expand Down
9 changes: 3 additions & 6 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
dehydrate,
HydrationBoundary,
} from "@tanstack/react-query";
import { getPosts } from "@/entities/post/api/getPosts";
import { getPosts } from "@/entities/post/api/getPosts.server";
import HomePageClient from "@/widgets/main/ui/Client/HomePage.client";

export default async function HomePage({
Expand All @@ -12,6 +12,7 @@ export default async function HomePage({
searchParams: Promise<Record<string, string>>;
}) {
const params = await searchParams;

const initialCategory = params?.category ?? "전체";
const initialSort = params?.sort ?? "latest";
const initialKeyword = (params?.keyword ?? "").trim();
Expand All @@ -32,11 +33,7 @@ export default async function HomePage({

return (
<HydrationBoundary state={dehydrate(queryClient)}>
<HomePageClient
initialCategory={initialCategory}
initialSort={initialSort}
initialKeyword={initialKeyword}
/>
<HomePageClient />
</HydrationBoundary>
);
}
31 changes: 31 additions & 0 deletions src/entities/post/api/getPosts.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { serverFetch } from "@/shared/api/fetcher.server";
import { POST_PAGE_SIZE } from "@/entities/post/model/constants/api";
import type { Post } from "@/entities/post/model/types/post";

export async function getPosts({
category,
keyword,
sort,
page,
}: {
category: string;
keyword?: string;
sort?: string;
page?: number;
}) {
const query = new URLSearchParams({
page: String(page ?? 1),
size: String(POST_PAGE_SIZE),
});

if (category !== "전체") query.append("category", category);
if (keyword) query.append("keyword", keyword);
if (sort) query.append("sort", sort);

const { data } = await serverFetch<{ data: Post[] }>(
`/api/postings?${query.toString()}`,
{ method: "GET" },
);

return data;
}
18 changes: 13 additions & 5 deletions src/entities/post/ui/list/PostList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { useRouter, useSearchParams } from "next/navigation";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useInfiniteScroll } from "@/shared/lib/useInfiniteScroll";
import { getPosts } from "@/entities/post/api/getPosts";
Expand All @@ -11,16 +12,17 @@ import PostCard from "@/entities/post/ui/card/PostCard";
interface Props {
selectedCategory: string;
selectedSortOption: string;
onSortChange: (value: string) => void;
selectedKeyword?: string;
selectedKeyword: string;
}

export default function PostList({
selectedCategory,
selectedSortOption,
onSortChange,
selectedKeyword,
}: Props) {
const router = useRouter();
const searchParams = useSearchParams();

const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery({
queryKey: [
Expand All @@ -41,6 +43,12 @@ export default function PostList({
initialPageParam: 1,
});

const handleSortChange = (sortValue: string) => {
const params = new URLSearchParams(searchParams);
params.set("sort", sortValue);
router.push(`/?${params.toString()}`);
};

const posts = data?.pages.flat() ?? [];
const lastRef = useInfiniteScroll(
() => fetchNextPage(),
Expand All @@ -49,15 +57,15 @@ export default function PostList({
);

return (
<section className="flex flex-col gap-[30px] px-[20px]">
<section className="flex flex-col gap-[30px] px-5">
<div className="flex items-center justify-between">
<h3 className="text-[20px] font-semibold text-white">
{selectedCategory || "전체"}
</h3>
<SortMenu
items={SORT_OPTION_LIST}
selectedItem={selectedSortOption}
onSelect={onSortChange}
onSelect={handleSortChange}
/>
</div>

Expand Down
11 changes: 0 additions & 11 deletions src/shared/commands/SearchCommands.ts

This file was deleted.

8 changes: 0 additions & 8 deletions src/shared/lib/ClientLayout.tsx

This file was deleted.

18 changes: 0 additions & 18 deletions src/shared/lib/useResetKeyword.ts

This file was deleted.

40 changes: 0 additions & 40 deletions src/shared/lib/useSearchMediator.ts

This file was deleted.

19 changes: 0 additions & 19 deletions src/shared/model/search.store.ts

This file was deleted.

45 changes: 20 additions & 25 deletions src/widgets/header/ui/SearchForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@

import { useState, useEffect } from "react";
import cn from "@/shared/lib/cn";
import { useRouter, usePathname } from "next/navigation";
import { useSearchStore } from "@/shared/model/search.store";
import { SearchCommands } from "@/shared/commands/SearchCommands";
import { useRouter, useSearchParams } from "next/navigation";

interface SearchFormProps {
className?: string;
placeholder?: string;
autoFocus?: boolean;
showIcon?: boolean;
name?: string;
onSubmitted?: () => void;
}

const SearchForm = ({
className,
Expand All @@ -13,39 +20,27 @@ const SearchForm = ({
showIcon = true,
name = "keyword",
onSubmitted,
}: {
className?: string;
placeholder?: string;
autoFocus?: boolean;
showIcon?: boolean;
name?: string;
onSubmitted?: () => void;
}) => {
}: SearchFormProps) => {
const router = useRouter();
const pathname = usePathname();
const { category, keyword } = useSearchStore();
const [value, setValue] = useState("");
const searchParams = useSearchParams();

const [value, setValue] = useState(searchParams.get("keyword") ?? "");
const category = searchParams.get("category") ?? "전체";

useEffect(() => {
setValue("");
SearchCommands.changeKeyword("");
}, [category]);

const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const q = value.trim();
if (q === keyword) return;
if (!q) return;

if (pathname === "/") {
SearchCommands.changeKeyword(q);
} else {
const params = new URLSearchParams({
category,
keyword: q,
});
router.push(`/?${params.toString()}`);
}
const params = new URLSearchParams(searchParams);
if (category && category !== "전체") params.set("category", category);
params.set("keyword", q);

router.push(`/?${params.toString()}`);
onSubmitted?.();
};

Expand Down
22 changes: 6 additions & 16 deletions src/widgets/main/ui/Client/HomePage.client.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
"use client";

import { useSearchParams } from "next/navigation";
import SideMenuWrapper from "@/widgets/main/ui/SideMenu/SideMenuWrapper";
import PostList from "@/entities/post/ui/list/PostList";
import { useSearchStore } from "@/shared/model/search.store";
import { useSearchMediator } from "@/shared/lib/useSearchMediator";

interface Props {
initialCategory: string;
initialSort: string;
initialKeyword: string;
}

export default function HomePageClient({
initialCategory,
initialSort,
initialKeyword,
}: Props) {
useSearchMediator({ initialCategory, initialSort, initialKeyword });
export default function HomePageClient() {
const searchParams = useSearchParams();

const { category, keyword, sort, setSort } = useSearchStore();
const category = searchParams.get("category") ?? "전체";
const sort = searchParams.get("sort") ?? "latest";
const keyword = searchParams.get("keyword") ?? "";

return (
<div className="flex flex-col gap-[60px] md:block">
Expand All @@ -30,7 +21,6 @@ export default function HomePageClient({
<PostList
selectedCategory={category}
selectedSortOption={sort}
onSortChange={setSort}
selectedKeyword={keyword}
/>
</main>
Expand Down
Loading