안녕하세요

카테고리(2) - 카테고리로 포스트 검색 기능 구현 방법 본문

Next13 블로그 프로젝트

카테고리(2) - 카테고리로 포스트 검색 기능 구현 방법

sakuraop 2023. 10. 1. 15:04

구현 순서

1. Link 태그를 이용하여 카테고리를 클릭하면 해당 url 로 이동하도록 합니다.
2. 라우터에서 searchParams를 전달받아 props로 전달합니다.
3. Props로 전달받은 데이터로 페이지에 카테고리 이름을 표시합니다.
4. 게시물 목록 api에 searchParams를 포함하여 GET 요청을 보냅니다.
5. DB에서 query로 검색한 게시물 목록을 반환합니다.
6. 게시물에 데이터를 전달합니다.


1. Link 태그를 이용하여 카테고리를 클릭하면 해당 url 로 이동하도록 합니다.

  return (
    <ul className={styles.subTitleBox}>
      {subCategories.map((sub) => {
        const { _id, title } = sub;
        return (
          <li key={_id} className={styles.subTitleItem}>
            <Link href={{ pathname: "category", query: { subtitle: title } }} passHref>
              <h5>- {title}</h5>
            </Link>
          </li>
        );
      })}
    </ul>
  );

pathname로 이동할 주소를 정해줍니다.

query로 api 요청에서 DB에서 검색에 이용할 쿼리를 전달합니다.

 

2. 라우터에서 searchParams를 전달받아 props로 전달합니다.

const CategoryRouter = (params: SearchParams) => {
  const { searchParams } = params;
  return (
    <div>
      <Category searchParams={searchParams} />
    </div>
  );
};

서버 컴포넌트에서 search params를 받아

리액트 클라이언트 컴포넌트(Category)에서 이용할 수 있도록 props로 전달합니다.

 

3. Props로 전달받은 데이터로 페이지에 카테고리 이름을 표시합니다.

const Category = async ({ searchParams }: SearchParams) => {
  const { subtitle } = searchParams; // 카테고리 쿼리

  return (
    <div className={styles.category}>
      <h2>{subtitle ? subtitle : "전체 게시물"} </h2>
      <Pagination />
    </div>
  );
};

 

4. 게시물 목록 api에 searchParams를 포함하여 GET 요청을 보냅니다.

const Category = async ({ searchParams }: SearchParams) => {
  const cardsData: Card[] | false = await getCardsData(searchParams); // 포스트 요청

  const { subtitle } = searchParams; // 카테고리 쿼리

  return (
    <div className={styles.category}>
      <h2>{subtitle ? subtitle : "전체 게시물"} </h2>
      <Pagination />
    </div>
  );
};

 

getCardsData() 함수는 아래와 같이 데이터를 요청하도록 하였습니다.

/**
 * 게시물 카드 리스트를 조회합니다.
 * @returns {Card[]} commentsData or false
 */
export const getCardsData = async ({ subtitle }: { subtitle: string }) => {
  const url = `${baseUrl}/category?subtitle=${subtitle}`;
  const options = setFetchOptions("GET");

  // 요청 결과 반환
  const res = await fetch(url, options);
  const data: Card[] = await res.json(); // 댓글 리스트

  return res.ok ? data : false;
};

 

5. DB에서 query로 검색한 게시물 목록을 검색하여 반환합니다.

// 게시물 정보를 불러오는 API입니다.
export const GET = async (req: NextRequest) => {
  const { searchParams } = new URL(req.url);
  const subtitle: string | null = searchParams.get("subtitle"); // 카테고리 부제목 이름

  const db = (await connectDB).db("blog");
  const postCollection = db.collection<Post>("posts");

  // searchParams.get()의 결과는 string을 반환하기 떄문에 "undefined"를 검사합니다.
  const query = subtitle === "undefined" ? {} : { subtitle };

  // card에 불필요한 데이터는 제외합니다.
  const cardsData: Card[] = await postCollection
    .find(query, { projection: { email: 0, content: 0, _id: 0 } })
    .toArray();

  if (cardsData) {
    return NextResponse.json(cardsData, { status: 200 });
  }

  return NextResponse.json({ message: "Internal Server Error" }, { status: 500 });
};

 

searchParams는 new URL 객체를 생성하여 .get() 메서드를 이용하여 가져올 수 있습니다.

  const { searchParams } = new URL(req.url);
  const subtitle: string | null = searchParams.get("subtitle"); // 카테고리 부제목 이름

 

URL.get() 메서드를 활용하여 가져온 결과는 string으로 치환됩니다.

  // searchParams.get()의 결과는 string을 반환하기 떄문에 "undefined"를 검사합니다.
  const query = subtitle === "undefined" ? {} : { subtitle };

 

query가 존재한다면 해당 쿼리를 이용한 데이터를 가져오고, 그렇지 않다면 모든 게시물 목록을 가져옵니다.({})

  // card에 불필요한 데이터는 제외합니다.
  const cardsData: Card[] = await postCollection
    .find(query, { projection: { email: 0, content: 0, _id: 0 } })
    .toArray();

 

6. 게시물에 데이터를 전달합니다.

  // 요청한 카드 데이터가 없는 경우
  if (!cardsData) {
    return (
      <div className={styles.category}>
        <div>게시물이 존재하지 않습니다.</div>
      </div>
    );
  }

  const { subtitle } = searchParams; // 카테고리 쿼리

  return (
    <div className={styles.category}>
      <h2>{subtitle ? subtitle : "전체 게시물"} </h2>
      <ul className={styles.cardContainer}>
        {cardsData.map((data: Card) => {
          return <CardItem key={data.id} data={data} />;
        })}
      </ul>
      <Pagination />
    </div>
  );