Go의 DynamoDB Scan (AWS SDK v2)

NewScanPaginator는 컬렉션이 아니라 커서입니다. 낙관적으로 시작해 소진된 채로 끝나며, 페이지 크기는 눈앞의 ScanInput이 보여 주지 않는 곳에서 가져옵니다. Scan을 아예 피해야 할 때는 Query vs. Scan을 보세요.

코드

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

func main() {
	ctx := context.TODO()
	cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-east-1"))
	if err != nil {
		log.Fatalf("load config: %v", err)
	}
	client := dynamodb.NewFromConfig(cfg)

	paginator := dynamodb.NewScanPaginator(client, &dynamodb.ScanInput{
		TableName:        aws.String("Music"),
		FilterExpression: aws.String("#filter0 >= :filterValue0"),
		ExpressionAttributeNames: map[string]string{
			"#filter0": "Year",
		},
		ExpressionAttributeValues: map[string]types.AttributeValue{
			":filterValue0": &types.AttributeValueMemberN{Value: "2010"},
		},
	})

	var items []map[string]types.AttributeValue
	for paginator.HasMorePages() {
		page, err := paginator.NextPage(ctx)
		if err != nil {
			log.Fatalf("scan: %v", err)
		}
		items = append(items, page.Items...)
	}
	fmt.Printf("Matched %d items\n", len(items))
}

HasMorePages()는 실제로 무엇을 반환하나요

약 3.9 KB짜리 항목 600곡 픽스처에 대해, 그중 8개가 Year >= 2010에 일치하며 정렬상 마지막에 놓입니다:

fresh HasMorePages():                    true    (before any request)
page 1: len(page.Items)=0  ScannedCount=271  CU=128.5  LastEvaluatedKey set
page 2: len(page.Items)=0  ScannedCount=271  CU=128.5  LastEvaluatedKey set
page 3: len(page.Items)=8  ScannedCount= 58  CU= 27.5  LastEvaluatedKey nil
exhausted HasMorePages():                false
NextPage() after exhaustion:             err = "no more pages available"

HasMorePages()는 첫 호출 전에 true이므로 루프는 항상 최소 한 번은 실행됩니다. 테이블이 비었는지 확인하는 용도로는 쓸 수 없습니다. 그리고 스캔이 아직 진행 중인데도 두 페이지가 항목을 하나도 반환하지 않았는데, 이는 일치 항목 8개를 가진 테이블에서 if len(page.Items) == 0 { break }가 빈 결과를 보고한다는 뜻입니다. 또 페이지네이터는 일회용입니다. 루프가 끝나면 그 값은 소진되고, NextPage는 다시 시작하는 대신 오류를 반환합니다. 다시 스캔하려면 NewScanPaginator를 다시 호출해야 합니다.

Limit은 두 곳에 있고, 옵션이 이깁니다

Go는 페이지 크기를 입력 구조체와 페이지네이터 자체 옵션으로 나눠 둡니다. 둘 다 설정하면 옵션이 우선합니다:

ScanInput.Limit = 500, ScanPaginatorOptions.Limit = 25  ->  ScannedCount 25

in.Limit = aws.Int32(500)을 할당하고 500개짜리 페이지를 기대하는 것은, 다른 어딘가의 함수형 옵션이 이미 25를 설정해 둔 경우 조용히 빗나갑니다. ScanInput만 건드린다면 페이지네이터는 그것을 존중합니다.

2026-07-28에 go1.26.5의 aws-sdk-go-v2/service/dynamodb v1.62.1로 DynamoDB Local(amazon/dynamodb-local)에 대해 측정했습니다.

설명

  • page.Items는 여러분의 구조체가 아니라 []map[string]types.AttributeValue입니다. attributevalue.UnmarshalListOfMaps(items, &songs)가 한 번의 호출로 묶음을 변환하고 Year를 Go의 int로 바꿔 줍니다. 매핑은 dynamodbav 구조체 태그로 제어합니다.
  • 전송 구간에서 숫자는 문자열입니다. 원시 값은 &types.AttributeValueMemberN{Value: "2010"}이며 N 멤버는 Go string을 담으므로, 타입이 지정된 구조체로 언마셜링하지 않는 한 어떤 산술에도 양쪽에 strconv가 따라붙습니다.
  • FilterExpression은 읽은 뒤에 실행되며, 그래서 항목이 없던 두 페이지도 각각 128.5 단위를 썼습니다. API 레퍼런스는 필터링이 "does not consume any additional read capacity units"라고 분명히 밝히는데, 그 따름정리는 아끼지도 못한다는 것입니다.
  • #filter0은 취향이 아니라 필수입니다. Year는 DynamoDB 예약어이며, 별칭 없이 쓰면 ValidationException: Invalid FilterExpression: Attribute name is a reserved keyword; reserved keyword: Year가 반환됩니다.
  • Segment / TotalSegments 는 각 고루틴에 테이블의 몫을 나눠 주며, 각각 자기 페이지네이터가 필요합니다. 병렬화는 실제 소요 시간을 줄여 줄 뿐 같은 용량을 씁니다.

시각적으로 해보기

DynamoDB 쿼리 빌더는 필터, 이름·값 맵, 페이지네이터 루프까지 프로그램 전체 골격을 내놓으므로, 이 페이지가 측정한 부분들을 기억에 의존하지 않고 생성해서 얻을 수 있습니다.

스캔을 서비스에 태우기 전에 필터가 실제로 몇 개 항목을 건드리는지 보려면 DynoTable을 다운로드해서 테이블에 대해 그리드로 실행해 보세요.

관련 예제

참고 자료

이 요청을 시각적으로 만들기

무료 DynamoDB 쿼리 빌더에서 이 작업을 구성하세요 — 키 조건, 필터, 인덱스, Limit, 정렬 순서, 페이지네이션 루프 — 그리고 실행 가능한 SDK v3, CLI, boto3 프로그램으로 다시 복사하세요.

DynamoDB 쿼리 빌더 열기

Console 없이 DynamoDB 작업하기

DynamoDB로는 실행할 수 없는 진짜 SQL(JOINs, GROUP BY, 집계)을 실행하는 빠른 DynamoDB 데스크톱 클라이언트. 시각적 편집과 여러분 자신의 Bedrock 키로 동작하는 AI 에이전트를 제공합니다.

30일 무료 체험, 신용카드 불필요 — 이후 기간 제한 없는 무료 요금제.