Python의 DynamoDB Query (boto3)
이 페이지가 짧은 이유는 boto3의 query 페이지네이터에 있습니다. LastEvaluatedKey를 완전히 감춰 주기 때문입니다. 대신 여러분이 원했을 법한 숫자 하나도 함께 감추는데, 그것을 믿기 전에 알아 둘 가치가 있는 부분입니다. 애초에 query를 언제 써야 하는지는 Query vs. Scan을 보세요.
코드
import boto3
client = boto3.client("dynamodb")
paginator = client.get_paginator("query")
items = []
for page in paginator.paginate(
TableName="Music",
KeyConditionExpression="#hashKey = :hashKeyValue AND begins_with(#rangeKey, :rangeKeyValue)",
ExpressionAttributeNames={"#hashKey": "Artist", "#rangeKey": "SongTitle"},
ExpressionAttributeValues={":hashKeyValue": {"S": "Arturo Sandoval"}, ":rangeKeyValue": {"S": "C"}},
):
items.extend(page["Items"])
print(f"Found {len(items)} items")페이지네이터는 요금을 합산해 주지 않습니다
곡마다 약 3.9 KB이고 모두 Artist = "Arturo Sandoval" 아래에 있는 600곡짜리 픽스처에 대해, 이 루프는 271개, 271개, 58개짜리 세 페이지를 내놓으며 각각 128.5, 128.5, 27.5 읽기 단위를 씁니다. 같은 페이지네이터에 병합된 결과 하나를 요청하면 이렇게 나옵니다:
build_full_result() -> Items 600 Count 600 ScannedCount 600
ConsumedCapacity.CapacityUnits 128.5Count와 ScannedCount는 합산되었습니다. ConsumedCapacity는 아닙니다. 이것은 첫 페이지의 수치이고, 실제 합계는 284.5였습니다. botocore의 DynamoDB 페이지네이터 설정이 그 이유를 분명히 밝힙니다. Count와 ScannedCount는 결과 키로, ConsumedCapacity는 비집계 키로 등록되어 있습니다. build_full_result()에서 용량을 로그로 남기고 있다면, 파티션 전체 읽기를 절반 이상 낮춰 보고하고 있는 것입니다.
위의 for page in paginator.paginate(...) 루프에서 나오는 페이지별 딕셔너리는 원시 응답이므로, page["ConsumedCapacity"]["CapacityUnits"]를 직접 합산하면 정직한 284.5가 나옵니다.
왕복을 58번 더 하게 만드는 Limit
Limit은 유효한 query 파라미터라 paginate()가 받아들이지만, Python 사용자가 기대하는 그 파라미터가 아닙니다:
paginate(..., Limit=10) -> 61 pages, 10 items each
paginate(...) -> 3 pages이것은 전체가 아니라 요청당 항목 수를 제한하므로, 페이지네이터는 같은 600개 항목을 가져오려고 성실하게 HTTP 호출을 61번 합니다. 총량을 제한하려면 PaginationConfig={"MaxItems": 10}을 쓰세요. Limit에 대응하는 손잡이는 PaginationConfig["PageSize"]입니다.
2026-07-28에 CPython 3.14.6의 boto3 1.43.58로 DynamoDB Local(amazon/dynamodb-local)에 대해 측정했습니다.
설명
- 클라이언트는 양방향 모두 DynamoDB JSON으로 말합니다. 값은
{"S": "Arturo Sandoval"}형태로 들어가고Year는{"N": "1994"}로 돌아옵니다. 리소스 API(boto3.resource("dynamodb").Table(...).query)는 양방향으로 변환해Decimal('1994')를 건네줍니다. 금액에는 올바른 선택이지만,float와 더해지기를 거부하는 것을 처음 보면 당황스럽습니다. Key("Artist").eq(...)는 리소스 API 전용입니다. 클라이언트에 넘기면 요청이 나가기도 전에 예외가 납니다:ParamValidationError: Invalid type for parameter KeyConditionExpression ... valid types: <class 'str'>. 클라이언트가 원하는 것은 이 페이지가 만드는 표현식 문자열입니다.- 키 조건은 등호 하나에 정렬 키 비교 최대 하나(
=,<,<=,>,>=,BETWEEN,begins_with)입니다. 그 밖의 것은FilterExpression에 넣으세요. boto3는 그대로 전달하고 DynamoDB는 읽은 뒤에 적용합니다.ScanIndexForward=False는 순서를 뒤집고,IndexName="..."은 인덱스로 대상을 바꿉니다.
시각적으로 해보기
DynamoDB Expression Builder는 키 조건과 타입이 지정된 ExpressionAttributeValues 맵을 boto3에 바로 쓸 수 있는 Python으로 작성해 줍니다. {"N": "2010"} 대신 {"N": 2010}을 타이핑할 때 어긋나는 바로 그 부분입니다.
키 조건 폼에서 같은 쿼리를 여러분의 테이블로 겨냥하고 결과를 페이지 단위 그리드로 읽으려면 DynoTable을 다운로드하세요.
관련 가이드
- Query vs. Scan —
query가 올바른 기본값인 이유. - 키 조건 표현식 — 사용할 수 있는 모든 파티션/정렬 키 연산자.
- "Query condition missed key schema element" — 키 조건이 잘못된 속성을 지정했거나 파티션 키를 건너뛴 경우.
- "Query key condition not supported" — contains나 두 번째 정렬 키 조건처럼 키 조건이 쓸 수 없는 연산자.