[Next.js App Router] Streaming, 검색 및 페이지네이션 기능 구현

이희령·2023년 11월 4일
0

[Chapter 9] Streaming

streaming이란?

  • streaming은 경로를 작은 chunk로 분해한 후 서버에서 클라이언트로 점진적으로 데이터를 전송받는 기술을 의미한다.
  • streaming을 이용하면 느린 데이터 요청이 하나라도 존재할 경우 전체 페이지를 차단(block)하는 것을 방지할 수 있다.
  • 사용자는 모든 데이터가 로드된 후 UI가 나타날 때까지 기다릴 필요 없이 페이지의 일부분에서 상호작용 할 수 있다.
  • Next.js에서 streaming을 두 가지 방법으로 구현할 수 있다.
    1. 페이지 레벨에서 loading.tsx 파일을 생성한다.
    2. 특정 컴포넌트에 <Suspense>를 적용한다.

페이지 전체에 loading.tsx를 적용하기

// loading.tsx
export default function Loading() {
  return <div>Loading...</div>;
}
  • 페이지 폴더 내부에 생성한 loading.tsx 파일은 해당 페이지의 데이터가 로드되는 동안 로딩 UI를 대신 보여주도록 한다.
  • <Sidebar>는 정적 UI이기 때문에 즉시 보여진다. 사용자는 데이터가 로드되는 동안 <Sidebar>에서 상호작용 할 수 있다.

스켈레톤 UI 추가하기

  • 스켈레톤 UI(loading skeleton)는 사용자에게 데이터가 로딩 중이라는 의미를 전달한다.
  • loading.tsx에 적용된 모든 UI는 정적 파일로, 사용자에게 로딩 중에 먼저 보여지며 그동안 나머지 동적 컨텐츠가 서버에서 클라이언트로 스트리밍된다.

route groups 사용하기

  • dashboard 폴더에 (overview) 폴더를 생성하면 loading.tsx 파일은 /dashboard 페이지에만 적용되며 /dashboard/invoices 페이지나 /dashboard/customers 페이지에 영향을 미치지 않는다.
  • ()를 사용해서 새로운 폴더를 생성하면(라우트 그룹을 사용하면), 이는 URL 경로에 포함되지 않는다. 따라서 /dashboard/(overview)/page.tsx가 아닌 /dashboard 경로가 생성될 것이다.

Suspense 컴포넌트를 streaming 하기

<Suspense fallback={<RevenueChartSkeleton />}>
  <RevenueChart />
</Suspense>
  • Suspense로 컴포넌트를 감싸면 데이터가 로드되는 동안 전체 페이지를 차단하지 않으며, 해당 컴포넌트 부분에만 fallback UI를 대신 보여준다.
  • 전체 페이지에 streaming을 적용하는 것보다는 데이터 fetching을 컴포넌트 레벨로 내려서 Suspense를 적용하는 것을 추천한다.

[Chapter 11] Adding Search and Pagination

URL search params를 사용하는 이유

검색어 입력값을 관리하기 위해 클라이언트의 state가 아닌 URL search params를 사용하는 데는 몇 가지 장점이 있다.

  • 북마크 및 공유 가능한 URL: 입력값이 URL에 있기 때문에 사용자가 검색어나 필터가 포함된 URL을 참조하거나 공유하기 위해 북마크 할 수 있다.
  • 서버 사이드 렌더링과 초기 로딩: URL parameters를 서버에서 직접 사용하여 초기 상태를 렌더링할 수 있으므로 서버 렌더링을 보다 쉽게 처리할 수 있다.
  • 분석과 추적: URL에 검색어나 필터를 가지고 있음으로써 별도의 클라이언트 사이드 로직 없이 사용자의 행동을 보다 쉽게 추적할 수 있다.

검색 기능 구현

검색 기능을 구현하기 위해 Next.js의 세 가지 클라이언트 hooks를 사용한다.

  • useSearchParams: 현재 URL의 parameter에 접근할 수 있다. 예를 들어 /dashboard/invoices?page=1&query=pending라는 URL의 search params는 다음과 같다. {page: '1', query: 'pending'}
  • usePathname: 현재 URL의 pathname을 읽어온다, 예를 들면 /dashboard/invoices 페이지의 pathname은 /dashboard/invoices가 될 것이다.
  • useRouter: 다양한 메서드를 사용해서 클라이언트 컴포넌트 간에 페이지를 이동할 수 있다.

검색 구현 단계
1. 사용자가 input에 입력한 값을 받는다.
2. URL에 search params를 업데이트한다.
3. input 입력값과 URL의 동기화를 유지한다.
4. 입력값을 반영하여 테이블의 데이터를 업데이트한다.


import { useSearchParams, usePathname, useRouter } from 'next/navigation';

const searchParams = useSearchParams();
const pathname = usePathname();
const { replace } = useRouter();

const handleSearch = useDebouncedCallback((term) => {
    const params = new URLSearchParams(searchParams);
    params.set('page', '1');

    if (term) {
      params.set('query', term);
    } else {
      params.delete('query');
    }

    replace(`${pathname}?${params.toString()}`);
  }, 300);
  • new URLSearchParams로 인스턴스를 생성한다.
  • 검색어를 입력했을 때 ?query='검색어'와 같이 query string을 업데이트한다.
  • replace()를 이용해 query string이 반영된 URL로 업데이트한다. 이때 Next.js의 client-side navigation 덕분에 페이지는 다시 로드되지 않는다.
  • input에 한 글자를 입력할 때마다 서버에 요청을 보내는 것을 방지하기 위해 debounce를 적용해 검색 기능을 최적화했다.

<input
  onChange={(e) => {
    handleSearch(e.target.value);
  }}
  defaultValue={searchParams.get('query')?.toString()}
/>
  • input 입력값과 URL을 동기화하기 위해 defatulValuesearchParams의 값을 전달한다.

export default async function InvoicesTable({
  query,
  currentPage,
}: {
  query: string;
  currentPage: number;
}) {
  const invoices = await fetchFilteredInvoices(query, currentPage);
  // ...
}
  • 검색어를 입력하면 URL이 업데이트되고, 서버에 새로운 요청을 전송해서 검색어에 해당하는 데이터만을 불러온다.

페이지네이션 기능 구현

import { usePathname, useSearchParams } from 'next/navigation';

const pathname = usePathname();
const searchParams = useSearchParams();
const currentPage = Number(searchParams.get('page')) || 1;

const createPageURL = (pageNumber: number | string) => {
  const params = new URLSearchParams(searchParams);
  params.set('page', pageNumber.toString());
  return `${pathname}?${params.toString()}`;
};
  • createPageURL이 현재 search parameters의 인스턴스를 생성한다.
  • 현재 페이지에 따른 page 파라미터를 업데이트한다.
  • 현재 pathname과 업데이트된 search parameters를 이용해서 URL을 구성한다.

profile
Small Steps make a Big Difference.🚶🏻‍♀️

0개의 댓글