페이지 매김 정보
REST API의 응답에 많은 결과가 포함되면 GitHub은(는) 결과를 페이지 매김하고 결과의 하위 집합을 반환합니다. 예를 들어 octocat/Spoon-Knife
리포지토리에 열려 있는 이슈가 1,600개가 넘는 경우에도 GET /repos/octocat/Spoon-Knife/issues
리포지토리에서 30개의 이슈만 반환합니다. 이렇게 하면 서버 및 사용자에 대한 응답을 더 쉽게 처리할 수 있습니다.
응답의 link
헤더를 사용하여 추가 데이터 페이지를 요청할 수 있습니다. 엔드포인트가 per_page
쿼리 매개 변수를 지원하는 경우 페이지에서 반환되는 결과 수를 제어할 수 있습니다.
이 문서에서는 페이지를 매긴 응답에 대한 추가 결과 페이지를 요청하는 방법, 각 페이지에서 반환되는 결과의 수를 변경하는 방법 및 여러 페이지의 결과를 가져오는 스크립트를 작성하는 방법을 보여 줍니다.
link
헤더 사용
응답의 페이지가 매겨지면 응답 헤더에 link
헤더가 포함됩니다. 엔드포인트가 페이지 매김을 지원하지 않거나 모든 결과가 단일 페이지에 맞는 경우 link
헤더는 생략됩니다.
link
헤더에는 결과의 추가 페이지를 가져오는 데 사용할 수 있는 URL이 포함되어 있습니다. 예를 들어 결과의 이전, 다음, 첫 번째 및 마지막 페이지가 있습니다.
특정 엔드포인트에 대한 응답 헤더를 보려면 curl, GitHub CLI 또는 요청을 만드는 데 사용하는 라이브러리를 사용하시면 됩니다. 라이브러리를 사용하여 요청을 만드는 경우 응답 헤더를 보려면 해당 라이브러리에 대한 설명서를 따르세요. curl 또는 GitHub CLI를 사용하는 경우 응답 헤더를 보려면 요청이 있는 --include
플래그를 전달하세요. 예시:
curl --include --request GET \
--url "https://api.github.com/repos/octocat/Spoon-Knife/issues" \
--header "Accept: application/vnd.github+json"
응답이 페이지를 매긴 경우 link
헤더는 다음과 같이 표시됩니다.
link: <https://api.github.com/repositories/1300192/issues?page=2>; rel="prev", <https://api.github.com/repositories/1300192/issues?page=4>; rel="next", <https://api.github.com/repositories/1300192/issues?page=515>; rel="last", <https://api.github.com/repositories/1300192/issues?page=1>; rel="first"
link
헤더는 결과의 이전, 다음, 첫 번째 및 마지막 페이지에 대한 URL을 제공합니다.
- 이전 페이지의 URL 뒤에
rel="prev"
이(가) 나옵니다. - 다음 페이지의 URL 뒤에
rel="next"
가 있습니다. - 마지막 페이지의 URL 뒤에
rel="last"
가 있습니다. - 첫 번째 페이지의 URL 뒤에는
rel="first"
가 있습니다.
경우에 따라 이러한 링크의 하위 집합만 사용할 수 있습니다. 예를 들어 결과의 첫 번째 페이지에 있는 경우 이전 페이지에 대한 링크가 포함되지 않으며, 링크를 계산할 수 없는 경우 마지막 페이지에 대한 링크는 포함되지 않습니다.
link
헤더의 URL을 사용하여 결과의 다른 페이지를 요청할 수 있습니다. 예를 들어 이전 예제를 기반으로 결과의 마지막 페이지를 요청하려면 다음을 수행하세요.
curl --include --request GET \
--url "https://api.github.com/repositories/1300192/issues?page=515" \
--header "Accept: application/vnd.github+json"
link
헤더의 URL은 쿼리 매개 변수를 사용하여 반환할 결과의 페이지를 나타냅니다. link
URL의 쿼리 매개 변수는 엔드포인트 간에 다를 수 있지만 페이지를 매긴 각 엔드포인트는 page
, before
/after
또는 since
쿼리 매개 변수를 사용합니다. (일부 엔드포인트는 페이지 매김 이외의 다른 항목에 since
매개 변수를 사용합니다.) 모든 경우에 link
헤더의 URL을 사용하여 결과의 추가 페이지를 가져올 수 있습니다. 쿼리 매개 변수에 대한 자세한 내용은 "REST API 시작"을 참조하세요.
페이지당 항목 수 변경
엔드포인트가 per_page
쿼리 매개 변수를 지원하는 경우 페이지에서 반환되는 결과 수를 제어할 수 있습니다. 쿼리 매개 변수에 대한 자세한 정보는 "REST API 시작"을(를) 참조하세요.
예를 들어 이 요청은 per_page
쿼리 매개 변수를 사용하여 페이지당 두 개의 항목을 반환합니다.
curl --include --request GET \
--url "https://api.github.com/repos/octocat/Spoon-Knife/issues?per_page=2" \
--header "Accept: application/vnd.github+json"
per_page
매개 변수는 link
헤더에 자동으로 포함됩니다. 예시:
link: <https://api.github.com/repositories/1300192/issues?per_page=2&page=2>; rel="next", <https://api.github.com/repositories/1300192/issues?per_page=2&page=7715>; rel="last"
페이지 매김을 사용한 스크립팅
link
헤더에서 URL을 수동으로 복사하는 대신 여러 페이지의 결과를 가져오는 스크립트를 작성할 수 있습니다.
다음 예제는 JavaScript 및 GitHub의 Octokit.js 라이브러리를 사용합니다. Octokit.js에 대한 자세한 정보는 "REST API 시작" 및 Octokit.js 추가 정보를 참조하세요.
Octokit.js 페이지 매김 메서드를 사용하는 예제
Octokit.js를 사용하여 페이지를 매긴 결과를 가져오려면 octokit.paginate()
를 사용할 수 있습니다. octokit.paginate()
는 마지막 페이지에 도달할 때까지 결과의 다음 페이지를 가져온 다음 모든 결과를 단일 배열로 반환합니다. 페이지가 매겨진 결과를 배열로 반환하는 대신, 몇 개의 엔드포인트는 페이지를 매긴 결과를 개체의 배열로 반환합니다. 원시 결과가 개체인 경우에도 octokit.paginate()
는 항상 항목 배열을 반환합니다.
예를 들어 이 스크립트는 octocat/Spoon-Knife
리포지토리에서 모든 이슈를 가져옵니다. 한 번에 100개의 이슈를 요청하지만 함수는 데이터의 마지막 페이지에 도달할 때까지 반환되지 않습니다.
import { Octokit } from "octokit"; const octokit = new Octokit({ baseUrl: "http(s)://HOSTNAME/api/v3", }); const data = await octokit.paginate("GET /repos/{owner}/{repo}/issues", { owner: "octocat", repo: "Spoon-Knife", per_page: 100, headers: { "X-GitHub-Api-Version": "2022-11-28", }, }); console.log(data)
import { Octokit } from "octokit";
const octokit = new Octokit({
baseUrl: "http(s)://HOSTNAME/api/v3",
});
const data = await octokit.paginate("GET /repos/{owner}/{repo}/issues", {
owner: "octocat",
repo: "Spoon-Knife",
per_page: 100,
headers: {
"X-GitHub-Api-Version": "2022-11-28",
},
});
console.log(data)
octokit.paginate()
에 선택적 맵 함수를 전달하여 마지막 페이지에 도달하기 전에 페이지 매김을 종료하거나 응답의 하위 집합만 유지하여 메모리 사용량을 줄일 수 있습니다. 모든 페이지를 요청하는 대신 한 번에 하나의 페이지를 반복하는 데 octokit.paginate.iterator()
를 사용할 수도 있습니다. 자세한 정보는 Octokit.js 설명서를 참조하세요.
페이지 매김 메서드를 만드는 예제
페이지 매김 메서드가 없는 다른 언어 또는 라이브러리를 사용하는 경우 사용자 고유의 페이지 매김 메서드를 빌드할 수 있습니다. 이 예제에서는 여전히 Octokit.js 라이브러리를 사용하여 요청을 수행하지만 octokit.paginate()
에 의존하지는 않습니다.
getPaginatedData
함수는 octokit.request()
를 사용하여 엔드포인트에 요청을 만듭니다. 응답의 데이터는 데이터가 반환되지 않는 경우 또는 반환되는 데이터가 배열 대신 개체인 경우를 처리하는 방식으로 parseData
에 의해 처리됩니다. 그러면 처리된 데이터가 지금까지 수집된 모든 페이지를 매긴 데이터가 포함된 목록에 추가됩니다. 응답에 link
헤더가 포함되고 link
헤더에 다음 페이지에 대한 링크가 포함된 경우, 함수는 RegEx 패턴(nextPattern
)을 사용하여 다음 페이지의 URL을 가져옵니다. 그런 다음 함수는 이 새 URL을 사용하여 이전 단계를 반복합니다. link
헤더에 다음 페이지에 대한 링크가 더 이상 포함되어 있지 않으면 모든 결과가 반환됩니다.
import { Octokit } from "octokit"; const octokit = new Octokit({ baseUrl: "http(s)://HOSTNAME/api/v3", }); async function getPaginatedData(url) { const nextPattern = /(?<=<)([\S]*)(?=>; rel="Next")/i; let pagesRemaining = true; let data = []; while (pagesRemaining) { const response = await octokit.request(`GET ${url}`, { per_page: 100, headers: { "X-GitHub-Api-Version": "2022-11-28", }, }); const parsedData = parseData(response.data) data = [...data, ...parsedData]; const linkHeader = response.headers.link; pagesRemaining = linkHeader && linkHeader.includes(`rel=\"next\"`); if (pagesRemaining) { url = linkHeader.match(nextPattern)[0]; } } return data; } function parseData(data) { // If the data is an array, return that if (Array.isArray(data)) { return data } // Some endpoints respond with 204 No Content instead of empty array // when there is no data. In that case, return an empty array. if (!data) { return [] } // Otherwise, the array of items that we want is in an object // Delete keys that don't include the array of items delete data.incomplete_results; delete data.repository_selection; delete data.total_count; // Pull out the array of items const namespaceKey = Object.keys(data)[0]; data = data[namespaceKey]; return data; } const data = await getPaginatedData("/repos/octocat/Spoon-Knife/issues"); console.log(data);
import { Octokit } from "octokit";
const octokit = new Octokit({
baseUrl: "http(s)://HOSTNAME/api/v3",
});
async function getPaginatedData(url) {
const nextPattern = /(?<=<)([\S]*)(?=>; rel="Next")/i;
let pagesRemaining = true;
let data = [];
while (pagesRemaining) {
const response = await octokit.request(`GET ${url}`, {
per_page: 100,
headers: {
"X-GitHub-Api-Version":
"2022-11-28",
},
});
const parsedData = parseData(response.data)
data = [...data, ...parsedData];
const linkHeader = response.headers.link;
pagesRemaining = linkHeader && linkHeader.includes(`rel=\"next\"`);
if (pagesRemaining) {
url = linkHeader.match(nextPattern)[0];
}
}
return data;
}
function parseData(data) {
// If the data is an array, return that
if (Array.isArray(data)) {
return data
}
// Some endpoints respond with 204 No Content instead of empty array
// when there is no data. In that case, return an empty array.
if (!data) {
return []
}
// Otherwise, the array of items that we want is in an object
// Delete keys that don't include the array of items
delete data.incomplete_results;
delete data.repository_selection;
delete data.total_count;
// Pull out the array of items
const namespaceKey = Object.keys(data)[0];
data = data[namespaceKey];
return data;
}
const data = await getPaginatedData("/repos/octocat/Spoon-Knife/issues");
console.log(data);