문서로 건너뛰기
Ruby

페이지네이션

페이지로 나뉘는 모든 목록에서, 한 페이지, 모든 페이지, 또는 한 번에 한 항목씩.

list, list_all, iterate

페이지로 나뉘는 모든 목록에는 세 가지 메서드가 있습니다. list는 한 페이지를 가져와 OpenEmail::Page를 반환합니다. list_all은 커서를 따라 모든 페이지를 돌고 하나의 Array를 반환합니다. iterate는 같은 페이지를 한 항목씩 순회합니다: 각 항목을 블록에 yield하거나, 블록을 주지 않으면 Enumerator를 반환합니다. 세 메서드 모두 목록의 필터, limit:, cursor:, api_key:를 받습니다.

three_ways.rb
page = client.emails.list(status: "failed", limit: 50)page.items.each { |email| puts "#{email[:id]} #{email[:lastError]}" } failures = client.emails.list_all(status: "failed") client.emails.iterate(status: "failed") do |email|  puts email[:id]end puts failures.size, page.has_more?

네임스페이스에 목록이 둘 이상 있으면, 순회하는 목록의 이름을 붙인 같은 세 이름이 반복됩니다: emails의 list_events, list_all_events, iterate_events, webhooks의 list_deliveries, list_all_deliveries, iterate_deliveries 등입니다.

OpenEmail::Page

itemsArray<Hash>
이 페이지의 행들로, API의 `data` 봉투에서 꺼낸 것이며 각각 Symbol 키를 가진 Hash입니다. 페이지에 아무것도 없으면 비어 있습니다.
has_more?Boolean
다음 페이지가 있는지 여부입니다. 물음표 없는 `has_more`도 같은 값을 읽습니다. API가 `hasMore`를 보내지 않으면, `next_cursor`가 있을 때 정확히 true가 됩니다.
next_cursorString or nil
다음 페이지를 위해 `cursor:`로 다시 전달할 값이며, 마지막 페이지에서는 nil입니다.

페이지는 Ruby의 Data 객체이므로 동결되어 있고, 값으로 비교되며, to_h로 Hash로 바뀝니다.

블록 또는 Enumerator

블록을 주면 iterate는 그 자리에서 모든 페이지를 순회하며 각 항목을 블록에 yield합니다. 블록이 없으면 Enumerator를 반환하고, 소비하기 전까지는 아무것도 가져오지 않습니다. 어느 쪽이든 현재 페이지의 항목을 모두 yield한 뒤에야 다음 페이지를 요청하므로, 일찍 멈추는 것은 요청도 멈춥니다: first(10)은 열 항목에 필요한 만큼의 페이지만 읽고, find는 일치하는 항목에서 멈추며, 블록 안의 break는 순회를 끝냅니다.

enumerator.rb
latest = client.emails.iterate(status: "failed", limit: 100).first(10) invoice = client.emails.iterate(status: "failed").find do |email|  email.dig(:tags, :invoice) == "inv_2026_09_4192"end from_api = client.emails.iterate(status: "bounced").lazy.select { |email| email[:source] == "api" }.first(5) p latest.size, invoice&.fetch(:id), from_api.map { |email| email[:id] }

Enumerator에 바로 호출한 select, map, count처럼 모든 항목이 필요한 Enumerable 메서드는, list_all처럼 반환하기 전에 모든 페이지를 읽습니다. 앞에 lazy를 붙이면 메서드를 연결하면서도 일찍 멈출 수 있습니다.

Enumerator는 소비될 때마다 순회를 처음부터 다시 시작하므로, 같은 Enumerator에 first(10)을 두 번 호출하면 첫 페이지를 두 번 가져옵니다. 다시 필요하다면 Enumerator가 아니라 결과를 보관하세요.

커서에서 이어 가기

커서는 불투명한 값입니다. 마지막으로 읽은 페이지의 next_cursor를 보관했다가 cursor:로 다시 전달하면, 나중의 요청이나 다른 프로세스에서도 거기서부터 이어 갈 수 있습니다. list_all과 iterate도 cursor:를 받으며, 그 뒤부터 순회를 시작합니다.

resume.rb
first_page = client.emails.list(status: "failed", limit: 25)saved = first_page.next_cursor if saved  rest = client.emails.list_all(status: "failed", cursor: saved)  puts rest.sizeend

커서는 그것을 반환한 목록과 필터에 속하므로, 같은 필터와 함께 보내세요. 목록이 위치를 찾을 수 없는 커서는 invalid_cursor로 거부되며, 그때는 커서 없이 처음부터 다시 시작하면 됩니다.

limit:

limit:은 합계가 아니라 각 페이지의 크기입니다. list에서는 돌아오는 행의 수입니다. list_all과 iterate에서는 각 요청이 요구하는 수이므로, 값이 클수록 같은 행을 더 적은 왕복으로 가져옵니다. 목록마다 고유한 범위와 기본값이 있으며, 대개 1~100이고 보내지 않으면 25입니다. 범위를 벗어난 값은 잘라 맞추지 않고 거부됩니다. 각 목록의 페이지에 그 범위가 적혀 있습니다.

순회가 멈추는 때

  • 페이지의 has_more?가 false일 때.
  • 페이지에 next_cursor가 없을 때. 더 있다고 하면서 커서를 알려 주지 않는 페이지는 영원히 반복될 것이기 때문입니다.
  • API가 방금 받은 커서를 그대로 돌려줄 때. 같은 이유입니다.

각 페이지는 GET이므로, 예외가 발생하기 전에 다른 읽기처럼 개별적으로 재시도됩니다. 재시도 후에도 남은 실패는 list_all에서 예외로 발생하며, 이미 가져온 항목은 버려집니다. iterate에서는 그때 이미 앞 페이지의 항목이 yield되었으므로, 블록이 하는 일을 두 번 실행해도 안전하게 만들거나, list로 페이지를 가져오며 각 next_cursor를 보관해 두 번째 시도가 첫 시도가 멈춘 곳에서 시작할 수 있게 하세요.

스레드와 초안

threads.list와 drafts.list, 그리고 각각의 list_all과 iterate는 커서가 아니라 API의 pageToken과 nextPageToken으로 페이지를 나눕니다. gem은 이 차이를 숨깁니다: 토큰을 cursor:로 전달하고 next_cursor에서 읽으세요.

page_token.rb
page = client.threads.list(folder: "inbox", limit: 50)later = client.threads.list(folder: "inbox", limit: 50, cursor: page.next_cursor) if page.has_more? p page.items.size, later&.items&.size

서버는 페이지가 가득 찬 채 돌아올 때마다 토큰을 주므로, 결과적으로 마지막이었던 페이지에서도 has_more?가 true일 수 있으며, 그 경우 다음 호출은 항목을 반환하지 않습니다.

더 많은 정보를 담은 페이지

몇몇 목록은 행 외의 정보도 함께 응답하며, OpenEmail::Page 대신 자체 Data 객체를 반환합니다.

메서드반환 내용추가되는 것
addresses.listOpenEmail::AddressBookPageitems 대신 addresses, 그리고 unrestricted와 domains, has_more?와 next_cursor.
addresses.list_allOpenEmail::AddressBookaddresses에 모든 주소가 담기고, unrestricted와 domains는 마지막 페이지가 보고한 값입니다. Array가 아니라 주소록 전체를 반환하는 유일한 list_all입니다. addresses.iterate는 주소만 yield합니다.
contacts.list_peopleOpenEmail::PeoplePageseen. 키가 메일에서 본 주소를 읽을 수 없으면 false입니다. list_all_people과 iterate_people은 사람만 반환합니다.
temp_mail.list_messagesOpenEmail::TempMessagesPageexpires_at. 받은편지함이 만료되는 시각입니다. list_all_messages와 iterate_messages는 메시지만 반환합니다.
templates.list_sendsOpenEmail::TemplateSends커서가 아니라 번호로 페이지를 나눕니다: items, total, page, page_size. 다음 페이지는 page:로 요청하세요.
emails.send_batchOpenEmail::BatchResult페이지가 아닙니다: 보낸 메시지마다 하나씩인 items와, sent 및 failed 건수입니다.

API의 Hash를 반환하는 목록

일부 목록은 오프셋, 페이지 번호, 또는 자체 숫자 커서로 페이지를 나누며, OpenEmail::Page가 아니라 받은 그대로의 파싱된 본문, 즉 data를 가진 Hash를 반환합니다. list_all이나 iterate가 없으므로 페이지 처리는 직접 하세요.

메서드페이지 방식반환되는 것
exports.listlimit:와 offset:data, total, hasMore.
imports.list_failuresafter:와 limit:data와 nextCursor. 이는 after:로 다시 전달할 Integer이며, 마지막 페이지에서는 nil입니다.
subscriptions.list와 subscriptions.list_domainslimit:와 offset:data, total, counts, hasMore.
billing.list_invoicespage:와 limit:data, total, page, limit, hasMore, metered.
offset_paging.rb
offset = 0 loop do  batch = client.subscriptions.list(status: "active", limit: 50, offset:)  batch[:data].each { |row| puts "#{row[:senderEmail]} #{row[:total]}" }   break unless batch[:hasMore]   offset += batch[:data].sizeend

languages.list, labels.list_colors, roles.list_permissions처럼 페이지로 나뉘지 않는 목록은 곧바로 Array를 반환합니다.