입문3분 분량

DynamoDB JSON & 마샬링

DynamoDB API에서 원시 데이터를 처음 읽으면, 넣었던 JSON처럼 보이지 않습니다. {"status": "open", "priority": 3} 같은 평범한 객체가 {"status": {"S": "open"}, "priority": {"N": "3"}}로 돌아옵니다. 모든 값이 그 타입을 명명하는 단일 키 객체로 감싸입니다. 그 감쌈이 DynamoDB JSON이고, 그것과 오가는 변환을 마샬링이라고 합니다.

이건 잡음이 아니라 — DynamoDB가 전송 중에 타입을 모호하지 않게 유지하는 방식입니다. 하지만 일반 JSON을 기대하는 누구든 헷갈리게 하고, 손으로 쓰면 오류가 나기 쉽습니다.

DynamoDB JSON이란 무엇인가요?

DynamoDB JSON은 DynamoDB가 사용하는 타입 태그 방식의 전송 형식으로, 모든 값이 해당 타입을 나타내는 단일 키 객체로 감싸집니다 — 문자열은 {"S": "open"}, 숫자는 {"N": "3"} 형태로요. 일반 JSON을 이 형식으로 변환하고 다시 되돌리는 작업을 마샬링이라고 합니다. 일반 JSON으로는 셋(Set), 바이너리, "3"3의 차이를 표현할 수 없기 때문에 타입을 명확하게 구분하기 위한 방식입니다.

  • DynamoDB JSON은 모든 값에 타입을 태깅합니다 — 문자열은 {"S": "..."}, 숫자는 {"N": "..."} 식으로요.
  • 마샬링 = 일반 JSON → DynamoDB JSON. 언마샬링 = 그 반대.
  • 숫자는 전송 중에 문자열입니다{"N": "3"}이지 {"N": 3}이 아닙니다 — 정밀도를 보존하기 위해서요.
  • 타입 태그가 곧 데이터 타입 시스템입니다, 이미 모델링에 쓰는 것들이죠. S, N, B, BOOL, NULL, L, M, SS, NS, BS.
  • 손으로 쓰지 마세요. SDK의 문서 클라이언트(또는 변환기)가 대신 마샬링합니다. 디버깅하거나 표현식을 만들 때만 수동으로 하세요.

문제: 일반 JSON으로는 부족합니다

JSON에는 스칼라 종류가 정확히 셋뿐입니다 — 문자열, 숫자, 불리언 — 더하기 널, 배열, 객체. DynamoDB에는 더 있습니다. 바이너리, 그리고 JSON이 아예 표현할 수 없는 세 가지 타입(문자열 셋, 숫자 셋, 바이너리 셋)이요. JSON은 또 문자열 "3"을 숫자 3과, 리스트를 셋과 구별하지 못합니다.

그래서 DynamoDB는 여러분의 JSON을 그대로 저장할 수 없습니다 — 각 값의 정확한 타입이 명시적으로 진술되어야 합니다. 타입 디스크립터가 모든 요청과 응답에서 그것을 무손실로 해내는 방식입니다.

인코딩의 작동 방식

모든 속성 값은 키가 타입 디스크립터인 단일 키 객체가 됩니다:

디스크립터타입예시
S문자열{"S": "open"}
N숫자(문자열로){"N": "3"}
B바이너리{"B": "dGV4dA=="}
BOOL불리언{"BOOL": true}
NULL{"NULL": true}
L리스트{"L": [{"S": "a"}, {"N": "1"}]}
M{"M": {"k": {"S": "v"}}}
SS / NS / BS문자열 / 숫자 / 바이너리 셋{"SS": ["a", "b"]}

리스트와 맵은 같은 디스크립터를 맨 아래까지 중첩하므로, 깊게 구조화된 아이템은 깊게 감싸입니다. 숫자가 전송 중에 문자열로 가는 것은 의도적입니다 — JSON 숫자(IEEE-754 배정밀도)가 조용히 반올림할 임의 정밀도를 DynamoDB가 보존하게 해 줍니다. 이들은 모델링에 쓰는 같은 데이터 타입이고, DynamoDB JSON은 그저 그것의 명시적인 전송 중 형태로, AWS 저수준 API 레퍼런스 에 정의되어 있습니다.

실전 예시: 감사 로그 항목

앱에서 작성할 일반 JSON:

{
  "actor": "u-204",
  "action": "ticket.close",
  "ticketId": 8842,
  "tags": ["billing", "urgent"],
  "redacted": false
}

API를 위해 DynamoDB JSON으로 마샬링한 것:

{
  "actor": {"S": "u-204"},
  "action": {"S": "ticket.close"},
  "ticketId": {"N": "8842"},
  "tags": {"SS": ["billing", "urgent"]},
  "redacted": {"BOOL": false}
}

마샬러가 내린 선택에 주목하세요. ticketId문자열 값을 가진 N이 되었고, tags는 리스트가 아니라 문자열 셋(SS)이 되었습니다 — 셋은 중복을 제거하고 순서가 없으므로 의도적인 결정이죠. tagsSS여야 하는지 L이어야 하는지는 변환기가 대신 내릴 수 없는 모델링 결정이며, 인코딩을 이해하는 것이 중요한 이유가 바로 그것입니다.

DynoTable에서 변환하기

이것을 손으로 읽거나 쓸 일은 드뭅니다. 일반 JSON을 DynamoDB JSON 변환기에 붙여 넣어 마샬링(과 그 반대)하고, 요청을 조립할 때는 DynamoDB 표현식 빌더가 표현식과 함께 올바르게 마샬링된 속성-값 맵을 내보냅니다. 앱 자체에서 DynoTable은 아이템을 일반적이고 읽기 좋은 값으로 보여주고 쓸 때 대신 마샬링합니다.

DynoTable이 아이템을 일반 값으로 보여주며, 원시 DynamoDB JSON도 함께 제공하는 모습.
DynoTable이 아이템을 일반 값으로 보여주며, 원시 DynamoDB JSON도 함께 제공하는 모습.

함정과 다음 단계

  • DynamoDB JSON에서 숫자는 문자열입니다{"N": "3"}. 따옴표가 중요합니다. 맨숫자를 내보내지 마세요.
  • 셋 vs 리스트는 모델링 결정이고 인코딩이 그것을 드러나게 합니다 — 의도적으로 고르세요(데이터 타입을 보세요).
  • 앱 코드에서는 손수 마샬링하지 말고 SDK 문서 클라이언트를 선호하세요. 수동 DynamoDB JSON은 디버깅과 표현식용으로 아껴 두세요.
  • 빈 문자열은 허용됩니다 — 아이템에서 허용되지만 역사적으로 도구를 헷갈리게 했습니다 — 엣지 케이스를 검증하세요.

타입 태그를 눈으로 디코딩하는 대신 아이템을 일반 값으로 둘러보고 싶으세요? DynoTable을 다운로드해서 데이터를 직접 다루세요.

업데이트됨