2 분 소요

뭐든지 잘 만드는게 중요하니까 리서치해보는 Best Practice 시간.

시리즈

1.커서기반페이징이란?

2.Base64인코딩된 토큰을 사용한 API 통신

3.커서기반페이징 API Request/Response의 BestPractice를 찾아서


서론(+잡담)

뭐든 이론을 살피고 난 후 POC(개념실증)을 만들었다고 끝나는것이 아니라고 생각한다.

나만 이해갔다고 해서 출시를 해버리면

시간이 흐른 뒤, 다른 개발자들이 살펴볼때

‘얘는 왜 이렇게 했지..?’라는 의문을 가져버린다면..

참 여러모로 골치아파진다. (부끄럽기도하고..)

따라서 이론 구현의 모범사례나 관례가 있다면 따르는것이 좋다는 개인적인 의견에서 비롯한!

커서기반페이징 API에서 사용되는 요청/응답 데이터 형식의 모범사례 찾기 시간을 가져볼까 한다.

1.현황

일단 현황부터 정리해보자.

1.1.Request

  • cursor( “idx” + “,” + “time” 형식을 base64 인코딩 한)
  • limit

1.2.Response

  • data (cursor와 limit 범위에 해당하는 데이터리스트)
  • cursor( “idx” + “,” + “time” 형식을 base64 인코딩 한)

2.주요개념

  • 커서는 데이터리스트에서 특정 항목을 표시하는 임의의 문자열을 가르킨다
  • 커서는 항상 그 항목을 가르키지만, 항목이 삭제되면 무효화된다.
    • 따라서 커서가 계속 유효할것이라고 가정하지 말아야 한다.

3.커서 응답데이터 포멧

Facebook API를 벤치마크했습니다.

"paging": {
  "cursors": {
    "after": "MTAxNTExOTQ1MjAwNzI5NDE=",
    "before": "NDMyNzQyODI3OTQw"
  },
  "previous": "https://graph.facebook.com/{your-user-id}/albums?limit=25&before=NDMyNzQyODI3OTQw"
  "next": "https://graph.facebook.com/{your-user-id}/albums?limit=25&after=MTAxNTExOTQ1MjAwNzI5NDE="
}
  • before

    • 반환된 데이터 페이지의 시작을 가르키는 커서
  • after

    • 반환된 데이터 페이지의 끝을 가르키는 커서
  • limit

    • 반환될 수 있는 최대 개체 수
  • next

    • 데이터의 다음 페이지를 반환하는 엔드포인트
    • 이 데이터가 포함되어있지 않으면, 현재 페이지가 마지막 페이지임을 나타냄
  • previous

    • 데이터의 이전 페이지를 반환하는 엔드포인트
    • 이 데이터가 포함되어있지 않으면, 현재 페이지가 첫번째 페이지임을 나타냄

4.인사이트

  • Cursor을 반환하지만, 이것도 결국 Paging기법 중 하나이므로 Paging으로 감싼다.
  • Cursor에는 After과 Before을 추가하여, 데이터의 시작과 끝 정보를 제공한다.
    • 이것으로 현재페이지가 첫페이지인지, 마지막페이지인지의 정보를 제공할 수 있다.
  • Next와 Previous를 제공하여 클라이언트의 API사용 편의성을 높인다.

5.적용

5.1.Before

"cursor": "Mzg4MTQ0MywyMDIyLTAzLTE3IDEzOjA1OjQw"

5.2.After

배경설정

  • 총 8개의 데이터가 존재한다고 했을 때
    • [1,2,3,4,5,6,7,8]
  • 3개씩 호출한다고 했을 때

5.2.3.최초 조회 시, 페이징 결과 값 (이전데이터 없음)

"paging": {
            "cursor": {
                "after": "MTk2MTQ3NzAsMjAyMy0wOC0wNyAyMjo0MDowNw=="
            },
            "next": "endpoint/limit=3&after=MTk2MTQ3NzAsMjAyMy0wOC0wNyAyMjo0MDowNw=="
        }
  • [1,2,3]반환

5.2.4.다음 조회 시, 페이징 결과 값 (이전/이후 데이터 있음)

"paging": {
    "cursor": {
        "before": "MTk2MTQ3NzAsMjAyMy0wOC0wNyAyMjo0MDowNw==",
        "after": "MTk1ODcyMDEsMjAyMy0wNS0wMiAxMDozMToxMg=="
    },
    "previous": "endpoint/limit=3&before=MTk2MTQ3NzAsMjAyMy0wOC0wNyAyMjo0MDowNw==",
    "next": "endpoint/limit=3&after=MTk1ODcyMDEsMjAyMy0wNS0wMiAxMDozMToxMg=="
}
  • [4,5,6]반환

5.2.5.다음 조회 시, 페이징 결과 값 (이후 데이터 없음)

"paging": {
            "cursor": {
                "before": "MTk1ODcyMDEsMjAyMy0wNS0wMiAxMDozMToxMg=="
            },
            "previous": "https://api.imtest.me/v2/external-review-service/members?limit=3&before=MTk1ODcyMDEsMjAyMy0wNS0wMiAxMDozMToxMg=="
        }
  • [7,8]반환

5.3.문제점

5.3.1.after와 before을 제외하는 경우를 어떻게 판단 할 수 있을까?

5.3.1.1.after를 제외하는 경우

마지막 데이터를 의미한다.

limit보다 적은 데이터가 반환될 때 를 기준으로 할 경우

  • 3개씩 조회하는데 총 9개의 데이터가 있을 경우에는 limit보다 적은 데이터가 반환 될 수 없다

어떻게 처리할 수 있을까?

5.3.1.2.before를 제외하는 경우

첫번째 데이터를 의미한다.

after로 조회할 때 를 기준으로할 경우

  • after나 before 파라미터를 주지않는 최초조회의 경우 체크 불가능
  • before로 조회할 때 에도 이전 데이터가 있을 수 있다.

어떻게 처리할 수 있을까?

5.3.2. 해결

N+1개의 데이터를 조회하는 방식으로 해결할 수 있다.

사용자가 3개를 요청했을 때, 3개가 아니라 4개를 요청하는것이다.

상세히 정리해보자.

  • after

    • after로 조회한다면 반환값-before는 무조건 존재한다
    • N+1개의 데이터를 전부 가져올 수 있다면 반환값-after는 존재한다
      • 가져올 수 없다면 반환값-after는 존재하지 못한다.
  • before

    • before로 조회한다면 반환값-after는 무조건 존재한다
    • 커서없이 조회한다면(최초조회) 반환값-before는 존재하지못하여 반환값-after는 존재한다.
    • N+1개의 데이터를 전부 가져올 수 있다면 반환값-before는 존재한다.
      • 가져올 수 없다면 반환값-before는 존재하지 못한다.