

# JSON 형식의 Amazon RDS Data API 쿼리 결과 처리
<a name="data-api-json"></a>

 `ExecuteStatement` 작업을 호출할 때 쿼리 결과를 JSON 형식의 문자열로 반환하도록 선택할 수 있습니다. 이렇게 하면 프로그래밍 언어의 JSON 구문 분석 기능을 사용하여 결과 집합을 해석하고 형식을 다시 지정할 수 있습니다. 그러면 결과 집합을 반복하고 각 열 값을 해석하는 추가 코드를 작성하지 않아도 됩니다.

 결과 집합을 JSON 형식으로 요청하려면 선택적으로 `formatRecordsAs` 파라미터를 `JSON` 값과 함께 전달합니다. JSON 형식의 결과 집합이 `ExecuteStatementResponse` 구조의 `formattedRecords` 필드에 반환됩니다.

 `BatchExecuteStatement` 작업은 결과 집합을 반환하지 않습니다. 따라서 JSON 옵션은 해당 작업에 적용되지 않습니다.

 JSON 해시 구조에서 키를 사용자 정의하려면 결과 집합에서 열 별칭을 정의합니다. SQL 쿼리의 열 목록에 있는 `AS` 절을 사용하면 됩니다.

 JSON 기능을 사용하여 결과 집합을 더 쉽게 읽도록 하고 내용을 언어별 프레임워크에 매핑할 수 있습니다. ASCII로 인코딩된 결과 집합의 볼륨이 기본 표현보다 크기 때문에 많은 수의 행이나 큰 열 값을 반환하여 애플리케이션에서 사용할 수 있는 것보다 많은 메모리를 사용하는 쿼리에 기본 표현을 선택할 수 있습니다.

**Topics**
+ [JSON 형식의 쿼리 결과 검색](#data-api-json-querying)
+ [데이터 유형 매핑](#data-api-json-datatypes)
+ [문제 해결](#data-api-json-troubleshooting)
+ [예시](#data-api-json-examples)

## JSON 형식의 쿼리 결과 검색
<a name="data-api-json-querying"></a>

 결과 집합을 JSON 문자열로 수신하려면 `ExecuteStatement` 호출에 `.withFormatRecordsAs(RecordsFormatType.JSON)`을 포함하세요. 반환 값은 `formattedRecords` 필드에 JSON 문자열로 반환됩니다. 이 경우 `columnMetadata`는 `null`입니다. 열 레이블은 각 행을 나타내는 객체의 키입니다. 이 열 이름은 결과 집합의 각 행에서 반복됩니다. 열 값은 따옴표로 묶인 문자열, 숫자 값 또는 `true`, `false` 또는 `null`을 나타내는 특수 값입니다. 길이 제약 조건 및 숫자 및 문자열의 정확한 유형과 같은 열 메타데이터는 JSON 응답에 보존되지 않습니다.

 `.withFormatRecordsAs()` 호출을 생략하거나 `NONE`의 파라미터를 지정하는 경우 결과 집합은 `Records` 및 `columnMetadata` 필드를 사용하여 이진 형식으로 반환됩니다.

## 데이터 유형 매핑
<a name="data-api-json-datatypes"></a>

 결과 집합의 SQL 값은 더 작은 JSON 유형 집합에 매핑됩니다. 값은 JSON에서 문자열, 숫자 및 `true`, `false` 및 `null`과 같은 특수 상수로 표시됩니다. 프로그래밍 언어에 적합하도록 강한 타이핑 또는 약한 타이핑을 사용하여 이 값을 애플리케이션에서 변수로 변환할 수 있습니다.


****  

|   JDBC 데이터 유형   |   JSON 데이터 유형   | 
| --- | --- | 
|   `INTEGER`, `TINYINT`, `SMALLINT`, `BIGINT`   |   기본적으로 숫자입니다. `LongReturnType` 옵션이 `STRING`으로 설정되어 있으면 문자열입니다.  | 
|   `FLOAT`, `REAL`, `DOUBLE`   |   숫자   | 
|   `DECIMAL`   |   기본적으로 문자열입니다. `DecimalReturnType` 옵션이 `DOUBLE_OR_LONG`으로 설정되어 있으면 숫자입니다.  | 
|   `STRING`   |   문자열   | 
|   `BOOLEAN`, `BIT`   |   부울   | 
|   `BLOB`, `BINARY`, `VARBINARY`, `LONGVARBINARY`   |   base64 인코딩의 문자열입니다.  | 
|   `CLOB`   |   String   | 
|   `ARRAY`   |   Array   | 
|   `NULL`   |   `null`   | 
|   다른 형식(날짜 및 시간과 관련된 형식 포함)   |   String   | 

## 문제 해결
<a name="data-api-json-troubleshooting"></a>

 JSON 응답은 10MB로 제한됩니다. 응답이 이 한도보다 크면 프로그램에서 `BadRequestException` 오류를 받습니다. 이런 경우 다음 기법 중 하나를 사용하여 오류를 해결할 수 있습니다.
+  결과 집합에서 행의 수를 줄입니다. `LIMIT` 절을 추가하면 됩니다. `LIMIT` 및 `OFFSET` 절이 있는 여러 쿼리를 제출하여 큰 결과 집합을 여러 개의 작은 결과 집합으로 나눌 수 있습니다.

   결과 집합에 애플리케이션 로직별로 필터링된 행이 포함되어 있는 경우 `WHERE` 절에 조건을 더 추가하여 결과 집합에서 해당 행을 제거할 수 있습니다.
+  결과 집합에서 열의 수를 줄입니다. 쿼리의 선택 목록에서 항목을 제거하면 됩니다.
+  쿼리에서 열 별칭을 사용하여 열 레이블을 줄입니다. 각 열 이름은 결과 집합의 각 행에 대한 JSON 문자열에서 반복됩니다. 따라서 열 이름이 길고 행이 많은 쿼리 결과는 크기 제한을 초과할 수 있습니다. 특히 복잡한 표현식에 열 별칭을 사용하면 JSON 문자열에서 전체 표현식이 반복되는 것을 방지할 수 있습니다.
+  SQL을 사용하면 열 별칭을 사용하여 동일한 이름의 열이 두 개 이상 있는 결과 집합을 생성할 수 있지만 JSON에서는 중복 키 이름이 허용되지 않습니다. 결과 집합을 JSON 형식으로 요청하고 둘 이상의 열의 이름이 같은 경우 RDS Data API에서 오류를 반환합니다. 따라서 모든 열 레이블의 이름이 고유하도록 해야 합니다.

## 예시
<a name="data-api-json-examples"></a>

 다음 Java 예제는 응답이 JSON 형식의 문자열인 `ExecuteStatement`를 호출한 후 결과 집합을 해석하는 방법을 보여줍니다. *databaseName*, *secretStoreArn* 및 *clusterArn* 파라미터를 적절한 값으로 대체합니다.

 다음 Java 예제에서는 결과 집합에서 소수 값을 반환하는 쿼리를 보여줍니다. `assertThat` 호출은 응답 필드에 JSON 결과 집합에 대한 규칙을 기반으로 예상되는 속성이 있는지 테스트합니다.

 이 예제는 다음 스키마 및 샘플 데이터에서 작동합니다.

```
create table test_simplified_json (a float);
insert into test_simplified_json values(10.0);
```

```
public void JSON_result_set_demo() {
    var sql = "select * from test_simplified_json";
    var request = new ExecuteStatementRequest()
      .withDatabase(databaseName)
      .withSecretArn(secretStoreArn)
      .withResourceArn(clusterArn)
      .withSql(sql)
      .withFormatRecordsAs(RecordsFormatType.JSON);
    var result = rdsdataClient.executeStatement(request);
}
```

 이전 프로그램의 `formattedRecords` 필드 값은 다음과 같습니다.

```
[{"a":10.0}]
```

 JSON 결과 집합이 있기 때문에 응답의 `Records` 및 `ColumnMetadata` 필드는 모두 Null입니다.

 다음 Java 예제에서는 결과 집합에서 정수 값을 반환하는 쿼리를 보여줍니다. 예제는 `getFormattedRecords`를 호출하여 JSON 형식의 문자열만 반환하고 비어 있거나 Null인 다른 응답 필드는 무시합니다. 이 예제에서는 결과를 레코드 목록을 나타내는 구조로 역직렬화합니다. 각 레코드에는 이름이 결과 집합의 열 별칭에 해당하는 필드가 있습니다. 이 기법은 결과 집합을 구문 분석하는 코드를 단순화합니다. 애플리케이션에서 결과 집합의 행과 열을 반복하여 각 값을 적절한 유형으로 변환할 필요가 없습니다.

 이 예제는 다음 스키마 및 샘플 데이터에서 작동합니다.

```
create table test_simplified_json (a int);
insert into test_simplified_json values(17);
```

```
public void JSON_deserialization_demo() {
    var sql = "select * from test_simplified_json";
    var request = new ExecuteStatementRequest()
      .withDatabase(databaseName)
      .withSecretArn(secretStoreArn)
      .withResourceArn(clusterArn)
      .withSql(sql)
      .withFormatRecordsAs(RecordsFormatType.JSON);
    var result = rdsdataClient.executeStatement(request)
      .getFormattedRecords();

/* Turn the result set into a Java object, a list of records.
   Each record has a field 'a' corresponding to the column
   labelled 'a' in the result set. */
    private static class Record { public int a; }
    var recordsList = new ObjectMapper().readValue(
        response, new TypeReference<List<Record>>() {
        });
}
```

 이전 프로그램의 `formattedRecords` 필드 값은 다음과 같습니다.

```
[{"a":17}]
```

 결과 행 0의 `a` 열을 검색하려면 애플리케이션은 `recordsList.get(0).a`를 참조합니다.

 반대로 다음 Java 예제에서는 JSON 형식을 사용하지 않을 때 결과 집합을 포함하는 데이터 구조를 구성하는 데 필요한 코드 종류를 보여줍니다. 이 경우 결과 집합의 각 행에는 단일 사용자에 대한 정보가 포함된 필드가 포함됩니다. 결과 집합을 나타내는 데이터 구조를 구축하려면 행을 반복해야 합니다. 각 행에 대해 코드는 각 필드의 값을 검색하고 적절한 형식 변환을 수행하며 행을 나타내는 객체의 해당 필드에 결과를 할당합니다. 그런 다음 코드는 각 사용자를 나타내는 객체를 전체 결과 집합을 나타내는 데이터 구조에 추가합니다. 결과 집합에서 필드를 재정렬, 추가 또는 제거하도록 쿼리가 변경된 경우 애플리케이션 코드도 변경해야 합니다.

```
/* Verbose result-parsing code that doesn't use the JSON result set format */
for (var row: response.getRecords()) {
    var user = User.builder()
      .userId(row.get(0).getLongValue())
      .firstName(row.get(1).getStringValue())
      .lastName(row.get(2).getStringValue())
      .dob(Instant.parse(row.get(3).getStringValue()))
      .build();
    result.add(user);
  }
```

 다음 샘플 값은 열, 열 별칭 및 열 데이터 유형이 서로 다른 결과 집합에 대한 `formattedRecords` 필드의 값을 보여줍니다.

 결과 집합에 여러 행이 포함된 경우 각 행은 배열 요소인 객체로 표시됩니다. 결과 집합의 각 열은 객체 내에서 키가 됩니다. 키는 결과 집합의 각 행에서 반복됩니다. 따라서 여러 행과 열로 구성된 결과 집합의 경우 전체 응답의 길이 제한을 초과하지 않도록 짧은 열 별칭을 정의해야 할 수 있습니다.

 이 예제는 다음 스키마 및 샘플 데이터에서 작동합니다.

```
create table sample_names (id int, name varchar(128));
insert into sample_names values (0, "Jane"), (1, "Mohan"), (2, "Maria"), (3, "Bruce"), (4, "Jasmine");
```

```
[{"id":0,"name":"Jane"},{"id":1,"name":"Mohan"},
{"id":2,"name":"Maria"},{"id":3,"name":"Bruce"},{"id":4,"name":"Jasmine"}]
```

 결과 집합의 열이 표현식으로 정의된 경우 표현식의 텍스트가 JSON 키가 됩니다. 따라서 일반적으로 쿼리의 선택 목록에서 각 표현식에 대한 설명 열 별칭을 정의하는 것이 편리합니다. 예를 들어, 다음 쿼리는 선택 목록에 함수 호출 및 산술 연산과 같은 표현식을 포함합니다.

```
select count(*), max(id), 4+7 from sample_names;
```

 이러한 표현식은 키로 JSON 결과 집합을 통해 전달됩니다.

```
[{"count(*)":5,"max(id)":4,"4+7":11}]
```

 설명 레이블이 있는 `AS` 열을 추가하면 JSON 결과 집합에서 키를 보다 간단하게 해석할 수 있습니다.

```
select count(*) as rows, max(id) as largest_id, 4+7 as addition_result from sample_names;
```

 수정된 SQL 쿼리의 경우, `AS` 절에 의해 정의된 열 레이블이 키 이름으로 사용됩니다.

```
[{"rows":5,"largest_id":4,"addition_result":11}]
```

 JSON 문자열의 각 키 값 쌍의 값은 따옴표로 묶인 문자열일 수 있습니다. 문자열에는 유니코드 문자가 포함될 수 있습니다. 문자열에 이스케이프 시퀀스가 포함되어 있거나 `"` 또는 `\`문자가 포함되어 있는 경우 이러한 문자 앞에는 백슬래시 이스케이프 문자가 있습니다. 다음 JSON 문자열의 예제는 이러한 가능성을 보여줍니다. 예를 들어 `string_with_escape_sequences` 결과에는 특수 문자 백스페이스, 줄 바꿈, 캐리지 리턴, 탭, 양식 피드 및 `\`가 포함됩니다.

```
[{"quoted_string":"hello"}]
[{"unicode_string":"邓不利多"}]
[{"string_with_escape_sequences":"\b \n \r \t \f \\ '"}]
```

 JSON 문자열의 각 키 값 쌍의 값은 숫자를 나타낼 수도 있습니다. 숫자는 정수, 부동 소수점 값, 음수 값 또는 지수 표기법으로 표현되는 값일 수 있습니다. 다음 JSON 문자열의 예제는 이러한 가능성을 보여줍니다.

```
[{"integer_value":17}]
[{"float_value":10.0}]
[{"negative_value":-9223372036854775808,"positive_value":9223372036854775807}]
[{"very_small_floating_point_value":4.9E-324,"very_large_floating_point_value":1.7976931348623157E308}]
```

 부울 값과 Null 값은 따옴표 없는 특수 키워드 `true`, `false` 및 `null`로 표현됩니다. 다음 JSON 문자열의 예제는 이러한 가능성을 보여줍니다.

```
[{"boolean_value_1":true,"boolean_value_2":false}]
[{"unknown_value":null}]
```

 BLOB 유형의 값을 선택하면 결과가 JSON 문자열에서 base64로 인코딩된 값으로 표시됩니다. 값을 원래 표현으로 다시 변환하려면 애플리케이션의 언어로 적절한 디코딩 기능을 사용할 수 있습니다. 예를 들어 Java에서는 `Base64.getDecoder().decode()` 함수를 호출합니다. 다음 샘플 출력은 `hello world`의 BLOB 값을 선택하고 결과 집합을 JSON 문자열로 반환한 것을 보여줍니다.

```
[{"blob_column":"aGVsbG8gd29ybGQ="}]
```

 다음 Python 예제는 Python `execute_statement` 함수에 대한 호출의 결과에서 값에 액세스하는 방법을 보여줍니다. 결과 집합은 필드 `response['formattedRecords']`의 문자열 값입니다. 이 코드는 `json.loads` 함수를 호출하여 JSON 문자열을 데이터 구조로 변환합니다. 그러면 결과 집합의 각 행은 데이터 구조 내의 목록 요소이며 각 행 내에서 이름으로 결과 집합의 각 필드를 참조할 수 있습니다.

```
import json

result = json.loads(response['formattedRecords'])
print (result[0]["id"])
```

 다음 JavaScript 예제는 JavaScript `executeStatement` 함수에 대한 호출의 결과에서 값에 액세스하는 방법을 보여줍니다. 결과 집합은 필드 `response.formattedRecords`의 문자열 값입니다. 이 코드는 `JSON.parse` 함수를 호출하여 JSON 문자열을 데이터 구조로 변환합니다. 그러면 결과 집합의 각 행은 데이터 구조 내의 배열 요소이며 각 행 내에서 이름으로 결과 집합의 각 필드를 참조할 수 있습니다.

```
<script>
    const result = JSON.parse(response.formattedRecords);
    document.getElementById("display").innerHTML = result[0].id;
</script>
```