문제 상황
Python의 boto3 라이브러리를 사용하여 본인 계정에서 생성한 AWS Glue Data Catalog의 S3 버킷에 저장된 객체 버전(object versions)을 페이지네이션 방식으로 조회하는 방법을 알아보겠습니다.
문제 해결 접근 방식
1단계: 예외 처리를 위해 boto3와 botocore의 예외 클래스를 임포트합니다.
2단계: 함수의 파라미터를 이해합니다. bucket_name은 필수 파라미터이며, max_items, page_size, starting_token은 선택적 파라미터입니다.
max_items: 반환할 총 레코드 수를 의미합니다. 사용 가능한 레코드 수가 max_items보다 많으면 응답에 NextToken이 포함되어 이후 페이지네이션을 이어갈 수 있습니다.
page_size: 한 페이지당 크기를 나타냅니다.
starting_token: 페이지네이션을 시작할 위치를 지정하며, 이전 응답의 NextKeyMarker 값을 사용합니다.
3단계: boto3를 사용해 AWS 세션을 생성합니다. 기본 프로필에 region_name이 설정되어 있지 않다면 세션 생성 시 명시적으로 리전 이름을 전달해야 합니다.
4단계: S3용 AWS 클라이언트를 생성합니다.
5단계: list_object_versions 연산을 기반으로 S3 버킷의 객체 버전 정보를 담는 페이지네이터(paginator) 객체를 생성합니다.
6단계: paginate 함수를 호출하고, max_items, page_size, starting_token은 PaginationConfig 파라미터로, bucket_name은 Bucket 파라미터로 각각 전달합니다.
7단계: max_size와 page_size 설정에 따라 지정된 개수만큼의 레코드가 반환됩니다.
8단계: 페이지네이션 과정에서 오류가 발생하면 일반 예외(generic exception)를 처리합니다.
예제 코드
다음 코드를 사용하면 본인 계정에 생성된 S3 버킷의 객체 버전을 페이지네이션 방식으로 조회할 수 있습니다.
import boto3
from botocore.exceptions import ClientError
def paginate_through_object_version_s3_bucket(bucket_name, max_items=None, page_size=None, starting_token=None):
session = boto3.session.Session()
s3_client = session.client('s3')
try:
paginator = s3_client.get_paginator('list_object_versions')
response = paginator.paginate(
Bucket=bucket_name,
PaginationConfig={
'MaxItems': max_items,
'PageSize': page_size,
'StartingToken': starting_token
}
)
return response
except ClientError as e:
raise Exception("boto3 client error in paginate_through_object_version_s3_bucket: " + e.__str__())
except Exception as e:
raise Exception("Unexpected error in paginate_through_object_version_s3_bucket: " + e.__str__())
# 첫 번째 실행
a = paginate_through_object_version_s3_bucket('s3-test-bucket', 2, 5)
print(*a)
# 두 번째 실행
for items in a:
next_token = items['NextKeyMarker']
b = paginate_through_object_version_s3_bucket('s3-test-bucket', 2, 5, next_token)
print(*b)실행 결과
첫 번째 실행에서는 MaxItems=2, PageSize=5 조건으로 객체 버전 목록과 함께 다음 페이지를 가리키는 NextKeyMarker가 반환됩니다.
{'ResponseMetadata': {'RequestId': 'XEVY**************CD0', 'HostId': **************, 'HTTPStatusCode': 200, 'HTTPHeaders': {'x-amz-id-2': *******************, 'x-amz-request-id': 'XEVYV***************', 'date': 'Sat, 03 Apr 2021 07:14:49 GMT', 'content-type': 'application/xml', 'transfer-encoding': 'chunked', 'server': 'AmazonS3'}, 'RetryAttempts': 0}, 'IsTruncated': True, 'KeyMarker': '', 'VersionIdMarker': '', 'NextKeyMarker': 'analytics-s3/template.json', 'NextVersionIdMarker': 'null',
'Versions': [{'ETag': '"e66659e02"', 'Size': 1554, 'StorageClass': 'STANDARD', 'Key': 'analytics-s3i/param.json', 'VersionId': 'null', 'IsLatest': True, 'LastModified': datetime.datetime(2020, 10, 29, 19, 50, 55, tzinfo=tzutc()), 'Owner': {'DisplayName': 'AWS.Development', 'ID': '928b5bd**************3d70'}},
{'ETag': '"22a4bf7**************9c1ed2612"', 'Size': 1756, 'StorageClass': 'STANDARD', 'Key': 'analytics-s3i/params.json', 'VersionId': 'null', 'IsLatest': True, 'LastModified': datetime.datetime(2021, 3, 10, 20, 10, 47, tzinfo=tzutc()), 'Owner': {'DisplayName': 'AWS.Development', 'ID': '928b5bde*****************2d4423d70'}}],
'Name': 's3-test-bucket', 'Prefix': '', 'MaxKeys': 5, 'EncodingType': 'url', 'DeleteMarkers': None, 'CommonPrefixes': None}두 번째 실행에서는 이전 응답의 NextKeyMarker를 starting_token으로 전달하여 나머지 객체 버전을 이어서 조회합니다.
{'ResponseMetadata': {'RequestId': '3V9*********703V', 'HostId': ****************, 'HTTPStatusCode': 200, 'HTTPHeaders': {'x-amz-id-2': '************************', 'x-amz-request-id': '3V9****************V', 'date': 'Sat, 03 Apr 2021 07:18:56 GMT', 'content-type': 'application/xml', 'transfer-encoding': 'chunked', 'server': 'AmazonS3'}, 'RetryAttempts': 0},
'IsTruncated': True, 'KeyMarker': analytics-s3i2/template.json, 'VersionIdMarker': '', 'NextKeyMarker': 'analytics-s3l/params.json', 'NextVersionIdMarker': 'null', 'Versions':
[{'ETag': '"dbd336ff00cb5af3d"', 'Size': 1557, 'StorageClass': 'STANDARD', 'Key': 'analytics-s3i3/param.json', 'VersionId': 'null', 'IsLatest': True, 'LastModified': datetime.datetime(2020, 10, 28, 18, 16, 38, tzinfo=tzutc()), 'Owner': {'DisplayName': 'AWS.Development', 'ID': '*****************'}},
{'ETag': '"66b49598df6"', 'Size': 21334, 'StorageClass': 'STANDARD', 'Key': 'analytics-s3i3/template.json', 'VersionId': 'null', 'IsLatest': True, 'LastModified': datetime.datetime(2020, 10, 28, 18, 16, 38, tzinfo=tzutc()), 'Owner': {'DisplayName': 'AWS.Development', 'ID': '****************'}}],
'Name': 's3-test-bucket', 'Prefix': '', 'MaxKeys': 5, 'EncodingType': 'url', 'DeleteMarkers': None, 'CommonPrefixes': None}정리
boto3의 페이지네이터를 활용하면 대량의 S3 객체 버전도 MaxItems와 PageSize로 조회량을 제어하고, StartingToken으로 이어서 조회할 수 있어 효율적인 데이터 관리가 가능합니다.