DynamoDB 프로젝션 표현식
프로젝션 표현식(projection expression) 은 DynamoDB의 SELECT col1, col2입니다.
GetItem, Query, Scan에게 아이템 전체 대신 그 속성들만 돌려주라고 알려 주는,
콤마로 구분된 속성 이름 목록이죠.
DynamoDB 프로젝션 표현식은 읽기 비용을 줄여 주나요?
ProjectionExpression은 응답 페이로드를 줄이지, 청구되는 읽기 용량을 줄이지 않습니다. DynamoDB는 저장소에서 전체 아이템을 읽고, 디스크 상 크기를 기준으로 를 계측한 뒤, 나가는 길에 지정하지 않은 속성을 떨굽니다. 읽기 비용을 실제로 줄이려면 커버링 를 사용하세요.
- 페이로드를 줄이지, 읽기 비용을 줄이지 않습니다. DynamoDB는 저장소에서 전체
아이템을 읽고(과금하고), 그런 다음 나가는 길에 지정하지 않은 속성을 떨굽니다.
ProjectionExpression은 네트워크 최적화이지 용량 최적화가 아닙니다. - 공개 부분집합을 가져오는 방법입니다. 호출자가 볼 수 있는 몇 속성만 지정하면, 나머지는 결코 테이블을 떠나지 않습니다.
- 예약일 수 있는 건 무엇이든
#name플레이스홀더를 쓰세요. 표현식 안의 맨 속성 이름은 DynamoDB의 약 570개 예약어와 충돌해 요청을 실패시킵니다. - 진짜 읽기 절약은 커버링 인덱스를 쓰세요. 필요한 컬럼만 프로젝션하는 GSI는 자체의 (더 작은) 크기로 읽힙니다.
실제로 절약하는 것
SQL에서 왔다면 SELECT a, b가 SELECT *보다 덜 스캔한다고 가정합니다. DynamoDB에서
그 직관은 틀립니다. 읽기의 용량
단위는 — 프로젝션이 적용되기 전 — 디스크
상 아이템 크기에서, 다음 4KB로 올림해 계산됩니다. AWS는 명시적입니다.
ProjectionExpression은 요청이 소비하는 읽기 용량을 바꾸지 않습니다.1
그래서 프로젝션은 두 가지를 절약합니다. 둘 다 실재하지만 둘 다 읽기 하류에 있습니다:
- 선을 타고 가는 바이트. 6KB 아이템을 두 개의 작은 속성으로 돌려주면 응답이
자그맣습니다. 수백 개의 아이템을 돌려주는
Query에서는 그게 빠르게 쌓입니다. - 클라이언트 측 작업. 역직렬화할 게 적고, 메모리에 들고 있을 게 적고, 실수로 로그나 API 응답에 새어 나갈 게 적습니다.
절약하지 않는 것은 RCU입니다. 그게 함정입니다 — 비용을 줄이려고 프로젝션에 손을 뻗고, 변화가 없자, DynamoDB가 고장 났다고 결론짓습니다. 아닙니다 — 잘못된 지렛대를 측정한 겁니다.
공개 사용자 프로필 프로젝션하기
사용자 디렉터리를 운영한다고 합시다. 각 프로필은 하나의 아이템으로, 핸들로 사람을 가져올 수 있게 키잉됩니다:
PK = "PROFILE#ada" (파티션 키)
SK = "PROFILE#ada" (정렬 키 — 단일 아이템 컬렉션)
아이템은 뚱뚱합니다. 계정의 공개된 얼굴에 더해 사적이고 운영적인 속성 더미를 실어 나릅니다:
{
"PK": "PROFILE#ada",
"SK": "PROFILE#ada",
"displayName": "Ada L.",
"avatarUrl": "https://cdn.example.com/u/ada.png",
"bio": "Builds things.",
"emailAddress": "ada@example.com",
"passwordResetToken": "…",
"billingCustomerId": "cus_…",
"lastLoginIp": "…",
"internalRiskScore": 0.02
}공개 프로필 카드는 세 필드가 필요합니다. 전체 아이템을 가져오면 emailAddress,
lastLoginIp, internalRiskScore가 결코 봐선 안 될 맥락으로 이동합니다. 공개
부분집합만 지정하세요:
GetItem PK = "PROFILE#ada" SK = "PROFILE#ada"
ProjectionExpression: displayName, avatarUrl, bio
응답은 세 속성을 실어 나릅니다. 사적인 것들은 테이블에 남습니다 — 도착 후 에 여러분의 앱이 걸러 내는 게 아니라, 애초에 응답에 직렬화되지 않습니다. 그게 보안 이득이고, 비밀이 일단 경계를 건넌 뒤엔 되돌리기 어려운 종류의 이득입니다.
DynamoDB 표현식 빌더에서 이 정확한 요청 —
이름, 플레이스홀더, 그리고 SDK 호출 — 을 조립하고 복사할 수 있습니다. 빌더가
ProjectionExpression과 ExpressionAttributeNames 맵을 대신 내보냅니다.
# 플레이스홀더로 예약어 이스케이프
여기서 깔끔한 프로젝션이 터집니다. DynamoDB는 긴 단어 목록을 예약합니다 — name,
status, comment, size, timestamp, 그리고 수백 개 더.2 프로젝션하는
속성이 그중 하나라면, 표현식 안의 날 이름은 거부됩니다.
프로필에 status 속성("active", "suspended")도 있다고 합시다. 이건 실패합니다:
ProjectionExpression displayName, status
status는 예약어입니다. 해결은 표현식 속성 이름 — 실제 이름에 매핑된 # 접두
플레이스홀더 — 입니다:
ProjectionExpression displayName, #s
ExpressionAttributeNames { "#s": "status" }
같은 메커니즘이 중첩 속성에도 닿습니다. 맵에서 단일 필드를, 또는 리스트의 한 원소를 뽑으려면 문서 경로 문법을 쓰세요 — 그리고 각 세그먼트를 플레이스홀더로 하세요. 그중 무엇이든 예약일 수 있으니까요:
ProjectionExpression #addr.#city, tags[0]
ExpressionAttributeNames { "#addr": "address", "#city": "city" }
실용적 규칙: 모든 것을 플레이스홀더로 하세요. 약 570개 예약어 중 어느 것을 밟고 있는지 기억할 필요가 전혀 없고, 표현식은 어느 쪽이든 똑같이 읽힙니다.
커버링 인덱스가 프로젝션을 이기는 때
페이로드만이 아니라 진짜로 읽기 비용을 줄여야 한다면, 지렛대는 여러분이 읽는 속성만
프로젝션하는 글로벌 보조 인덱스입니다. GSI는 데이터의 별개 복사본이고, 그
프로젝션으로 KEYS_ONLY, INCLUDE, ALL을 고릅니다.3 KEYS_ONLY나 좁은
INCLUDE 인덱스는 아이템당 물리적으로 더 작으므로, 그것에 대한 Query는 그 더 작은
크기로 계측됩니다.
그게 커버링 인덱스(covering index) 입니다. 쿼리가 베이스 테이블로 돌아가지 않고 전적으로 인덱스에서 답해지죠. 뜨거운 읽기 패턴이 큰 아이템에서 늘 몇 속성만 필요로 할 때 쓰세요.
ProjectionExpression | 커버링 GSI | |
|---|---|---|
| 페이로드 절감 | 예 | 예 |
| 읽기 비용 절감 | **아니오 | 예** — 인덱스의 크기로 읽음 |
| 추가 저장 | 없음 | 프로젝션된 필드의 두 번째 복사본 |
| 추가 쓰기 비용 | 없음 | 쓰기가 인덱스로 전파됨 |
| 적합한 경우 | 사적 필드 숨기기; 작은 이득 | 큰 아이템에서 몇 필드의 뜨거운 읽기 |
절충은 정직합니다. 인덱스는 읽기 용량을 아끼는 대가로 저장과 쓰기 용량을 듭니다.
무거운 아이템에서 얇은 슬라이스를 자주 읽는다면 가치가 있고, 일회성 GetItem을 깎으려
한다면 가치가 없습니다. 인덱스 유형을 고르는 건
GSI vs LSI를, 그리고 뜨거운 경로에 올리기 전에
GSI 읽기가 묵을 수 있는 때를 보세요.
함정과 다음 단계
- 더 작은 청구서를 기대하지 마세요. 프로젝션만으로는 결코 RCU가 바뀌지 않습니다. 숫자가 안 움직였다면, 그건 버그가 아니라 문서화된 동작입니다.
- 예약어는 플레이스홀더로. 표현식 안의 맨
name이나status는 요청을 실패시킵니다 —#로 매핑하세요. - 키 속성을 프로젝션하는 건 공짜이고 종종 유용합니다 — DynamoDB가 그것들을 저렴하게 돌려주고, 페이지를 넘기거나 다시 가져올 수 있게 합니다.
- 커버링 인덱스에 손을 뻗는 건 뜨거운 패턴이 큰 아이템에서 몇 필드를 읽을 때만입니다. 쓰기/저장 비용을 먼저 저울질하세요.
표현식 빌더에서
ProjectionExpression과 그 속성 이름 맵을 만든 뒤,
DynoTable을 써보세요. 여러분 테이블에 이 프로젝션들을 실행하고 응답이
줄어드는 걸 지켜보세요.
- AWS DynamoDB 개발자 안내서, 읽기 및 쓰기 작업 다루기 — 읽기 용량은 어떤
ProjectionExpression이 적용되기 전의 아이템 크기에 기반합니다. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ ↩ - AWS DynamoDB 개발자 안내서, DynamoDB의 예약어. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html ↩
- AWS DynamoDB 개발자 안내서, 속성 프로젝션 (
KEYS_ONLY/INCLUDE/ALL). https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html ↩