본문 바로가기
Back-end/Python

[GraphQL] 무작정 시작하기 (5) - Connection Field를 이용한 Pagination

by 허도치 2020. 5. 4.

2020/04/13 - [Back-end/Python] - [GraphQL] 무작정 시작하기 (1) - Schema & Query

2020/04/14 - [Back-end/Python] - [GraphQL] 무작정 시작하기 (2) - Mutation

2020/04/20 - [Back-end/Python] - [GraphQL] 무작정 시작하기 (3) - Object Field를 이용한 Pagination

2020/04/21 - [Back-end/Python] - [GraphQL] 무작정 시작하기 (4) - Relay와 Connection이란?

 

0. 서론

  지난 포스트에서 Relay와 Connection에 대해서 알아보았다. 이번 포스트에서는 간단하게 Connection Field를 이용하여 Pagination 패턴을 구현해보도록 하겠다. Object Field를 이용한 Pagination 편에서 다루었던 소스에서 query.py와 types.py 두개의 파일만 수정하면 되며 소스가 훨씬 간결해질 것이다.

 

 

 

1. 소스 작성
1-1. api/types.py
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
32
33
34
# api/types.py
import datetime
 
from graphene import relay
from graphene_mongo import MongoengineObjectType
from .models import RankModel
 
# MongoDB에서 데이터를 조회하는 객체 정의
class RankType(MongoengineObjectType):
  class Meta:
    model = RankModel
  
  # reg_dttm을 출력할 때, 처리하는 로직
  def resolve_reg_dttm(parent, info, **input):
    return datetime.datetime.strptime(parent.reg_dttm, "%Y%m%d%H%M%S").strftime("%Y-%m-%d %H:%M:%S")
 
  # upd_dttm을 출력할 때, 처리하는 로직
  def resolve_upd_dttm(parent, info, **input):
    if parent.upd_dttm is not None:
      return datetime.datetime.strptime(parent.upd_dttm, "%Y%m%d%H%M%S").strftime("%Y-%m-%d %H:%M:%S")
    else:
      return parent.upd_dttm
 
# Connection 객체 정의
class RankConnection(relay.Connection):
  class Meta:
    node = RankType
 
# Connection Object Type 객체 정의
class RankConnectionType(MongoengineObjectType):
  class Meta:
    model = RankModel
    interfaces = ( relay.Node, )
    connection_class = RankConnection
cs

1. [ 4 ln ] - Connection 객체를 정의하기 위한 Relay 모듈 가져오기.

 

2. [ 9~22 ln ] - 실제 데이터의 구조를 가지고 있는 ObjectType 객체 정의.

 

3. [ 25~27 ln ] - Connection 객체 정의

    : Meta 클래스에 node를 RankType으로 정의.

    * Graph에서 Node는 각 점들을 의미하는데, GraphQL에서는 하나의 ObjectType을 의미

    * Node가 연결된 집합체를 Edges라고 함.

 

4. [ 30~34 ln ] - Connection Object Type 객체 정의

 

 

1-2. api/query.py
1
2
3
4
5
6
7
8
9
# api/query.py
import graphene
from graphene_mongo import MongoengineConnectionField
from .types import RankConnectionType
 
# Query Field 객체 정의
class Query(graphene.ObjectType):
  # Connection Field 설정
  rank_edges = MongoengineConnectionField(RankConnectionType)
cs

1. [ 3 ln ] - Connection ObjectType을 ConnectionField로 변환하는 모듈 가져오기

   : MongoengineObjectType으로 만들었기 때문에, MongoengineConnectionField를 사용.
   : graphene.ObjectType으로 만들었다면, graphene.ConnectionField를 사용.

 

2. [ 7~9 ln ] - ConnectionField 설정

    : ObjectField와 다른 점은 Resolver를 작성하지 않아도 된다는 점.

 

 

 

2. 실행

[ Query 실행 결과 ]

 

2-1. Query
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
 query ConnectionPagenationQuery(
  $lastCnt: Int,
  $firstCnt: Int,
  $beforeCursor:String,
  $afterCursor: String
) {
  rankEdges(
    before: $beforeCursor
    after: $afterCursor
    last: $lastCnt,
    first: $firstCnt
  ) {
    pageInfo {
      hasNextPage
      hasPreviousPage
      startCursor
      endCursor
    }
    edges {
      cursor
      node {
        isMobile
        mode
        name
        score
        regDttm
        updDttm
      }
    }
  }
}
cs

1. [ 8~11 ln ] - 4개의 Arguments를 사용가능.

   : [ 8 ln ] - before는 입력된 Cursor 이전의 모든 Node를 조회.

   : [ 9 ln ] - after는 입력된 Cursor 이후의 모든 Node를 조회.

   : [ 10 ln ] - last는 조회된 결과에서 끝에서부터 N개를 출력.

   : [ 11 ln ] - first는 조회된 결과의 시작에서부터 N개를 출력.

 

2. [ 13~18 ln ] - ConnectionField의 페이지정보.

    : 기본으로 제공되는 데이터 Field.

    : [ 14 ln ] - hasNextPage는 다음 Page의 여부를 알 수 있음.

    : [ 15 ln ] - hasPreviousPage는 이전 Page의 여부를 알 수 있음.

    : [ 16 ln ] - startCursor는 조회된 결과의 첫번째 Cursor.

    : [ 17 ln ] - endCursor는 조회된 결과의 마지막 Cursor.

 

3. [ 19~29 ln ] - 조회 결과.

    : [ 20 ln ] - Cursor는 각 Node에 대한 주소값.

    : [ 21~28 ln ] - Node는 조회된 데이터.

 

 

 

3. Pagination

  ConnectionField를 이용하면 Pagination을 간편하게 구현할 수 있다. pageInfo Field에서 알 수 있는 hasNextPageendCursor, 그리고 Arguments로 줄 수 있는 afterfirst만 있으면 다음 페이지를 알 수 있다. 반대로 hasPreviosPage, start, befroe, last를 통해 이전 페이지를 구할 수 있다.

 

  구현방법은 간단하다.

 

- 다음페이지,

1. hasNextPage가 참인지 거짓인지 판단한다.

2. endCursor의 값을 afterCursor에 입력한다.

3. 보여줄 Node의 수를 first에 입력한다.

 

- 이전페이지,

1. hasPreviousPage가 참인지 거짓인지 판단한다.

2. startCursor의 값을 beforeCursor에 입력한다.

3. 보여줄 Node의 수를 last에 입력한다.

 

 

 

 

마치며

  지금까지 GraphQL을 이용하여 조회하고 입력, 수정, 삭제까지 모두 다루어보았다. 써보면서 느꼈는데 확실히 REST API보다 구현하는게 편하긴했다. 특히 React Hooks와 Apollo를 사용하면 코드가 많이 간결해져서 좋았다.

  그리고 아직 대용량 조회를 안해봐서 속도면에서는 어떨지 모르겠다. 앞으로 간단한 개인 프로젝트들을 만들면서 좀 더 연구를 해봐야겠다.

댓글