

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# 에서 Aurora Serverless v2 사용 AWS AppSync
<a name="tutorial-rds-resolvers"></a>

를 사용하여 GraphQL API를 Aurora Serverless 데이터베이스에 연결합니다 AWS AppSync. 이러한 통합 시 GraphQL 쿼리, 변형 및 구독을 통해 SQL 문을 실행할 수 있으므로 관계형 데이터와 유연하게 상호 작용할 수 있습니다.

**참고**  
이 자습서에서는 `US-EAST-1` 리전을 사용합니다.

**이점**
+ GraphQL과 관계형 데이터베이스 간의 원활한 통합
+ GraphQL 인터페이스를 통해 SQL 작업을 수행하는 기능
+ Aurora Serverless v2를 사용한 서버리스 확장성
+  AWS Secrets Manager를 통한 보안 데이터 액세스
+ 입력 삭제를 통한 SQL 인젝션 방지
+ 필터링 및 범위 작업을 포함한 유연한 쿼리 기능

**일반 사용 사례**
+ 관계형 데이터 요구 사항을 사용하여 확장 가능한 애플리케이션 구축
+ GraphQL 유연성과 SQL 데이터베이스 기능이 모두 필요한 API 생성
+ GraphQL 변형 및 쿼리를 통한 데이터 작업 관리
+ 보안 데이터베이스 액세스 패턴 구현

이 자습서에서는 다음을 배웁니다.
+ Aurora Serverless v2 클러스터 설정
+ 데이터 API 기능 활성화
+ 데이터베이스 구조 생성 및 구성
+ 데이터베이스 작업에 대한 GraphQL 스키마 정의
+ 쿼리 및 변형에 대한 해석기 구현
+ 적절한 입력 삭제를 통해 데이터 액세스 보호
+ GraphQL 인터페이스를 통해 다양한 데이터베이스 작업 실행

**Topics**
+ [데이터베이스 클러스터 설정](#create-cluster)
+ [데이터 API 활성화](#enable-data-api)
+ [데이터베이스 및 테이블 생성](#create-database-and-table)
+ [GraphQL 스키마](#graphql-schema)
+ [API를 데이터베이스 작업에 연결](#configuring-resolvers)
+ [API를 통해 데이터 수정](#run-mutations)
+ [데이터 검색](#run-queries)
+ [데이터 액세스 보호](#input-sanitization)

## 데이터베이스 클러스터 설정
<a name="create-cluster"></a>

Amazon RDS 데이터 소스를에 추가 AWS AppSync하기 전에 먼저 Aurora Serverless v2 클러스터에서 데이터 API를 활성화하고 **를 사용하여 보안 암호를 구성**해야 합니다*AWS Secrets Manager*. Aurora Serverless v2 클러스터를 생성하기 위해 AWS CLI를 사용할 수 있습니다.

```
aws rds create-db-cluster \
    --db-cluster-identifier appsync-tutorial \
    --engine aurora-mysql \
    --engine-version 8.0 \
    --serverless-v2-scaling-configuration MinCapacity=0,MaxCapacity=1 \
    --master-username USERNAME \
    --master-user-password COMPLEX_PASSWORD \
    --enable-http-endpoint
```

그러면 클러스터에 대한 ARN이 반환됩니다.

클러스터를 생성한 후 다음 명령을 사용하여 Aurora Serverless v2 인스턴스를 추가해야 합니다.

```
aws rds create-db-instance \
    --db-cluster-identifier appsync-tutorial \
    --db-instance-identifier appsync-tutorial-instance-1 \
    --db-instance-class db.serverless \
    --engine aurora-mysql
```

**참고**  
이러한 엔드포인트를 활성화하는 데 시간이 걸립니다. 클러스터의 **연결 및 보안** 탭에 있는 Amazon RDS 콘솔에서 상태를 확인할 수 있습니다. 다음 AWS CLI 명령을 사용하여 클러스터의 상태를 확인할 수도 있습니다.  

```
aws rds describe-db-clusters \
    --db-cluster-identifier appsync-tutorial \
    --query "DBClusters[0].Status"
```

 AWS Secrets Manager 콘솔을 사용하여 *보안* 암호를 생성하거나 AWS CLI 이전 단계의 `USERNAME` 및를 사용하여 다음과 같은 입력 파일을 사용하여 보안 암호를 생성할 수 `COMPLEX_PASSWORD` 있습니다.

```
{
    "username": "USERNAME",
    "password": "COMPLEX_PASSWORD"
}
```

이를 파라미터로 AWS CLI에 전달합니다.

```
aws secretsmanager create-secret --name HttpRDSSecret --secret-string file://creds.json --region us-east-1
```

그러면 암호에 대한 ARN이 반환됩니다.

 데이터 원본을 생성할 때 AppSync 콘솔에서 나중에 사용할 수 있도록 암호화 Aurora Serverless 클러스터의 **ARN을 적어 둡니다**.

## 데이터 API 활성화
<a name="enable-data-api"></a>

[RDS 문서의 지침에 따라](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/data-api.html) 클러스터에 대한 데이터 API를 활성화할 수 있습니다. AppSync 데이터 소스로 추가하기 전에 데이터 API를 활성화해야 합니다.

## 데이터베이스 및 테이블 생성
<a name="create-database-and-table"></a>

데이터 API를 활성화하면 AWS CLI에서 데이터 API가 `aws rds-data execute-statement` 명령과 작동하는지 확인할 수 있습니다. 그러면 AppSync API에 추가하기 전에 Aurora Serverless 클러스터가 올바르게 구성되도록 할 수 있습니다. 먼저 다음과 같이 `--sql` 파라미터를 사용하여 *TESTDB*라는 데이터베이스를 생성합니다.

```
aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:123456789000:cluster:http-endpoint-test" \
--schema "mysql"  --secret-arn "arn:aws:secretsmanager:us-east-1:123456789000:secret:testHttp2-AmNvc1"  \
--region us-east-1 --sql "create DATABASE TESTDB"
```

오류 없이 실행되면 *create table* 명령을 사용하여 테이블을 추가합니다.

```
aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:123456789000:cluster:http-endpoint-test" \
 --schema "mysql"  --secret-arn "arn:aws:secretsmanager:us-east-1:123456789000:secret:testHttp2-AmNvc1" \
 --region us-east-1 \
 --sql "create table Pets(id varchar(200), type varchar(200), price float)" --database "TESTDB"
```

모든 것이 문제 없이 실행되면 AppSync API에서 해당 클러스터를 데이터 소스로 추가하는 단계로 진행할 수 있습니다.

## GraphQL 스키마
<a name="graphql-schema"></a>

Aurora Serverless 데이터 API가 테이블과 함께 실행 중이므로 변형 및 구독을 수행하기 위해 GraphQL 스키마를 생성하고 해석기를 연결해 보겠습니다. AWS AppSync 콘솔에서 새 API를 생성하고 **스키마** 페이지로 이동하여 다음을 입력합니다.

```
type Mutation {
    createPet(input: CreatePetInput!): Pet
    updatePet(input: UpdatePetInput!): Pet
    deletePet(input: DeletePetInput!): Pet
}

input CreatePetInput {
    type: PetType
    price: Float!
}

input UpdatePetInput {
id: ID!
    type: PetType
    price: Float!
}

input DeletePetInput {
    id: ID!
}

type Pet {
    id: ID!
    type: PetType
    price: Float
}

enum PetType {
    dog
    cat
    fish
    bird
    gecko
}

type Query {
    getPet(id: ID!): Pet
    listPets: [Pet]
    listPetsByPriceRange(min: Float, max: Float): [Pet]
}

schema {
    query: Query
    mutation: Mutation
}
```

 스키마를 **저장**하고 **데이터 원본** 페이지로 이동해 새 데이터 원본을 생성합니다. 데이터 원본 유형으로 **관계형 데이터베이스**를 선택하고 기억하기 쉬운 이름을 입력합니다. 마지막 단계에서 생성한 데이터베이스 이름과 해당 데이터베이스를 생성한 **클러스터 ARN**을 사용합니다. **역할**의 경우 AppSync에서 새 역할을 생성하거나 아래와 유사한 정책을 사용해 역할을 하나 생성하도록 할 수 있습니다.

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "rds-data:BatchExecuteStatement",
                "rds-data:BeginTransaction",
                "rds-data:CommitTransaction",
                "rds-data:ExecuteStatement",
                "rds-data:RollbackTransaction"
            ],
            "Resource": [
                "arn:aws:rds:us-east-1:111122223333:cluster:mydbcluster",
                "arn:aws:rds:us-east-1:111122223333:cluster:mydbcluster:*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue"
            ],
            "Resource": [
            "arn:aws:secretsmanager:us-east-1:111122223333:secret:mysecret",
            "arn:aws:secretsmanager:us-east-1:111122223333:secret:mysecret:*"
            ]
        }
    ]
}
```

------

이 정책에는 역할 액세스 권한을 부여하는 **명령문**이 2개 있습니다. 첫 번째 **리소스**는 Aurora Serverless 클러스터이고 두 번째 리소스는 AWS Secrets Manager ARN입니다. **생성**을 클릭하기 전에 AppSync 데이터 소스 구성에서 두 ARN을 **모두** 제공해야 합니다.

이를 파라미터로에 전달합니다 AWS CLI.

```
aws secretsmanager create-secret \
  --name HttpRDSSecret \
  --secret-string file://creds.json \
  --region us-east-1
```

그러면 암호에 대한 ARN이 반환됩니다. AWS AppSync 콘솔에서 데이터 소스를 생성할 때 나중에 사용할 수 있도록 Aurora Serverless 클러스터의 ARN과 보안 암호를 기록해 둡니다.

### 데이터베이스 구조 구축
<a name="create-database-and-table"></a>

데이터 API를 활성화하면 AWS CLI에서 데이터 API가 `aws rds-data execute-statement` 명령과 작동하는지 확인할 수 있습니다. 이렇게 하면 AWS AppSync API에 추가하기 전에 Aurora Serverless v2 클러스터가 올바르게 구성됩니다. 먼저 다음과 같이 `--sql` 파라미터를 사용하여 *TESTDB*라는 데이터베이스를 생성합니다.

```
aws rds-data execute-statement \
                --resource-arn "arn:aws:rds:us-east-1:111122223333:cluster:appsync-tutorial" \
                --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333:secret:appsync-tutorial-rds-secret"  \
                --region us-east-1 \
                --sql "create DATABASE TESTDB"
```

오류 없이 실행되면 다음 *create table* 명령을 사용하여 테이블을 추가합니다.

```
aws rds-data execute-statement \
      --resource-arn "arn:aws:rds:us-east-1:111122223333:cluster:http-endpoint-test" \
      --secret-arn "arn:aws:secretsmanager:us-east-1:111122223333:secret:testHttp2-AmNvc1" \
      --region us-east-1 \
      --sql "create table Pets(id varchar(200), type varchar(200), price float)" \
      --database "TESTDB"
```

### API 인터페이스 설계
<a name="graphql-schema"></a>

Aurora Serverless v2 데이터 API가 테이블과 함께 실행된 후에는 변형 및 구독을 수행하기 위해 GraphQL 스키마를 생성하고 해석기를 연결합니다. AWS AppSync 콘솔에서 새 API를 생성하고 콘솔의 **스키마** 페이지로 이동하여 다음을 입력합니다.

```
type Mutation {
        createPet(input: CreatePetInput!): Pet
        updatePet(input: UpdatePetInput!): Pet
        deletePet(input: DeletePetInput!): Pet
    }
    
    input CreatePetInput {
        type: PetType
        price: Float!
    }
    
    input UpdatePetInput {
        id: ID!
        type: PetType
        price: Float!
    }
    
    input DeletePetInput {
        id: ID!
    }
    
    type Pet {
        id: ID!
        type: PetType
        price: Float
    }
    
    enum PetType {
        dog
        cat
        fish
        bird
        gecko
    }
    
    type Query {
        getPet(id: ID!): Pet
        listPets: [Pet]
        listPetsByPriceRange(min: Float, max: Float): [Pet]
    }
    
    schema {
        query: Query
        mutation: Mutation
    }
```

 스키마를 **저장**하고 **데이터 원본** 페이지로 이동해 새 데이터 원본을 생성합니다. **데이터 소스** 유형으로 **관계형 데이터베이스**를 선택하고 기억하기 쉬운 이름을 입력합니다. 마지막 단계에서 생성한 데이터베이스 이름과 해당 데이터베이스를 생성한 **클러스터 ARN**을 사용합니다. **역할**의 경우 새 역할을 AWS AppSync 생성하거나 다음과 유사한 정책을 사용하여 역할을 생성할 수 있습니다.

------
#### [ JSON ]

****  

```
{
        "Version":"2012-10-17",		 	 	 
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "rds-data:BatchExecuteStatement",
                    "rds-data:BeginTransaction",
                    "rds-data:CommitTransaction",
                    "rds-data:ExecuteStatement",
                    "rds-data:RollbackTransaction"
                ],
                "Resource": [
                    "arn:aws:rds:us-east-1:111122223333:cluster:mydbcluster",
                    "arn:aws:rds:us-east-1:111122223333:cluster:mydbcluster:*"
                ]
            },
            {
                "Effect": "Allow",
                "Action": [
                    "secretsmanager:GetSecretValue"
                ],
                "Resource": [
                "arn:aws:secretsmanager:us-east-1:111122223333:secret:mysecret",
                "arn:aws:secretsmanager:us-east-1:111122223333:secret:mysecret:*"
                ]
            }
        ]
    }
```

------

이 정책에는 역할 액세스 권한을 부여하는 **명령문**이 2개 있습니다. 첫 번째 **리소스**는 Aurora Serverless v2 클러스터이고 두 번째 리소스는 AWS Secrets Manager ARN입니다. **생성을** 클릭하기 전에 AWS AppSync 데이터 소스 구성에 **두** ARNs을 모두 제공해야 합니다.

## API를 데이터베이스 작업에 연결
<a name="configuring-resolvers"></a>

이제 유효한 GraphQL 스키마와 RDS 데이터 소스가 있으므로 해석기를 스키마에 대한 GraphQL 필드에 연결할 수 있습니다. API는 다음과 같은 기능을 제공합니다.

1. *Mutation.createPet* 필드를 통해 애완 동물 생성

1. *Mutation.updatePet* 필드를 통해 애완 동물 업데이트

1. *Mutation.deletePet* 필드를 통해 애완 동물 삭제

1. *Query.getPet* 필드를 통해 단일 애완 동물 가져오기

1. *Query.listPets* 필드를 통해 모든 애완 동물 나열

1. *Query.listPetsByPriceRange* 필드를 통해 가격 범위 내 애완 동물 나열

### Mutation.createPet
<a name="mutation-createpet"></a>

 AWS AppSync 콘솔의 스키마 편집기에서 오른쪽에서 용 **해석기 연결을** 선택합니다`createPet(input: CreatePetInput!): Pet`. RDS 데이터 소스를 선택합니다. **요청 매핑 템플릿** 섹션에 다음 템플릿을 추가합니다.

```
#set($id=$utils.autoId())
{
"version": "2018-05-29",
    "statements": [
        "insert into Pets VALUES (:ID, :TYPE, :PRICE)",
        "select * from Pets WHERE id = :ID"
    ],
    "variableMap": {
        ":ID": "$ctx.args.input.id",
        ":TYPE": $util.toJson($ctx.args.input.type),
        ":PRICE": $util.toJson($ctx.args.input.price)
    }
}
```

시스템이 **설명문** 배열의 순서에 따라 SQL 문을 순차적으로 실행합니다. 결과 역시 동일한 순서로 반환됩니다. 이것은 변형이므로, *삽입* 후에 *select* 문을 실행하여 커밋된 값을 검색하여 GraphQL 응답 매핑 템플릿을 채웁니다.

**응답 매핑 템플릿** 섹션에 다음 템플릿을 추가합니다.

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])
```

*명령문*에 SQL 쿼리가 2개 있기 때문에 `$utils.rds.toJsonString($ctx.result))[1][0])`을 사용하여 데이터베이스에서 반환되는 매트릭스에서 두 번째 결과를 지정해야 합니다.

### Mutation.updatePet
<a name="mutation-updatepet"></a>

 AWS AppSync 콘솔의 스키마 편집기에서에 대한 **해석기 연결을** 선택합니다`updatePet(input: UpdatePetInput!): Pet`. **RDS 데이터 소스**를 선택합니다. **요청 매핑 템플릿** 섹션에 다음 템플릿을 추가합니다.

```
{
"version": "2018-05-29",
    "statements": [
        $util.toJson("update Pets set type=:TYPE, price=:PRICE WHERE id=:ID"),
        $util.toJson("select * from Pets WHERE id = :ID")
    ],
    "variableMap": {
        ":ID": "$ctx.args.input.id",
        ":TYPE": $util.toJson($ctx.args.input.type),
        ":PRICE": $util.toJson($ctx.args.input.price)
    }
}
```

**응답 매핑 템플릿** 섹션에 다음 템플릿을 추가합니다.

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[1][0])
```

### Mutation.deletePet
<a name="mutation-deletepet"></a>

 AWS AppSync 콘솔의 스키마 편집기에서에 대한 **해석기 연결을** 선택합니다`deletePet(input: DeletePetInput!): Pet`. **RDS 데이터 소스**를 선택합니다. **요청 매핑 템플릿** 섹션에 다음 템플릿을 추가합니다.

```
{
"version": "2018-05-29",
    "statements": [
        $util.toJson("select * from Pets WHERE id=:ID"),
        $util.toJson("delete from Pets WHERE id=:ID")
    ],
    "variableMap": {
        ":ID": "$ctx.args.input.id"
    }
}
```

**응답 매핑 템플릿** 섹션에 다음 템플릿을 추가합니다.

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])
```

### Query.getPet
<a name="query-getpet"></a>

이제 스키마에 대한 변형이 생성되었으므로 개별 항목을 가져와 나열하고 SQL 필터링을 적용하는 방법을 보여주기 위해 쿼리 3개를 연결합니다. AWS AppSync 콘솔의 **스키마 편집기**에서에 대한 **해석기 연결을** 선택합니다`getPet(id: ID!): Pet`. **RDS 데이터 소스**를 선택합니다. **요청 매핑 템플릿** 섹션에 다음 템플릿을 추가합니다.

```
{
"version": "2018-05-29",
        "statements": [
            $util.toJson("select * from Pets WHERE id=:ID")
    ],
    "variableMap": {
        ":ID": "$ctx.args.id"
    }
}
```

**응답 매핑 템플릿** 섹션에 다음 템플릿을 추가합니다.

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0][0])
```

### Query.listPets
<a name="query-listpets"></a>

 AWS AppSync 콘솔의 스키마 편집기에서 오른쪽에서 용 **해석기 연결을** 선택합니다`getPet(id: ID!): Pet`. **RDS 데이터 소스**를 선택합니다. **요청 매핑 템플릿** 섹션에 다음 템플릿을 추가합니다.

```
{
    "version": "2018-05-29",
    "statements": [
        "select * from Pets"
    ]
}
```

**응답 매핑 템플릿** 섹션에 다음 템플릿을 추가합니다.

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])
```

### Query.listPetsByPriceRange
<a name="query-listpetsbypricerange"></a>

 AWS AppSync 콘솔의 스키마 편집기에서 오른쪽에서 용 **해석기 연결을** 선택합니다`getPet(id: ID!): Pet`. **RDS 데이터 소스**를 선택합니다. **요청 매핑 템플릿** 섹션에 다음 템플릿을 추가합니다.

```
{
    "version": "2018-05-29",
    "statements": [
            "select * from Pets where price > :MIN and price < :MAX"
    ],

    "variableMap": {
        ":MAX": $util.toJson($ctx.args.max),
        ":MIN": $util.toJson($ctx.args.min)
    }
}
```

**응답 매핑 템플릿** 섹션에 다음 템플릿을 추가합니다.

```
$utils.toJson($utils.rds.toJsonObject($ctx.result)[0])
```

## API를 통해 데이터 수정
<a name="run-mutations"></a>

SQL 문을 사용하여 모든 해석기를 구성했고 Serverless Aurora 데이터 API에 GraphQL API를 연결했으므로 변형 및 쿼리 수행을 시작할 수 있습니다. AWS AppSync 콘솔에서 **쿼리** 탭을 선택하고 다음을 입력하여 Pet를 생성합니다.

```
mutation add {
    createPet(input : { type:fish, price:10.0 }){
        id
        type
        price
    }
}
```

응답에는 다음과 같이 *id*, *유형* 및 *가격*이 포함되어 있어야 합니다.

```
{
  "data": {
    "createPet": {
      "id": "c6fedbbe-57ad-4da3-860a-ffe8d039882a",
      "type": "fish",
      "price": "10.0"
    }
  }
}
```

*updatePet* 변형을 실행하여 항목을 수정할 수 있습니다.

```
mutation update {
    updatePet(input : {
        id: ID_PLACEHOLDER,
        type:bird,
        price:50.0
    }){
        id
        type
        price
    }
}
```

참고로 앞서 *createPet* 작업에서 반환된 *id*를 사용했습니다. 해석기에서 `$util.autoId()`를 사용했기 때문에 이 ID는 레코드에 대해 고유한 값이 될 것입니다. 레코드는 다음과 같이 유사한 방식으로 삭제할 수 있습니다.

```
mutation delete {
    deletePet(input : {id:ID_PLACEHOLDER}){
        id
        type
        price
    }
}
```

첫 번째 변형을 사용해 *가격*에 대한 값을 다르게 해 레코드를 몇 개 생성한 다음 쿼리를 실행합니다.

## 데이터 검색
<a name="run-queries"></a>

콘솔의 **쿼리** 탭에서 다음 문을 사용해 생성된 레코드를 모두 나열합니다.

```
query allpets {
    listPets {
        id
        type
        price
    }
}
```

다음 GraphQL 쿼리를 사용하여 *Query.listPetsByPriceRange*에 대한 매핑 템플릿에 `where price > :MIN and price < :MAX`가 있는 SQL *WHERE* 조건자를 활용합니다.

```
query petsByPriceRange {
    listPetsByPriceRange(min:1, max:11) {
        id
        type
        price
    }
}
```

*가격*이 \$11 이상이거나 \$110 미만인 레코드만 표시되어야 합니다. 마지막으로, 다음과 같이 개별 레코드를 검색하는 쿼리를 수행할 수 있습니다.

```
query onePet {
    getPet(id:ID_PLACEHOLDER){
        id
        type
        price
    }
}
```

## 데이터 액세스 보호
<a name="input-sanitization"></a>

SQL 인젝션은 데이터베이스 애플리케이션의 보안 취약성입니다. 공격자가 사용자 입력 필드를 통해 악성 SQL 코드를 삽입할 때 발생합니다. 이는 데이터베이스 데이터에 대한 무단 액세스를 허용할 수 있습니다. SQL 인젝션 공격으로부터 보호하기 위해 `variableMap`을 사용하여 처리하기 전에 모든 사용자 입력을 주의 깊게 검증하고 삭제하는 것이 좋습니다. 변수 맵을 사용하지 않는 경우 GraphQL 작업의 인수를 삭제해야 합니다. 폐기 방법 중 하나는 데이터 API에 대해 SQL 문을 실행하기 전에 요청 매핑 템플릿에 입력 관련 확인 단계를 제공하는 것입니다. `listPetsByPriceRange` 예제의 요청 매핑 템플릿을 수정하는 방법을 살펴보겠습니다. 전적으로 사용자 입력에만 의존하는 대신 다음과 같이 수행할 수 있습니다.

```
#set($validMaxPrice = $util.matches("\d{1,3}[,\\.]?(\\d{1,2})?",$ctx.args.maxPrice))

#set($validMinPrice = $util.matches("\d{1,3}[,\\.]?(\\d{1,2})?",$ctx.args.minPrice))


#if (!$validMaxPrice || !$validMinPrice)
    $util.error("Provided price input is not valid.")
#end
{
    "version": "2018-05-29",
    "statements": [
            "select * from Pets where price > :MIN and price < :MAX"
    ],

    "variableMap": {
        ":MAX": $util.toJson($ctx.args.maxPrice),
        ":MIN": $util.toJson($ctx.args.minPrice)
    }
}
```

데이터 API에 대해 해석기를 실행할 때 무단 입력을 방지하기 위한 또 다른 방법은 저장 프로시저 및 파라미터화된 입력과 함께 준비된 명령문을 사용하는 것입니다. 예를 들어, `listPets`에 대한 해석기에서 준비된 명령문으로 *select*를 실행하는 다음 프로시저를 정의합니다.

```
CREATE PROCEDURE listPets (IN type_param VARCHAR(200))
  BEGIN
     PREPARE stmt FROM 'SELECT * FROM Pets where type=?';
     SET @type = type_param;
     EXECUTE stmt USING @type;
     DEALLOCATE PREPARE stmt;
  END
```

Aurora Serverless v2 인스턴스에서 이것을 생성합니다.

```
aws rds-data execute-statement --resource-arn "arn:aws:rds:us-east-1:xxxxxxxxxxxx:cluster:http-endpoint-test" \
--schema "mysql"  --secret-arn "arn:aws:secretsmanager:us-east-1:xxxxxxxxxxxx:secret:httpendpoint-xxxxxx"  \
--region us-east-1  --database "DB_NAME" \
--sql "CREATE PROCEDURE listPets (IN type_param VARCHAR(200)) BEGIN PREPARE stmt FROM 'SELECT * FROM Pets where type=?'; SET @type = type_param; EXECUTE stmt USING @type; DEALLOCATE PREPARE stmt; END"
```

이제 저장 프로시저만 호출하면 되므로 결과적으로 listPets에 대한 해석기 코드가 간단해졌습니다. 최소한 문자열 입력에서는 작은 따옴표를 [이스케이프](#escaped) 처리해야 합니다.

```
#set ($validType = $util.isString($ctx.args.type) && !$util.isNullOrBlank($ctx.args.type))
#if (!$validType)
    $util.error("Input for 'type' is not valid.", "ValidationError")
#end

{
    "version": "2018-05-29",
    "statements": [
        "CALL listPets(:type)"
    ]
    "variableMap": {
        ":type": $util.toJson($ctx.args.type.replace("'", "''"))
    }
}
```

### 이스케이프 문자열 사용
<a name="escaped"></a>

작은 따옴표는 SQL 문에서 문자열 리터럴의 시작과 끝을 나타냅니다(예: `'some string value'`). 문자열 내에서 하나 이상의 작은 따옴표 문자(`'`)가 있는 문자열 값을 사용할 수 있게 하려면 각 문자열 값을 두 개의 작은 따옴표(`''`)로 바꿔야 합니다. 예를 들어 입력 문자열이 `Nadia's dog`인 경우 SQL 문에 대해 이스케이프 처리합니다.

```
update Pets set type='Nadia''s dog' WHERE id='1'
```