Published on

[Svelte] SvelteKit Pagination

[Svelte] SvelteKit Pagination

SvelteKit에서 페이지네이션을 구현하는 방법에 대해 알아보겠습니다.

페이지네이션을 구현하기 위해서는 querystring으로 페이지 정보를 받아와서 페이지에 해당하는 게시글을 조회하면 됩니다.

QueryString 값 조회

+page.ts 파일에서 파라미터로 url을 받을 수 있습니다.

해당 url에서 querystring을 추출할수 있습니다.

또한 페이징이 여러곳에서 필요하다면 현재 route 경로 (엄밀히 말하면 폴더 경로)를 주는 routeId를 이용 할 수 있습니다.

저는 그냥 사용하기 보단 클래스화 시켜 사용하였습니다.

  • PostPageDTO
class PostPageDTO {
  private readonly page: number
  private readonly size: number

  constructor(page: number) {
    this.page = page > 0 ? page : 1
    this.size = 10
  }

  public getPage(): number {
    return this.page
  }

  public getSize(): number {
    return this.size
  }
}

export default PostPageDTO
  • PostPaginationDTO
class PostPaginationDTO {
  private readonly currentPage: number
  private readonly lastPage: number
  private readonly routeId: string

  constructor(currentPage: number, lastPage: number, routeId: string) {
    this.currentPage = currentPage
    this.lastPage = lastPage
    this.routeId = routeId
  }

  public getCurrentPage(): number {
    return this.currentPage
  }

  public getPreviousPage(): string {
    return this.getPage(this.currentPage - 1)
  }

  public getNextPage(): string {
    return this.getPage(this.currentPage + 1)
  }

  private getPage(page: number): string {
    return `/${this.getRouteId()}?page=${page}`
  }

  public isFirstPage(): boolean {
    return this.getCurrentPage() === 1
  }

  public isLastPage(): boolean {
    return this.getCurrentPage() >= this.lastPage
  }

  public getLastPage(): number {
    return this.lastPage
  }

  public getRouteId(): string {
    return this.routeId
  }
}

export default PostPaginationDTO
  • +page.ts
export type PostsPage = {
  postItems: PostItemDTO[];
  postPagination: PostPaginationDTO;
};

// /blog?page=1
export const load: PageLoad<PostsPage> = async ({ url, routeId }) => {
  const postPage = new PostPageDTO(Number(url.searchParams.get('page')));

  const postItems = axios.get(...);
  const postPagination = new PostPaginationDTO(postPage.getPage(), 10, routeId ? routeId : 'blog');

  return {
    postItems,
    postPagination
  }
}

Pagination 만들기

  • +page.svelte

<script lang=ts>
  export let data;
  let postItems = data.postItems;
  let postPagination = data.postPagination;
</script>

<Posts {postItems} {postPagination}/>
  • Posts.svelte

<script lang="ts">
  import PostItem from '$lib/posts/component/list/PostItem.svelte';
  import PostPaginationDTO from '../../dto/postPaginationDTO';
  import PostItemDTO from '../../dto/postItemDTO';
  import PostPagination from '$lib/posts/component/list/PostPagination.svelte';

  export let postItems: PostItemDTO[];
  export let postPagination: PostPaginationDTO;
</script>

<section>
  {#each postItems as post}
    <PostItem {post}/>
  {/each}
  <PostPagination {postPagination}/>
</section>
  • PostPagination.svelte

<script lang="ts">
  import PostPaginationDTO from '../../dto/postPaginationDTO';

  export let postPagination: PostPaginationDTO;

  const handleLinkOnClick = (href: string) => {
    location.href = href;
  };
</script>

<div class="mt-12 flex justify-center">
  {#if postPagination.isFirstPage()}
    <button class="cursor-default text-gray-400">Previous</button>
  {:else}
    <button on:click={() => handleLinkOnClick(postPagination.getPreviousPage())}>Previous</button>
  {/if}

  <div class="mx-12 flex gap-2">
    <div>{postPagination.getCurrentPage()}</div>
    <div>of</div>
    <div>{postPagination.getLastPage()}</div>
  </div>

  {#if postPagination.isLastPage()}
    <button class="cursor-default text-gray-400">Next</button>
  {:else}
    <button on:click={() => handleLinkOnClick(postPagination.getNextPage())}>Next</button>
  {/if}
</div>

PostItem부분은 중요한게 아니니 생략했습니다.

여기서 보면 a 태그를 사용하지 않고 click 이벤트를 넣어 location.href를 직접 호출한 것을 볼 수 있습니다.

처음에는 a 태그를 사용하여 호출 하려 했지만, 어떤 이유인지 +page.ts가 동작하지 않았습니다. (아마 내부적으로 최적화를 하기 떄문에 발생하는 문제 같습니다.)

그래서 정말 페이지를 새로 렌더링 하도록 location.href를 사용하였고 잘 동작합니다.

그리고 PostPaginationDTO를 보면 routeId를 이용해서 경로를 만들기 때문에 여러곳에서 사용할 수 있습니다.

  • blog: blog?page=1
  • search: search?page=1

마치며

핵심 내용은 a 태그 대신 location.href를 직접 핸들링 해야 +page.ts가 다시 실행된다는 것입니다.

간단하게 클래스화 시켰는데 프론트는 대부분 이런식으로 사용하지 않는 것을 알고 있습니다.

하지만 프론트도 이렇게 사용해도 충분히 좋은것 같습니다.