커서(Cursor)란 무엇인가
- 데이터베이스에서 커서는 일련의 데이터에 순차적으로 액세스할 때,
- 데이터의 현재 위치를 나타내는 요소
- 컴퓨터에서 마우스 커서가 화면에서 현재 사용자의 위치를 나타내는 것을 생각하면 이해하기 쉽다
- GraphQL에서는 커서를 데이터를 페이징하고 탐색하기 위한 특별한 값으로 사용하며,
- 주로 리스트나 연결된 데이터를 쿼리할 때 사용된다
- 이 값은 일반적으로 문자열이나 숫자로 표현되며, 현재 결과 집합에서 특정 항목의 위치를 나타낸다
커서(Cursor)의 활용
- 커서는 클라이언트가 쿼리를 통해 결과를 요청하고 탐색할 때 유용하다
- 예를 들어, 무한 스크롤 또는 페이지네이션과 같은 사용자 경험을 구현할 때 Cursor를 활용할 수 있다
- 클라이언트는 다음 페이지를 조회하기 위해,
- 현재 페이지의 마지막 항목의 Cursor를 기준으로 이후 데이터를 요청한다
- 클라이언트는 이전 페이지를 조회하기 위해,
- 현재 페이지의 첫 번쩨 항목의 Cursor를 기준으로 이전 데이터를 요청한다
커서(Cursor) 사용의 이점
- 페이지네이션은 흔히 오프셋 기반의 페이지네이션(offset-based-pagination) 기법을 사용한다
-
예를 들어 어떤 테이블의 데이터를 최신순으로 정렬하여 조회한다고 가정해 보면 쿼리는 아래 단계를 거친다
-
대상 테이블 값을 전부 스캔한다
- offset 값 이후의 데이터를 잘라낸다
-
잘라낸 데이터 중 맨 앞에서부터, 한 페이지 당 표시 할 게시물 갯수만큼 잘라서 리턴한다
-
1번 과정으로 대용량 데이터 처리 시에 매우 느린 퍼포먼스가 나오게 되지만,
- 이는 적절한 인덱스 사용으로 해결 할 수 있다
- 다만 다음 페이지를 질의하기 전 새로운 데이터가 추가 된다면?
- 이전 페이지에서 조회한 데이터가 중복으로 노출된다
- 이는 무한 스크롤링 방식의 데이터 조회 화면에서는 부적절하다
GraphQL에서 커서(Cursor)의 활용 예시
- GraphQL 쿼리에서 커서 필드를 사용하여 페이징된 결과 집합을 요청할 수 있다
- 서버는 해당 Cursor 값을 포함한 결과를 반환하고, 클라이언트는 이를 사용하여 추가적인 데이터를 요청한다
- 예를 들어, 사번(emp_no)이 10000 부터 시작하는 데이터를 가진 Employee 테이블의 데이터를 질의해보자
- 클라이언트에서 요청 시 보내는 GraphQL 쿼리는 다음과 같다
query Employees {
employees(first: 3) {
edges {
node {
empNo
firstName
lastName
}
}
pageInfo {
hasNextPage # 다음 페이지의 존재 여부
hasPreviousPage # 이전 페이지의 존재 여부
startCursor # 조회된 결과의 첫 번째 커서
endCursor # 조회된 결과의 마지막 커서
}
}
}
- 서버로부터 응답 받은 결과는 아래와 같다
- startCursor 의 값은 조회한 첫 번째 노드의 커서의 값으로, 사번이 10001 인 노드
- endCursor 의 값은 조회한 마지막 노드의 커서의 값으로, 사번이 10003 인 노드
{
"data": {
"employees": {
"totalCount": 300024,
"edges": [
{
"node": {
"empNo": "10001",
"firstName": "Georgi",
"lastName": "Facello"
}
},
{
"node": {
"empNo": "10002",
"firstName": "Bezalel",
"lastName": "Simmel"
}
},
{
"node": {
"empNo": "10003",
"firstName": "Parto",
"lastName": "Bamford"
}
}
],
"pageInfo": {
"hasNextPage": true,
"hasPreviousPage": false,
"startCursor": "YXJyYXljb25uZWN0aW9uOjA=",
"endCursor": "YXJyYXljb25uZWN0aW9uOjI="
}
}
}
}
- 위 결과를 바탕으로 다음 페이지를 질의한다고 할 때, 쿼리는 다음과 같이 작성 할 수 있다
- after의 값으로 endCursor 의 값을 사용한다
- endCursor의 값은 사번이 10003 인 노드를 가리키므로, 그 이후 노드들을 조회한다
query Employees {
employees(first: 3, after: "YXJyYXljb25uZWN0aW9uOjI=") {
totalCount
edges {
node {
empNo
firstName
lastName
}
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
}
}