GraphQL Pagination
서로 다른 페이지 매김 모델은 서로 다른 클라이언트 기능을 가능하게 합니다.1)
GraphQL의 일반적인 사용 사례는 개체 집합 간의 관계를 탐색하는 것입니다. 이러한 관계를 GraphQL에 노출할 수 있는 여러 가지 방법이 있어 클라이언트 개발자에게 다양한 기능 세트를 제공합니다.2)
Plurals
객체 간의 연결을 노출하는 가장 간단한 방법은 복수형을 반환하는 필드를 사용하는 것입니다. 예를 들어, R2-D2의 친구 목록을 얻으려면 모든 친구를 요청할 수 있습니다.3)
{ hero { name friends { name } } }
{ "data": { "hero": { "name": "R2-D2", "friends": [ { "name": "Luke Skywalker" }, { "name": "Han Solo" }, { "name": "Leia Organa" } ] } } }
Slicing
그러나 클라이언트가 원할 수 있는 추가 동작이 있음을 빠르게 인식합니다. 클라이언트는 가져올 친구 수를 지정할 수 있기를 원할 수 있습니다. 아마도 그들은 처음 두 개만 원할 것입니다. 그래서 우리는 다음과 같은 것을 노출하고 싶습니다:4)
{ hero { name friends(first:2) { name } } }
그러나 처음 두 개를 가져왔다면 목록도 페이지 매김을 하고 싶을 것입니다. 클라이언트가 처음 두 친구를 가져오면 다음 두 친구를 요청하기 위해 두 번째 요청을 보낼 수 있습니다. 어떻게 그 행동을 가능하게 할 수 있습니까?5)
Pagination and Edges
페이지 매김을 할 수 있는 방법에는 여러 가지가 있습니다.6)
friends(first:2 offset:2)
와 같은 작업을 수행하여 목록에서 다음 두 개를 요청할 수 있습니다.7)- 우리는
friends(first:2 after:$friendId)
와 같은 것을 할 수 있습니다. 우리가 가져온 마지막 친구 다음에 다음 두 친구를 요청할 수 있습니다.8) friends(first:2 after:$friendCursor)
와 같은 작업을 수행할 수 있습니다. 여기서 마지막 항목에서 커서를 가져와 페이지 매김에 사용합니다.9)
일반적으로 커서 기반 페이지 매김이 설계된 것 중 가장 강력하다는 것을 발견했습니다. 특히 커서가 불투명한 경우 커서 기반 페이지 매김을 사용하여 오프셋 또는 ID 기반 페이지 매김(커서를 오프셋 또는 ID로 만들기)을 구현할 수 있으며 커서를 사용하면 향후 페이지 매김 모델이 변경될 경우 추가적인 유연성을 제공합니다. 커서가 불투명하고 형식에 의존해서는 안 된다는 점을 상기시키기 위해 base64 인코딩을 제안합니다.10)
그것은 우리를 문제로 이끕니다. 그렇지만; 객체에서 커서를 얻는 방법은 무엇입니까? 커서가 사용자 유형에 있는 것을 원하지 않습니다. 그것은 개체가 아니라 연결의 속성입니다. 그래서 우리는 간접 참조의 새로운 레이어를 도입하고 싶을 수도 있습니다. 친구 필드는 가장자리 목록을 제공해야 하며 가장자리에는 커서와 기본 노드가 모두 있어야 합니다.11)
{ hero { name friends(first:2) { edges { node { name } cursor } } } }
모서리의 개념은 객체 중 하나가 아닌 모서리에 특정한 정보가 있는 경우에도 유용합니다. 예를 들어, API에서 “우정 시간”을 노출하고 싶다면 엣지에 라이브를 두는 것이 자연스러운 위치입니다.12)
End-of-list, counts, and Connections
이제 커서를 사용하여 연결을 통해 페이지 매김을 할 수 있지만 연결 끝에 도달했을 때를 어떻게 알 수 있습니까? 우리는 빈 목록을 다시 얻을 때까지 계속 쿼리해야 하지만, 연결이 끝에 도달했을 때 알려주고 싶기 때문에 추가 요청이 필요하지 않습니다. 마찬가지로 연결 자체에 대한 추가 정보를 알고 싶다면 어떻게 해야 할까요? 예를 들어, R2-D2의 총 친구 수는 몇 명입니까?13)
이 두 가지 문제를 모두 해결하기 위해 친구 필드는 연결 개체를 반환할 수 있습니다. 그런 다음 연결 개체에는 가장자리에 대한 필드와 기타 정보(예: 총 개수 및 다음 페이지 존재 여부에 대한 정보)가 있습니다. 따라서 최종 쿼리는 다음과 같이 보일 수 있습니다.14)
{ hero { name friends(first:2) { totalCount edges { node { name } cursor } pageInfo { endCursor hasNextPage } } } }
이 PageInfo 개체에 endCursor 및 startCursor를 포함할 수도 있습니다. 이렇게 하면 가장자리에 포함된 추가 정보가 필요하지 않은 경우 pageInfo에서 페이지 매김에 필요한 커서를 얻었으므로 가장자리를 쿼리할 필요가 전혀 없습니다. 이는 연결에 대한 잠재적인 사용성 향상으로 이어집니다. 에지 목록만 노출하는 대신 간접 계층을 피하기 위해 노드의 전용 목록을 노출할 수도 있습니다.15)
Complete Connection Model
분명히 이것은 복수형을 갖는 원래 디자인보다 더 복잡합니다! 그러나 이 디자인을 채택하여 클라이언트를 위한 여러 기능을 잠금 해제했습니다.16)
- 목록을 페이지 매김하는 기능.17)
- totalCount 또는 pageInfo와 같은 연결 자체에 대한 정보를 요청할 수 있는 기능.18)
- cursor 또는 friendTime과 같은 에지 자체에 대한 정보를 요청할 수 있는 기능입니다.19)
- 사용자가 불투명한 커서만 사용하기 때문에 백엔드가 페이지 매김을 수행하는 방식을 변경할 수 있는 기능20)
이를 실제로 확인하기 위해 예제 스키마에 이러한 모든 개념을 노출하는 friendsConnection이라는 추가 필드가 있습니다. 예제 쿼리에서 확인할 수 있습니다. 페이지 매김이 어떻게 영향을 받는지 보려면 friendsConnection에 대한 after 매개변수를 제거해 보십시오. 또한 edge 필드를 연결의 helper friends 필드로 교체해 보십시오. 그러면 클라이언트에게 적절한 경우 간접 참조의 추가 에지 레이어 없이 친구 목록으로 직접 이동할 수 있습니다.21)
{ hero { name friendsConnection(first:2 after:"Y3Vyc29yMQ==") { totalCount edges { node { name } cursor } pageInfo { endCursor hasNextPage } } } }
{ "data": { "hero": { "name": "R2-D2", "friendsConnection": { "totalCount": 3, "edges": [ { "node": { "name": "Han Solo" }, "cursor": "Y3Vyc29yMg==" }, { "node": { "name": "Leia Organa" }, "cursor": "Y3Vyc29yMw==" } ], "pageInfo": { "endCursor": "Y3Vyc29yMw==", "hasNextPage": false } } } } }
Connection Specification
Continue Reading
관련 문서
friends(first:2 after:$friendId)
, to ask for the next two after the last friend we fetched.friends(first:2 after:$friendCursor)
, where we get a cursor from the last item and use that to paginate.