Python과 Boto3를 사용한 Amazon DynamoDB 프로그래밍 - Amazon DynamoDB

Python과 Boto3를 사용한 Amazon DynamoDB 프로그래밍

이 안내서는 Python을 통해 Amazon DynamoDB를 사용하려는 프로그래머에게 지침을 제공합니다. 다양한 추상화 계층, 구성 관리, 오류 처리, 재시도 정책 제어, 연결 유지 관리 등에 대해 알아봅니다.

Boto 소개

공식 AWS SDK for Python(일반적으로 Boto3라고 함)을 사용하여 Python에서 DynamoDB에 액세스할 수 있습니다. Boto('보토'라고 발음함)라는 이름은 아마존 강에 서식하는 민물 돌고래에서 유래했습니다. Boto3 라이브러리는 2015년에 처음 출시된 이 라이브러리의 세 번째 메이저 버전입니다. Boto3 라이브러리는 DynamoDB뿐만 아니라 모든 AWS 서비스를 지원하므로 상당히 큽니다. 이 오리엔테이션에서는 Boto3에서 DynamoDB와 관련된 부분만 대상으로 합니다.

Boto는 GitHub에서 호스팅되는 오픈 소스 프로젝트로 AWS에서 유지 관리하고 게시합니다. Boto는 BotocoreBoto3라는 두 개의 패키지로 나뉘어 있습니다.

  • Botocore는 하위 수준 기능을 제공합니다. Botocore에서는 클라이언트, 세션, 자격 증명, 구성 및 예외 클래스를 찾을 수 있습니다.

  • Boto3는 Botocore를 기반으로 빌드됩니다. Python과 더 유사한 더 높은 수준의 인터페이스를 제공합니다. 특히 DynamoDB 테이블을 리소스로 노출하고 하위 수준의 서비스 지향적 클라이언트 인터페이스에 비해 더 단순하고 세련된 인터페이스를 제공합니다.

이러한 프로젝트는 GitHub에서 호스팅되므로 소스 코드를 보거나 미해결 문제를 추적하거나 직접 문제를 제출할 수 있습니다.

Boto 설명서 사용

다음 리소스로 Boto 설명서 사용을 시작하세요.

  • 패키지 설치를 위한 탄탄한 출발점 역할을 하는 Quickstart 섹션부터 시작하세요. 아직 설치하지 않은 경우 Boto3를 설치하는 방법에 대한 지침을 보려면 Quickstart 섹션을 방문하세요(Boto3는 AWS Lambda와 같은 AWS 서비스 내에서 자동으로 제공되는 경우가 많습니다).

  • 그 다음에는 설명서의 DynamoDB 안내서를 중점적으로 살펴보세요. 테이블 생성 및 삭제, 항목 조작, 배치 작업 실행, 쿼리 실행, 스캔 수행 등 기본적인 DynamoDB 활동을 수행하는 방법을 보여줍니다. 이 예시에서는 리소스 인터페이스를 사용합니다. boto3.resource('dynamodb')가 표시되면 상위 수준 리소스 인터페이스를 사용하고 있다는 뜻입니다.

  • 안내서를 살펴본 후 DynamoDB 참조를 검토할 수 있습니다. 이 랜딩 페이지는 사용 가능한 클래스와 메서드의 전체 목록을 제공합니다. 상단에 DynamoDB.Client 클래스가 있습니다. 이 클래스는 모든 컨트롤 플레인 및 데이터 영역 작업에 대한 하위 수준 액세스를 제공합니다. 하단에는 DynamoDB.ServiceResource 클래스가 있습니다. 이 클래스는 더 상위 수준의 Python 스타일 인터페이스입니다. 이를 통해 테이블을 만들거나, 테이블 전체에 걸쳐 배치 작업을 수행하거나, 테이블별 작업을 위한 DynamoDB.ServiceResource.Table 인스턴스를 확보할 수 있습니다.

클라이언트 및 리소스 추상화 계층 이해

사용할 두 인터페이스는 클라이언트 인터페이스와 리소스 인터페이스입니다.

  • 하위 수준 클라이언트 인터페이스는 기본 서비스 API에 대한 일대일 매핑을 제공합니다. DynamoDB에서 제공하는 모든 API는 클라이언트를 통해 사용할 수 있습니다. 즉, 클라이언트 인터페이스가 완전한 기능을 제공할 수 있지만 사용하기가 더 복잡한 경우가 많습니다.

  • 상위 수준 리소스 인터페이스는 기본 서비스 API의 일대일 매핑을 제공하지 않습니다. 하지만 batch_writer와 같이 보다 편리하게 서비스에 액세스할 수 있는 메서드를 제공합니다.

다음은 클라이언트 인터페이스를 사용하여 항목을 삽입하는 예시입니다. 모든 값이 유형(문자열의 경우 'S', 숫자의 경우 'N')을 나타내는 키와 문자열 값으로 구성된 맵으로 전달되는 것을 확인할 수 있습니다. 이를 DynamoDB JSON 형식이라고 합니다.

import boto3 dynamodb = boto3.client('dynamodb') dynamodb.put_item( TableName='YourTableName', Item={ 'pk': {'S': 'id#1'}, 'sk': {'S': 'cart#123'}, 'name': {'S': 'SomeName'}, 'inventory': {'N': '500'}, # ... more attributes ... } )

다음은 동일한 PutItem 작업을 리소스 인터페이스를 사용하여 수행한 것입니다. 데이터 입력은 암시적입니다.

import boto3 dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('YourTableName') table.put_item( Item={ 'pk': 'id#1', 'sk': 'cart#123', 'name': 'SomeName', 'inventory': 500, # ... more attributes ... } )

필요한 경우 boto3에서 제공하는 TypeSerializerTypeDeserializer 클래스를 사용하여 일반 JSON과 DynamoDB JSON 간에 변환할 수 있습니다.

def dynamo_to_python(dynamo_object: dict) -> dict: deserializer = TypeDeserializer() return { k: deserializer.deserialize(v) for k, v in dynamo_object.items() } def python_to_dynamo(python_object: dict) -> dict: serializer = TypeSerializer() return { k: serializer.serialize(v) for k, v in python_object.items() }

클라이언트 인터페이스를 사용하여 쿼리를 수행하는 방법은 다음과 같습니다. 쿼리를 JSON 구성으로 표현합니다. 잠재적 키워드 충돌을 처리하기 위해 변수 대체가 필요한 KeyConditionExpression 문자열을 사용합니다.

import boto3 client = boto3.client('dynamodb') # Construct the query response = client.query( TableName='YourTableName', KeyConditionExpression='pk = :pk_val AND begins_with(sk, :sk_val)', FilterExpression='#name = :name_val', ExpressionAttributeValues={ ':pk_val': {'S': 'id#1'}, ':sk_val': {'S': 'cart#'}, ':name_val': {'S': 'SomeName'}, }, ExpressionAttributeNames={ '#name': 'name', } )

리소스 인터페이스를 사용한 동일한 쿼리 작업을 줄이고 단순화할 수 있습니다.

import boto3 from boto3.dynamodb.conditions import Key, Attr dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('YourTableName') response = table.query( KeyConditionExpression=Key('pk').eq('id#1') & Key('sk').begins_with('cart#'), FilterExpression=Attr('name').eq('SomeName') )

마지막 예로 테이블의 대략적인 크기(테이블에 보관되며 약 6시간마다 업데이트되는 메타데이터)를 구한다고 가정해 보겠습니다. 클라이언트 인터페이스에서 describe_table() 작업을 수행하고 반환된 JSON 구조에서 답을 가져옵니다.

import boto3 dynamodb = boto3.client('dynamodb') response = dynamodb.describe_table(TableName='YourTableName') size = response['Table']['TableSizeBytes']

리소스 인터페이스에서 테이블이 설명 작업을 암시적으로 수행하고 데이터를 속성으로 직접 표시합니다.

import boto3 dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('YourTableName') size = table.table_size_bytes
참고

개발에 클라이언트 인터페이스를 사용할지 리소스 인터페이스를 사용할지 고려할 때는 리소스 인터페이스에 신규 기능이 추가되지 않는다는 점을 유의하세요. 리소스 설명서에는 "AWS Python SDK 팀은 boto3의 리소스 인터페이스에 신규 기능을 추가할 의도가 없습니다. 기존 인터페이스는 boto3의 수명 주기 동안 계속 작동할 것입니다. 클라이언트 인터페이스를 통해 새로운 서비스 기능에 액세스할 수 있습니다.”라고 명시되어 있습니다.

batch_writer 테이블 리소스 사용

상위 수준 테이블 리소스에서만 사용할 수 있는 편리한 기능 중 하나는 batch_writer입니다. DynamoDB는 한 번의 네트워크 요청으로 최대 25개의 넣기 또는 삭제 작업을 허용하는 배치 쓰기 작업을 지원합니다. 이와 같은 배치 작업은 네트워크 왕복을 최소화하여 효율성을 개선합니다.

하위 수준 클라이언트 라이브러리에서는 client.batch_write_item() 작업을 사용하여 배치를 실행합니다. 작업을 25개씩 배치로 수동으로 분할해야 합니다. 각 작업 후에는 처리되지 않은 항목 목록도 수신을 요청해야 합니다. 쓰기 작업 중 일부는 성공하지만 다른 작업은 실패할 수 있습니다. 그런 다음 처리되지 않은 항목을 이후 batch_write_item() 작업으로 다시 전달해야 합니다. 보일러플레이트 코드가 상당히 많습니다.

Table.batch_writer 메서드는 객체를 배치로 작성하기 위한 컨텍스트 관리자를 만듭니다. 마치 한 번에 하나씩 항목을 쓰는 것처럼 보이지만 내부적으로는 항목을 버퍼링하고 배치로 전송하는 인터페이스를 제공합니다. 또한 처리되지 않은 항목 재시도를 암시적으로 처리합니다.

dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('YourTableName') movies = # long list of movies in {'pk': 'val', 'sk': 'val', etc} format with table.batch_writer() as writer: for movie in movies: writer.put_item(Item=movie)

클라이언트 및 리소스 계층을 탐색하는 추가 코드 예시

클라이언트와 리소스를 모두 사용하여 다양한 함수의 사용법을 탐색하는 다음 코드 샘플 리포지토리를 참조할 수도 있습니다.

Client 및 Resource 객체가 세션 및 스레드와 상호 작용하는 방식 이해

Resource 객체는 스레드로부터 안전하지 않으므로 스레드 또는 프로세스 간에 공유해서는 안 됩니다. 자세한 내용은 Resource 가이드를 참조하세요.

반면 Client 객체는 특정 고급 기능을 제외하고는 일반적으로 스레드로부터 안전합니다. 자세한 내용은 Clients 가이드를 참조하세요.

Session 객체는 스레드로부터 안전하지 않습니다. 따라서 다중 스레드 환경에서 Client나 Resource를 만들 때마다 먼저 새 Session을 만든 다음 Session에서 Client 또는 Resource를 만들어야 합니다. 자세한 내용은 Sessions 가이드를 참조하세요.

boto3.resource()를 직접 호출하면 암시적으로 기본 Session을 사용하게 됩니다. 이는 단일 스레드 코드를 작성할 때 편리합니다. 다중 스레드 코드를 작성할 때는 먼저 각 스레드에 대해 새 Session을 생성한 다음 해당 Session에서 리소스를 검색하는 것이 좋습니다.

# Explicitly create a new Session for this thread session = boto3.Session() dynamodb = session.resource('dynamodb')

Config 객체 사용자 지정

Client 또는 Resource 객체를 구성할 때 선택적으로 명명된 파라미터를 전달하여 동작을 사용자 지정할 수 있습니다. config라는 파라미터를 사용하면 다양한 기능을 사용할 수 있습니다. 이 파라미터는 botocore.client.Config의 인스턴스이며 Config에 대한 참조 설명서에 사용자가 제어할 수 있도록 노출되는 모든 내용이 나와 있습니다. Configuration 가이드는 개요를 알아보기에 좋습니다.

참고

이러한 동작 설정의 대부분은 Session 수준에서, AWS 구성 파일 내에서 또는 환경 변수로 수정할 수 있습니다.

제한 시간을 위한 Config

사용자 지정 구성의 용도 중 하나는 네트워킹 동작을 조정하는 것입니다.

  • connect_timeout(float 또는 int) - 연결을 시도할 때 제한 시간 예외가 발생하기까지의 시간(초)입니다. 기본값은 60초입니다.

  • read_timeout (float 또는 int) - 연결에서 읽기를 시도할 때 제한 시간 예외가 발생하기까지의 시간(초)입니다. 기본값은 60초입니다.

DynamoDB에서는 60초의 제한 시간은 너무 깁니다. 즉, 일시적인 네트워크 결함으로 인해 재시도하기 전에 클라이언트에게 1분 정도의 지연 시간이 발생한다는 뜻입니다. 다음 코드는 제한 시간을 1초로 단축합니다.

import boto3 from botocore.config import Config my_config = Config( connect_timeout = 1.0, read_timeout = 1.0 ) dynamodb = boto3.resource('dynamodb', config=my_config)

제한 시간에 대한 자세한 설명은 Tuning AWS Java SDK HTTP request settings for latency-aware DynamoDB applications를 참조하세요. 참고로 Java SDK에는 Python보다 더 많은 제한 시간 구성이 있습니다.

연결 유지를 위한 Config

botocore 1.27.84 이상을 사용하는 경우 TCP 연결 유지도 제어할 수 있습니다.

  • tcp_keepalive(bool) - False(기본값 True)으로 설정된 경우 새 연결을 만들 때 사용하는 TCP 연결 유지 소켓 옵션을 활성화합니다. 이 기능은 botocore 1.27.84부터 사용할 수 있습니다.

TCP 연결 유지를 True로 설정하면 평균 지연 시간을 줄일 수 있습니다. 다음은 올바른 botocore 버전을 사용하는 경우 TCP 연결 유지를 조건부로 true로 설정하는 샘플 코드입니다.

import botocore import boto3 from botocore.config import Config from distutils.version import LooseVersion required_version = "1.27.84" current_version = botocore.__version__ my_config = Config( connect_timeout = 0.5, read_timeout = 0.5 ) if LooseVersion(current_version) > LooseVersion(required_version): my_config = my_config.merge(Config(tcp_keepalive = True)) dynamodb = boto3.resource('dynamodb', config=my_config)
참고

TCP 연결 유지는 HTTP 연결 유지와 다릅니다. TCP 연결 유지를 사용하면 기본 운영 체제가 소켓 연결을 통해 작은 패킷을 전송하여 연결을 유지하고 연결 해제를 즉시 감지합니다. HTTP 연결 유지를 사용하면 기본 소켓에 구축된 웹 연결이 재사용됩니다. Boto3에서는 HTTP 연결 유지가 항상 활성화되어 있습니다.

유휴 연결을 유지할 수 있는 기간에는 한도가 있습니다. 연결이 유휴 상태이지만 다음 요청에서 이미 설정된 연결을 사용하도록 하려면 주기적으로(예: 1분마다) 요청을 보내는 것을 고려하세요.

재시도를 위한 Config

이 구성에서는 원하는 재시도 동작을 지정할 수 있는 retry라는 딕셔너리도 허용합니다. SDK에서 오류를 수신하고 오류가 일시적 유형인 경우 SDK 내에서 재시도가 발생합니다. 오류가 내부적으로 재시도되어 결국 성공적인 응답이 나오는 경우 직접 호출하는 코드의 관점에서 보면 오류가 나타나지 않고 지연 시간이 약간 길어질 뿐입니다. 지정할 수 있는 값은 다음과 같습니다.

  • max_attempts - 단일 요청에서 수행할 수 있는 최대 재시도 횟수를 나타내는 정수입니다. 예를 들어 이 값을 2로 설정하면 초기 요청 이후 요청이 최대 두 번 재시도됩니다. 이 값을 0으로 설정하면 초기 요청 이후 재시도하지 않습니다.

  • total_max_attempts - 단일 요청에서 수행할 수 있는 총 시도 횟수를 나타내는 정수입니다. 여기에는 초기 요청이 포함되므로 값이 1이면 요청이 재시도되지 않음을 나타냅니다. total_max_attemptsmax_attempts를 모두 제공하는 경우 total_max_attempts가 우선합니다. total_max_attemptsAWS_MAX_ATTEMPTS 환경 변수 및 max_attempts 구성 파일 값에 매핑되기 때문에 max_attempts보다 우선하는 것입니다.

  • mode - Botocore가 사용해야 하는 재시도 모드의 유형을 나타내는 문자열입니다. 유효한 값은 다음과 같습니다.

    • legacy - 기본 모드입니다. 첫 번째 재시도를 50ms 동안 기다렸다가 기본 계수 2의 지수 백오프를 사용합니다. DynamoDB의 경우 위 값으로 재정의하지 않는 한 모두 합쳐 최대 10회까지 시도합니다.

      참고

      지수 백오프를 사용할 경우 마지막 시도는 거의 13초간 기다리게 됩니다.

    • standard - 다른 AWS SDK와 더 일관적이기 때문에 표준이라는 이름이 지정되었습니다. 첫 번째 재시도를 위해 0ms에서 1,000ms 사이의 임의의 시간 동안 기다립니다. 다시 재시도해야 하는 경우 0ms에서 1,000ms 사이의 또 다른 임의의 시간을 선택하여 2를 곱합니다. 추가 재시도가 필요한 경우 같은 범위에서 임의로 선택한 시간에 4를 곱하는 식으로 반복합니다. 각 대기 시간은 20초로 제한됩니다. 이 모드는 legacy 모드보다 더 많이 감지된 장애 조건에 대해 재시도를 수행합니다. DynamoDB의 경우 위 값으로 재정의하지 않는 한 모두 합쳐 최대 3회까지 시도합니다.

    • adaptive - 표준 모드의 모든 기능을 포함하고 자동 클라이언트 측 제한도 포함하는 실험적인 재시도 모드입니다. 적응형 속도 제한을 사용하면 SDK가 요청 전송 속도를 늦춰 AWS 서비스의 요청 용량을 더 잘 수용할 수 있습니다. 이 모드는 동작이 변경될 수 있는 임시 모드입니다.

이러한 재시도 모드의 확장된 정의는 재시도 가이드SDK 참조의 Retry behavior 주제에서 확인할 수 있습니다.

다음은 모두 합쳐 최대 3회의 요청(재시도 횟수 2회)을 수행하는 legacy 재시도 정책을 명시적으로 사용하는 예시입니다.

import boto3 from botocore.config import Config my_config = Config( connect_timeout = 1.0, read_timeout = 1.0, retries = { 'mode': 'legacy', 'total_max_attempts': 3 } ) dynamodb = boto3.resource('dynamodb', config=my_config)

DynamoDB는 가용성이 높고 지연 시간이 짧은 시스템이므로 기본 제공되는 재시도 정책이 허용하는 것보다 더 공격적으로 재시도 속도를 높이는 것이 좋습니다. Boto3에 의존하여 암시적 재시도를 수행하는 대신 최대 시도 횟수를 0으로 설정하고, 직접 예외를 포착하고, 자체 코드에서 적절하게 재시도함으로써 자체 재시도 정책을 구현할 수 있습니다.

자체 재시도 정책을 관리하는 경우 제한과 오류를 구분하는 것이 좋습니다.

  • 제한(ProvisionedThroughputExceededException 또는 ThrottlingException으로 표시됨)은 정상 서비스에서 DynamoDB 테이블 또는 파티션의 읽기 또는 쓰기 용량을 초과했음을 알리는 것입니다. 밀리초가 지날 때마다 약간 더 많은 읽기 또는 쓰기 용량을 사용할 수 있게 되므로 빠르게(예: 50ms마다) 재시도하여 새로 확보된 용량에 액세스하려고 시도할 수 있습니다. 제한을 사용하면 지수 백오프가 특별히 필요하지 않습니다. 제한은 DynamoDB가 반환하기에 가볍고 요청당 요금이 부과되지 않기 때문입니다. 지수 백오프는 이미 가장 오래 기다린 클라이언트 스레드에 더 긴 지연을 할당하여 통계적으로 p50과 p99를 밖으로 확장합니다.

  • 오류(특히 InternalServerError 또는 ServiceUnavailable로 표시됨)는 서비스에 일시적인 문제가 있음을 나타냅니다. 테이블 전체에 대한 것일 수도 있고, 읽거나 쓰고 있는 파티션에만 해당될 수도 있습니다. 오류가 발생하면 재시도 전에 더 오래(예: 250ms 또는 500ms) 중단하고 지터를 사용하여 재시도에 시차를 줄 수 있습니다.

최대 풀 연결을 위한 Config

마지막으로, 구성을 통해 연결 풀 크기를 제어할 수 있습니다.

  • max_pool_connections(int) - 연결 풀에 유지할 수 있는 최대 연결 수입니다. 값을 설정하지 않으면 기본값 10이 사용됩니다.

이 옵션은 재사용을 위해 풀링된 상태로 유지할 최대 HTTP 연결 수를 제어합니다. Session마다 다른 풀이 보관됩니다. 동일한 Session에서 구축된 클라이언트나 리소스에 대해 스레드가 10개 이상 발생할 것으로 예상되면 스레드가 풀링된 연결을 사용하는 다른 스레드에서 대기하지 않아도 되도록 이 값을 늘리는 것이 좋습니다.

import boto3 from botocore.config import Config my_config = Config( max_pool_connections = 20 ) # Setup a single session holding up to 20 pooled connections session = boto3.Session(my_config) # Create up to 20 resources against that session for handing to threads # Notice the single-threaded access to the Session and each Resource resource1 = session.resource('dynamodb') resource2 = session.resource('dynamodb') # etc

오류 처리

AWS 서비스 예외가 Boto3에서 모두 정적으로 정의되어 있지는 않습니다. 이는 AWS 서비스의 오류 및 예외가 매우 다양하고 변경될 수 있기 때문입니다. Boto3는 모든 서비스 예외를 ClientError로 래핑하고 세부 정보를 구조화된 JSON으로 노출합니다. 예를 들어, 오류 응답은 다음과 같이 구성될 수 있습니다.

{ 'Error': { 'Code': 'SomeServiceException', 'Message': 'Details/context around the exception or error' }, 'ResponseMetadata': { 'RequestId': '1234567890ABCDEF', 'HostId': 'host ID data will appear here as a hash', 'HTTPStatusCode': 400, 'HTTPHeaders': {'header metadata key/values will appear here'}, 'RetryAttempts': 0 } }

다음 코드는 모든 ClientError 예외를 포착하고 Error 내의 Code 문자열 값을 검토하여 취해야 할 조치를 결정합니다.

import botocore import boto3 dynamodb = boto3.client('dynamodb') try: response = dynamodb.put_item(...) except botocore.exceptions.ClientError as err: print('Error Code: {}'.format(err.response['Error']['Code'])) print('Error Message: {}'.format(err.response['Error']['Message'])) print('Http Code: {}'.format(err.response['ResponseMetadata']['HTTPStatusCode'])) print('Request ID: {}'.format(err.response['ResponseMetadata']['RequestId'])) if err.response['Error']['Code'] in ('ProvisionedThroughputExceededException', 'ThrottlingException'): print("Received a throttle") elif err.response['Error']['Code'] == 'InternalServerError': print("Received a server error") else: raise err

전부는 아니지만 일부 예외 코드는 최상위 수준 클래스로 구체화되었습니다. 이를 직접 처리할 수도 있습니다. Client 인터페이스를 사용하는 경우 이러한 예외는 클라이언트에 동적으로 채워지며 다음과 같이 클라이언트 인스턴스를 사용하여 이러한 예외를 포착할 수 있습니다.

except ddb_client.exceptions.ProvisionedThroughputExceededException:

Resource 인터페이스를 사용할 때는 .meta.client를 사용하여 다음과 같이 리소스에서 기본 Client로 이동해야 예외에 액세스할 수 있습니다.

except ddb_resource.meta.client.exceptions.ProvisionedThroughputExceededException:

구체화된 예외 유형 목록을 검토하려면 목록을 동적으로 생성하면 됩니다.

ddb = boto3.client("dynamodb") print([e for e in dir(ddb.exceptions) if e.endswith('Exception') or e.endswith('Error')])

조건식을 사용하여 쓰기 작업을 수행할 때 표현식이 실패할 경우 오류 응답에서 항목 값을 반환하도록 요청할 수 있습니다.

try: response = table.put_item( Item=item, ConditionExpression='attribute_not_exists(pk)', ReturnValuesOnConditionCheckFailure='ALL_OLD' ) except table.meta.client.exceptions.ConditionalCheckFailedException as e: print('Item already exists:', e.response['Item'])

오류 처리 및 예외에 대한 추가 정보:

로깅

boto3 라이브러리는 Python의 기본 제공 로깅 모듈과 통합되어 세션 중에 발생하는 일을 추적합니다. 로깅 수준을 제어하기 위해 로깅 모듈을 구성할 수 있습니다.

import logging logging.basicConfig(level=logging.INFO)

이렇게 하면 루트 로거가 INFO 이상 수준의 메시지를 로깅하도록 구성됩니다. 이 수준보다 덜 심각한 로깅 메시지는 무시됩니다. 로깅 수준은 DEBUG, INFO, WARNING, ERROR, CRITICAL입니다. 기본값은 WARNING입니다.

Boto3의 로거는 계층적입니다. 라이브러리는 각각 라이브러리의 다른 부분에 해당하는 몇 가지 다른 로거를 사용합니다. 각 로거의 동작을 개별적으로 제어할 수 있습니다.

  • boto3: boto3 모듈의 기본 로거입니다.

  • botocore: botocore 패키지의 메인 로거입니다.

  • botocore.auth: 요청에 대한 AWS 서명 생성을 로깅하는 데 사용됩니다.

  • botocore.credentials: 자격 증명 가져오기 및 새로 고침 프로세스를 로깅하는 데 사용됩니다.

  • botocore.endpoint: 네트워크를 통해 전송되기 전에 요청 생성을 로깅하는 데 사용됩니다.

  • botocore.hooks: 라이브러리에서 트리거되는 이벤트를 로깅하는 데 사용됩니다.

  • botocore.loaders: AWS 서비스 모델의 일부가 로드될 때 로깅하는 데 사용됩니다.

  • botocore.parsers: AWS 서비스 응답을 구문 분석하기 전에 로깅하는 데 사용됩니다.

  • botocore.retryhandler: AWS 서비스 요청 재시도 처리를 로깅하는 데 사용됩니다(레거시 모드).

  • botocore.retries.standard: AWS 서비스 요청 재시도 처리를 로깅하는 데 사용됩니다(표준 또는 적응형 모드).

  • botocore.utils: 라이브러리의 기타 활동을 로깅하는 데 사용됩니다.

  • botocore.waiter: 특정 상태에 도달할 때까지 AWS 서비스를 폴링하는 웨이터의 기능을 로깅하는 데 사용됩니다.

다른 라이브러리도 로깅합니다. 내부적으로 boto3는 HTTP 연결 처리를 위해 서드 파티 urllib3를 사용합니다. 지연 시간이 중요한 경우 urllib3가 언제 새 연결을 설정하거나 유휴 연결을 종료하는지 확인하여 풀을 제대로 활용하고 있는지 확인할 수 있습니다.

  • urllib3.connectionpool: 연결 풀 처리 이벤트를 로깅하는 데 사용합니다.

다음 코드 스니펫은 엔드포인트 및 연결 풀 활동에 대한 DEBUG 로깅과 함께 대부분의 로깅을 INFO로 설정합니다.

import logging logging.getLogger('boto3').setLevel(logging.INFO) logging.getLogger('botocore').setLevel(logging.INFO) logging.getLogger('botocore.endpoint').setLevel(logging.DEBUG) logging.getLogger('urllib3.connectionpool').setLevel(logging.DEBUG)

이벤트 후크

Botocore는 실행의 여러 부분에서 이벤트를 내보냅니다. 이러한 이벤트에 대한 핸들러를 등록하여 이벤트가 발생할 때마다 핸들러가 직접 호출되도록 할 수 있습니다. 이렇게 하면 내부를 수정하지 않고도 botocore의 동작을 확장할 수 있습니다.

예를 들어 애플리케이션의 DynamoDB 테이블에서 PutItem 작업이 직접 호출될 때마다 추적하고 싶다고 가정해 보겠습니다. 관련 Session에서 PutItem 작업이 간접 호출될 때마다 'provide-client-params.dynamodb.PutItem' 이벤트를 등록하여 포착하고 로깅할 수 있습니다. 다음은 그 예입니다.

import boto3 import botocore import logging def log_put_params(params, **kwargs): if 'TableName' in params and 'Item' in params: logging.info(f"PutItem on table {params['TableName']}: {params['Item']}") logging.basicConfig(level=logging.INFO) session = boto3.Session() event_system = session.events # Register our interest in hooking in when the parameters are provided to PutItem event_system.register('provide-client-params.dynamodb.PutItem', log_put_params) # Now, every time you use this session to put an item in DynamoDB, # it will log the table name and item data. dynamodb = session.resource('dynamodb') table = dynamodb.Table('YourTableName') table.put_item( Item={ 'pk': '123', 'sk': 'cart#123', 'item_data': 'YourItemData', # ... more attributes ... } )

핸들러 내에서 파라미터를 프로그래밍 방식으로 조작하여 동작을 변경할 수도 있습니다.

params['TableName'] = "NewTableName"

이벤트에 대한 자세한 내용은 이벤트에 대한 botocore 설명서이벤트에 대한 boto3 설명서를 참조하세요.

페이지 매김 및 페이지네이터

쿼리 및 스캔과 같은 일부 요청의 경우 단일 요청에서 반환되는 데이터 크기가 제한되므로 후속 페이지를 가져오려면 반복적으로 요청해야 합니다.

limit 파라미터를 사용하여 각 페이지에 대해 읽을 항목의 최대 수를 제어할 수 있습니다. 예를 들어, 마지막 10개의 항목을 원하는 경우 limit을 사용하여 마지막 10개만 검색할 수 있습니다. 이 제한은 필터링을 적용하기 전에 테이블에서 읽어야 하는 분량이라는 것을 참고하세요. 필터링 후 정확히 10개를 원한다고 지정할 수 있는 방법은 없습니다. 필터링 전의 개수를 제어하고 실제로 10개 항목을 검색한 경우에만 클라이언트 측에서 확인할 수 있습니다. 제한과 관계없이 모든 응답의 최대 크기는 항상 1MB입니다.

응답에 LastEvaluatedKey가 포함된 경우 개수 또는 크기 제한에 도달하여 응답이 종료되었음을 나타냅니다. 이 키는 해당 응답에 대해 마지막으로 평가된 키입니다. 이 LastEvaluatedKey를 검색하고 후속 직접 호출에 ExclusiveStartKey로 전달하여 해당 시작 지점부터 다음 청크를 읽을 수 있습니다. 이것을 반환한 LastEvaluatedKey가 없으면 Query 또는 Scan과 일치하는 항목이 더 이상 없음을 의미합니다.

다음은 Resource 인터페이스를 사용하지만 Client 인터페이스의 패턴이 같은 간단한 예시로, 페이지당 최대 100개의 항목을 읽고 모든 항목을 읽을 때까지 반복합니다.

import boto3 dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('YourTableName') query_params = { 'KeyConditionExpression': Key('pk').eq('123') & Key('sk').gt(1000), 'Limit': 100 } while True: response = table.query(**query_params) # Process the items however you like for item in response['Items']: print(item) # No LastEvaluatedKey means no more items to retrieve if 'LastEvaluatedKey' not in response: break # If there are possibly more items, update the start key for the next page query_params['ExclusiveStartKey'] = response['LastEvaluatedKey']

편의를 위해 boto3에서는 페이지네이터를 사용하여 이 작업을 대신 수행할 수 있습니다. 하지만 이 기능은 Client 인터페이스에서만 작동합니다. 페이지네이터를 사용하도록 다시 작성된 코드는 다음과 같습니다.

import boto3 dynamodb = boto3.client('dynamodb') paginator = dynamodb.get_paginator('query') query_params = { 'TableName': 'YourTableName', 'KeyConditionExpression': 'pk = :pk_val AND sk > :sk_val', 'ExpressionAttributeValues': { ':pk_val': {'S': '123'}, ':sk_val': {'N': '1000'}, }, 'Limit': 100 } page_iterator = paginator.paginate(**query_params) for page in page_iterator: # Process the items however you like for item in page['Items']: print(item)

자세한 내용은 페이지네이터 가이드DynamoDB.Paginator.Query에 대한 API 참조를 확인하세요.

참고

페이지네이터에는 MaxItems, StartingToken, PageSize라는 자체 구성 설정도 있습니다. DynamoDB로 페이지를 매기는 경우 이러한 설정을 무시해야 합니다.

Waiters

웨이터는 작업이 완료될 때까지 기다렸다가 진행하는 기능을 제공합니다. 현재는 테이블이 생성되거나 삭제될 때까지 기다리는 것만 지원합니다. 백그라운드에서 웨이터 작업은 20초마다 최대 25회까지 사용자를 대신하여 확인을 수행합니다. 이 작업은 직접 할 수도 있지만, 자동화를 작성할 때는 웨이터를 사용하는 것이 좋습니다.

다음 코드는 특정 테이블이 생성될 때까지 기다리는 방법을 보여줍니다.

# Create a table, wait until it exists, and print its ARN response = client.create_table(...) waiter = client.get_waiter('table_exists') waiter.wait(TableName='YourTableName') print('Table created:', response['TableDescription']['TableArn']

자세한 내용은 웨이터 가이드웨이터에 대한 참조를 확인하세요.