Next.js 하나로 Full Stack 작업2 (기존 GET API에서 페이지네이션 및 검색 기능 추가 / MongoDB)

Eunhye Kim·2024년 8월 5일
2
post-thumbnail

본 포스팅은 API가 주제입니다. UI와 관련된 코드와 내용은 없어요.

이전 포스팅에서는 단순히 DB에서 데이터를 가져오는 기능만 있었습니다.

이번에는 여기에 추가적으로 페이지네이션과 검색이 가능하도록 기능을 추가해볼게요.


시작 하기 앞서
일단 페이지네이션을 하려면 파라미터에 page값과 limit값이 필요합니다.
여기에서 page는 현재 페이지 번호고 limit은 한 페이지에 표시할 항목의 수 입니다.

그리고 검색을 할 시 파라미터에 search값이 필요해요.
여기에서 search는 사용자가 검색한 단어를 얘기합니다.

1. 통신 URL수정

기존에는 API 통신 URL이 단순히 /api/users였지만, 페이지네이션과 검색 기능을 추가하려면 파라미터 값을 추가 해야 합니다.

그래서 /api/users에서 파라미터를 추가한 URL로 변경했어요.

기존 코드

export const USER_DATA_URL = "/api/users";

변경된 코드

export const USER_DATA_URL = "/api/users";

export const getPaginationUserDate = (
  page: number,
  limit: number,
  searchWord?: string
) => {
  const baseUrl = `${USER_DATA_URL}?page=${page}&limit=${limit}`;
  // 쿼리에`searchWord`가 있을 때만 `searchWord` 쿼리 파라미터를 추가
  return searchWord ? `${baseUrl}&search=${searchWord}` : baseUrl;
};

저는 페이지네이션과 검색 URL을 한번에 같이 쓰고 싶어서 이렇게 작성했어요.

저기에 보이는 baseUrl에서는 pagelimit만 있고 만약에 검색을 한 경우에는 baseUrl에 search=${searchWord}를 추가하도록 했어요.

이렇게하면 하나의 URL로 페이지네이션, 검색 두 개다 가능해요.

2. GET API 수정

기존 GET API 코드

export const GET = async (req: NextRequest) => {
  try {
    await connect();
    const users = await UserSchema.find();
    return new NextResponse(JSON.stringify(users), { status: 200 });
  } catch (err) {
    console.log(err);
    return new NextResponse("Database Error", { status: 500 });
  }
};

페이지네이션 & 검색 기능 추가된 GET API

export const GET = async (req: NextRequest) => {
  try {
    await connect();

    // 1. 파라피터값 가져오기
    const { searchParams } = new URL(req.url);
    const page = Number(searchParams.get("page"));
    const limit = Number(searchParams.get("limit"));
    const searchQuery = searchParams.get("search");

    // 2. 검색
    const query = searchQuery
      ? {
          $or: [
            { name: { $regex: searchQuery, $options: "i" } },
            { phone: { $regex: searchQuery, $options: "i" } },
          ],
        }
      : {};

    // 3. 페이지네이션
    const skip = (page - 1) * limit;
    const users = await User.find(query)
      .skip(skip)
      .limit(limit)
      .sort({ createdAt: -1 });

    // 전체 사용자 수
    const totalUsers = await User.countDocuments(query);

    // 페이지네이션 정보와 함께 응답
    const response = {
      users,
      page,
      limit,
      totalUsers,
      totalPages: Math.ceil(totalUsers / limit),
    };

    return new NextResponse(JSON.stringify(response), { status: 200 });
  } catch (err) {
    console.log(err);
    return new NextResponse("Database Error", { status: 500 });
  }
};

1. 파라피터값 가져오기

URL에 보낸 값을 이제 API에서 가져옵니다.

2. 검색

MongoDB에서 검색을 하는 방법입니다.

검색을 하지 않으면 {}을 반환하고 검색한 값(searchQuery)이 있으면 query객체를 생성해요.

searchQuery가 존재할 때 생성되는 query 객체는 다음과 같아요.

{
  $or: [
    { name: { $regex: searchQuery, $options: "i" } },
    { phone: { $regex: searchQuery, $options: "i" } },
  ]
}

여기서 중요한 부분은 $or 연산자와 $regex 연산자입니다.

$or: MongoDB의 논리 연산자 중 하나로, 배열 안의 조건들 중 하나라도 만족하는 문서를 찾습니다. 즉, 배열에 있는 여러 조건들 중 하나라도 참이면 그 문서를 검색 결과에 포함시킵니다.
$regex: MongoDB의 정규 표현식 연산자입니다. 이는 텍스트 검색을 수행하는데 사용돼요.
{ $regex: searchQuery, $options: "i" }: searchQuery를 포함하는 문자열을 찾아요.
$options: "i"는 대소문자를 구분하지 않는다는 의미입니다.
따라서, query 객체는 name 필드나 phone 필드에 searchQuery를 포함하는 문서를 검색합니다. 그리고 대소문자 구분 없이 검색해요.

3. 페이지네이션

1. 페이지네이션 계산

const skip = (page - 1) * limit;

page는 현재 페이지 번호를 나타내며, limit은 페이지당 사용자 수를 나타냅니다.
skip 변수는 MongoDB 쿼리에서 데이터를 건너뛸 개수를 계산해요.
예를 들어, 페이지가 2이고 limit이 10이면, skip은 10이 되어 첫 번째 페이지의 10개 항목을 건너뛰고 두 번째 페이지의 항목을 가져옵니다.

2. 사용자 데이터 조회


const users = await User.find(query)
  .skip(skip)
  .limit(limit)
  .sort({ createdAt: -1 });

User.find(query): query에 기반한 조건으로 MongoDB에서 사용자 데이터를 찾아요.
.skip(skip): skip만큼의 문서를 건너뛰어 페이지에 해당하는 데이터를 가져와요.
.limit(limit): 페이지당 가져올 문서의 개수를 제한해요.
.sort({ createdAt: -1 }): createdAt 필드를 기준으로 내림차순 정렬하여 최신 사용자부터 반환해요. 저는 최근 데이터가 페이지네이션 제일 첫 번째로 나오게끔 하고 싶어서 sort를 사용했습니다.

3. 전체 사용자 수 조회

const totalUsers = await User.countDocuments(query);

query에 기반하여 전체 문서의 개수를 셉니다.

4. 응답 데이터 생성

const response = {
  users,
  page,
  limit,
  totalUsers,
  totalPages: Math.ceil(totalUsers / limit),
};

users: 현재 페이지에 해당하는 사용자 데이터.
page: 현재 페이지 번호.
limit: 페이지당 사용자 수.
totalUsers: 전체 사용자 수.
totalPages: 총 페이지 수를 계산.

이제 클라이언트에게 보내는 응답 데이터가 완료 되었어요.
마지막으로 NextResponse으로 클라이언트로 데이터를 보내주면 끝입니다.

return new NextResponse(JSON.stringify(response), { status: 200 });

마무리

이렇게 해서 GET API에서 페이지네이션 및 검색 기능 추가가 완료 되었어요.
이제 이 값을 사용해서 원하는 UI로 완성시키면 돼요!

다른 글 보기
1. Next.js 하나로 Full Stack 작업1 (get, post API 제작 / MongoDB)
2. Python으로 만들어보는 GET, POST API (Flask, MongoDB)

profile
개발에 몰두하며 성장하는 도중에 얻은 인사이트에 희열을 느낍니다.

0개의 댓글