티스토리 뷰

Backend/꾸준히 TIL

[NestJS+TypeORM] Pagination 처리

개발하는 후딘 2022. 11. 7. 11:13
728x90
반응형

1. 상황별로 받을 수 있게 하면 안될까?

(상황)

모든 게시물들을 불러올 수 있는 API는 [GET] /api/posts/ 이다. 

여기서 조건이 있다. 


조건1: 게시글이 중복으로 나타내지 않는다 
조건2: 한 페이지당 20개 단위로
조건3: 최신글이 먼저 나오도록

 

조건3은 order by 로 하면 될 것 같고

조건1, 조건2 를 페이지네이션으로 구현하기로 했다.

페이지네이션은 page와 pageSize로 구성되어있으며, 한 페이지당 보여줄 수 있는 게시물의 수를 제한시키는 것이다.

그리고 페이지별로 게시물의 수가 다르게 나타나야한다.

 

페이지네이션은 게시글 목록에서 페이징을 나타낼 수도 있지만, 스크롤을 내릴 때마다 글들이 계속 랜더링되는 것도 해당한다.

 

아래는 페이지네이션의 예시이다. 마우스를 스크롤 내릴때마다 jobs?xxx 라는 요청 url을 호출한다.

호출하는 것은 1페이지에서 2페이지로 넘어가듯이 2페이지에 필요한 데이터들을 요청하는 것이다.

영상에서는 limit=1, offset=40 으로 설정되어있다. 

스크롤 시 자동으로 페이지를 요청하며 응답데이터는 최대 40개를 불러온다는 것이다.

 

 

 

 

'상황별로 받는다는 것'은 두가지 케이스로 나뉜다.

(케이스1) 

  • URL /api/posts/ 로 호출 
  • 쿼리스트링이 없으므로 기본값을 미리 설정
    • page 기본값을 1 로함
    • pageSize 기본값을 20으로 함

 

(케이스2)

  • URL /api/posts?page=1&pageSize=10 와 같이 호출
  • 쿼리스트링이 존재함

 

- PaginationOption.ts

(문제코드)

쿼리스트링이 없을 때는 /api/posts/  기본값을 갖겠지만

쿼리스트링이 존재할 때 page와 pageSize가 기본값으로 덮어써지는 현상이 생겼다.

export class PaginationOption {
  // 페이지 (기본값: 1)
  page = 1;

  // 추가 로드 20개 단위
  pageSize = 20;
}

 

(해결)

export class PaginationOption {
  // 페이지 (기본값: 1)
  page: number;

  // 추가 로드 20개 단위
  pageSize: number;
}

 

 

- posts.controller.ts

(문제코드) 디버깅결과 page값과 pageSize값이 undefined로 되어있다.

 @Get()
  async findAll(
    @Query() option: PaginationOption
  ) {
    return await this.postsService.findAllPosts(option);
  }

 

(해결) page, pageSize 를 각각 쿼리스트링으로 받도록 했다.

그리고 쿼리스트링으로 받은 데이터는 문자열 타입이다.

여기서 parseIntPipe를 추가하고 싶었으나, 쿼리스트링이 존재하지 않은 검색일 때 값을 입력해야된다는 에러가 뜬다.

 @Get()
  async findAll(
    @Query('page') page?: number,
    @Query('pageSize') pageSize?: number,
  ) {
    return await this.postsService.findAllPosts({ page, pageSize });
  }

 

 

- (해결) posts.service.ts

쿼리스트링이 존재하지 않을 때는 options 값에 page, pageSize 값이 모두 NaN으로 떴다.

값이 존재하지 않으면 기본값을 할당하고, 값이 존재하면 해당 값을 넣는 방안으로 했다.

async findAllPosts(options: PaginationOption) {
    const { page, pageSize } = options;

    const _page = page ? page : 1;
    const _pageSize = pageSize ? pageSize : 20;


    const posts = await this.postsRepository
      .createQueryBuilder('posts')
      .innerJoin('posts.User', 'users')
      .orderBy('posts.createdAt', 'DESC')
      .take(_pageSize)
      .skip(_pageSize * (_page - 1))
      .select(['postId', 'title', 'users.userId AS userId', 'name'])
      .getRawMany();



    return { list: posts, page: _page, pageSize: posts.length };
  }

 


2. 페이지와 페이지사이즈를 변경했지만 전체 데이터가 나와버렸다.

 

  async findAllPosts(options: PaginationOption) {
    const { page, pageSize } = options;

    const _page = page ? page : 1;
    const _pageSize = pageSize ? pageSize : 20;


    const posts = await this.postsRepository
      .createQueryBuilder('posts')
      .innerJoin('posts.User', 'users')
      .orderBy('posts.createdAt', 'DESC')
      .take(_pageSize)
      .skip(_pageSize * (_page - 1))
      .select(['postId', 'title', 'users.userId AS userId', 'name'])
      .getRawMany();

    return { list: posts, page: _page, pageSize: posts.length };
  }

 

그러나 테스트를 page, pageSize 를 변경해서 테스트를 해봤지만

아래 사진과 같이 list 영역에서 모든 데이터가 나왔다. 

원래 10개만 나와야되는데, 모든 데이터가 나온거다.

page = 2,pageSize= 10 을 요청했는데 전체데이터가 나왔다.

 

(수정)

이유는 getRawMany() 때문이다. raw 데이터를 반환할 때는 limit 과 offset 을 사용하면 나타날 수 있다.

반대로 raw데이터가 아닌 경우에는 take, skip을 사용하면 된다.

 

take() 를 limit() 로, skip() 를 offset() 으로 변경했다.

  async findAllPosts(options: PaginationOption) {
    const { page, pageSize } = options;

    const _page = page ? page : 1;
    const _pageSize = pageSize ? pageSize : 20;

    const posts = await this.postsRepository
      .createQueryBuilder('posts')
      .innerJoin('posts.User', 'users')
      .orderBy('posts.createdAt', 'DESC')
      .limit(_pageSize)
      .offset(_pageSize * (_page - 1))
      .select(['postId', 'title', 'users.userId AS userId', 'name'])
      .getRawMany();


    return { list: posts, page: _page, pageSize: posts.length };
  }

 

 

 

[참고]

 

Nest.js에서 페이지네이션 직접 구현하기!!(TypeORM 사용기준)

페이지네이션 관련해서 API 나 라이브러리를 설치해서 구현했었지만 워낙 기본기라면 기본기? 라고 할수있기때문에 직접구현을 선택했다 순서는 Paginate 모듈 폴더 생성 barrel 타입 으로 각각 impor

ganzicoder.tistory.com

 

[Dimelo Project] take, skip 버그수정 (TypeOrm querybuilder)

페이지네이션을 구현하기 위해 take, skip을 사용했었다.https://typeorm.biunav.com/en/select-query-builder.html복잡한 쿼리 구문에는 limit과 offset이 잘 작동하지 않는다는 typeOrm문서를 보고 tak

velog.io

 

TypeORM select all rows but limit 25

CandidateEntity @Entity({ name: 'users' }) export class CandidateEntity { @PrimaryGeneratedColumn() public id: number; @OneToOne(() => CandidateEmployeeInfoEntity, employeeInfo =>

stackoverflow.com


Refactoring

리팩토링 하기전 목표

- post.service.ts 에서  NaN값이 들어올때와 아닐때 처리로직이 거슬린다.

- PaginationOption에서 기본값을 할당해주고, string에서 number로 변환했으면 좋겠다.

- 하나의 객체를 하나의 Query로 할 수 없을까?

[ Refactoring ]

1. posts.controller.ts

@Get()
  async findAll(@Query() option: PaginationOption) {
    return await this.postsService.findAllPosts(option);
  }

 

2. pagination-option.dto.ts

export class PaginationOption {
  // 페이지 (기본값: 1)
  @IsOptional() // 선택값으로 한다.
  @Type(() => Number) //값 존재시 문자열을 Number 로 변환
  @IsNumber()
  page?: number = 1;

  // 추가 로드 20개 단위
  @IsOptional() // 선택값으로 한다.
  @Type(() => Number) // 값 존재시 문자열을 Number로 변환
  @IsNumber()
  pageSize?: number = 20;
}

 

3. posts.service.ts

async findAllPosts(options: PaginationOption) {
    const { page, pageSize } = options;

    const posts = await this.postsRepository
      .createQueryBuilder('posts')
      .innerJoin('posts.User', 'users')
      .orderBy('posts.createdAt', 'DESC')
      .limit(pageSize)
      .offset(pageSize * (page - 1))
      .select(['postId', 'title', 'users.userId AS userId', 'name'])
      .getRawMany();

    return { list: posts, page: page, pageSize: posts.length };
  }

 

 

[참고]

 

How to pass default parameters to the @Query class in Nest.Js?

I'm trying to pass the default parameters maxnodes=3 and addstats=false to the controller via the @Query parameter in Nest.Js. The code works fine, but the default parameters are not used. When I p...

stackoverflow.com

728x90
반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함