API Gateway 데이터 매핑, 메소드 정의

읽기 전

  • 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다. (예시 코드는 works on my machine 상태라 다르게 작동될 수 있습니다.)
  • Udemy강의(AWS Serverless APIs & Apps - A Complete Introduction)를 기반으로 작성되었습니다. 필자의 프로젝트 요구사항에 맞춰 일부 변형하였으니 자세한 이해를 원하신다면 결제해서 수강해보시길 권장드립니다. 좋은 강의라고 생각합니다. 항상 할인 중이니 저렴한 가격에 줍줍하세요
  • API Gateway에서 Request/Response data를 매핑하고 데이터가 정상적으로 송수신되는 지 확인합니다. GET, DELETE 메소드를 정의합니다.

POST 메소드 Input Data 매핑하기

외부 클라이언트에서 전달받은 데이터를 Lambda가 기능하고자 하는 목적에 따라 매핑해야 하는 이유는 Input 데이터의 크기가 크고 전부를 Lambda에 전달할 필요가 없다면 선택적으로 데이터를 전달함이 효율적일 수 있기 때문이다. Lambda 함수의 기능이 정상적으로 작동하기 위해 필요한 데이터가 누락된 경우도 고려해야 한다.

예시로 특정인의 점수와 랭킹정보를 전달하는 POST 메소드가 있다고 하자. 각 방법에 대해 살펴보자.

Lambda에서 접근하여 처리

굳이 API Gateway에서 필터링하지 않고 Lambda 코드 상에서 접근하여 원하는 기능을 구현할 수 있긴하다. 데이터가 어떻게 반환되는지 확인을 위해 POST 메소드와 연결된 Lambda 함수에서 그대로 입력받은 데이터를 반환하게끔 코드를 수정한다. 그 뒤 API Gateway에서 POST 메소드 테스트 탭으로 들어가 그림과 같이 전송할 데이터를 입력하여 보내면 우측과 같이 리턴되었음을 확인할 수 있다. JSON 객체이지만 마치 python의 dict 자료형처럼 보인다.

AWS_Serverless_003_01

그렇다면 Lambda로 돌아가 각각에 대해 2를 곱한 값을 리턴하는 함수를 작성해보자.

import json

def lambda_handler(event, context):
    # TODO implement
    return {
        "mPrevScore": event['prevScore']*2,
        "mPrevRank": event['prevRank']*2,
        "mCurrScore": event['currScore']*2,
        "mCurrRank": event['currRank']*2
    }

AWS_Serverless_003_02

Lambda에선 dict처럼 쓰면 된다는 점을 확인하였다.

Integration Request(통합 요청)에서 매핑하기

Lambda에서 값에 접근하는 방법은 전체 Request 데이터가 함수로 전달된다. 만약 Lambda에서 요청 데이터의 일부만 추출하여 사용한다면 효율 면에서 조금 아깝다는 생각이 든다.

AWS_Serverless_003_03

AWS_Serverless_003_04

위 그림과 같이 'prevScore' 키에 대해서만 전달되게끔 요청 템플릿을 구성한 뒤 이전과 동일한 데이터를 넣고 테스팅을 하면 KeyError가 발생한다.

AWS_Serverless_003_05

전달한 데이터에 Lambda가 요구하는 값들 중 'prevScore' 키에 해당하는 값은 존재하지만 이후 처리하는 데이터인 'prevRank' 키가 없어 KeyError를 출력하였다. 이런 방식으로 매핑 템플릿을 정의하여 선택적인 데이터 추출이 가능하다.

Models(모델)을 생성하여 적용하기

Integration Request 매핑 템플릿에서 데이터를 매핑하는 작업은 즉각적으로 데이터를 추가/제거할 때 유용할 수 있지만 필요한 데이터가 모두 포함되었는지 검증하고자 한다면 모델의 생성을 고려해볼 수 있다.

좌측 메뉴의 모델 탭을 클릭하여 새로운 모델을 생성하자. 모델 생성은 JSON 스키마를 사용한다. 모델 내용은 이전, 현재 점수와 랭크 정보를 정수값으로 받고 누락된 경우 요청을 받지 않겠음을 의미한다.

AWS_Serverless_003_06

모델을 생성했으니 API에 적용해야 한다. 다시 POST 메소드로 접근하여 Method Request(메소드 요청) 탭을 클릭한다.

AWS_Serverless_003_07

AWS_Serverless_003_08

요청 본문을 클릭한 뒤 방금 생성한 모델을 적용하자. 그리고 상단의 요청 검사기(Request Validator)를 클릭하여 본문 검사(Validate Body)를 클릭한다. 여기까지 하면 API Gateway가 모델에서 required 처리된 Property들이 제대로 입력되었는지 검증해준다. 다른 방법도 있겠지만 이 방법이 제일 편하다고 생각한다.

AWS_Serverless_003_09

반드시 받아야할 데이터를 정의하였으니 Lambda 함수로 넘겨준 데이터를 다시 통합 요청(Integration Request) 탭에서 정의하자. 그림과 같이 템플릿 생성을 클릭하면 방금 적용했던 모델이 생겼음을 확인할 수 있고 클릭해보면 하단에 자동으로 매핑해준다. 그러나 고정값으로 매핑되어 있으므로 입력받을 변수로 교체해주자. 이전에는 path를 사용하여 입력받은 키값에 대해 값을 조회하였지만 모델을 적용하였으니 좀 더 아래와 같이 간편하게 조회할 수 있다.

AWS_Serverless_003_10

이제 다시 테스팅을 하면 정상적으로 출력됨을 확인할 수 있다. 누락된 경우 invalid request를 출력하고 정의하지 않은 데이터가 입력된다면 해당 데이터를 제외하고 Lambda에 전달한다.

AWS_Serverless_003_11

POST 메소드 Response Data 매핑하기

반대로 Lambda 함수에서 전달받은 데이터를 원하는 방식으로 매핑할 수 있다. 통합 응답(Integration Response) 탭의 매핑 템플릿에서 받고자 하는 형식으로 정의하자.

AWS_Serverless_003_12

그리고 다시 동일한 값으로 테스트를 해보면 방금 Integration Response에서 정의한 대로 Response 값이 반환됨을 확인할 수 있다.

AWS_Serverless_003_13

Lambda 함수에서 미리 클라이언트가 원하는 형식으로 데이터를 가공하였다면 굳이 Response까지 매핑할 필요는 없겠다.

DELETE, GET 메소드 정의하기

POST 메소드는 요청 데이터를 전송하여 DB에 입력하는 기능을 갖는다고 하면 DELETE와 GET은 DB 데이터를 삭제하거나 조회하는 기능을 해야하므로 DB 데이터가 있어야 한다. 그러나 지금은 아직 DB를 연걸한 상태가 아니기 때문에 연결되었다는 확인 메세지만 리턴하자.

각 메소드에 연결할 Lambda 함수를 정의해두자. 하나의 Lambda 함수로 통합할 수 있겠지만 직관성을 위해 기능별로 구분하여 작성하기로 하였다. 설명을 위해 DELETE 메소드는 basic-delete-data 함수, GET 메소드는 basic-get-data 함수와 연결하였다.

DELETE 메소드는 특정 UserId를 입력받아 레코드를 삭제하는 기능을, GET 메소드는 파라미터 값에 따라 전체/특정 데이터를 조회하는 기능을 한다고 가정하자.

DELETE 메소드 정의

  1. 미리 정의해둔 Lambda 함수(basic-delete-data)와 연결한다.
import json

def lambda_handler(event, context):
    # TODO implement
    return 'deleted UserId: ' + event['UserId']
  1. UserId 값을 입력받아 해당 Id에 해당하는 레코드를 삭제한다고 가정했기 때문에 미리 모델을 정의한다. 모델 생성 후 Method Request(메소드 요청), Integration Request(통합 요청) 탭에 가서 모델을 적용하고 함수에 넘겨줄 데이터 형식을 정의한다.

AWS_Serverless_003_14

AWS_Serverless_003_15

  1. 정상적으로 UserId가 전달되었음을 확인하였다.

AWS_Serverless_003_16

GET 메소드 정의

GET 메소드는 조금 다른 방식으로 정의하려 한다. 접근 URL의 파라미터를 전달하여 전체 데이터 혹은 특정 데이터를 조회할 것인지 결정하게끔 하자. 이전에 생성했던 'basic-api-res' 리소스 하단에 새로운 리소스를 생성한다. 차이점은 리소스 경로에 괄호를 추가하여 경로 파라미터로 사용할 것을 선언하였다.

AWS_Serverless_003_17

그리고 경로 리소스 하단에 GET 메소드를 생성한다.

AWS_Serverless_003_18

경로 파라미터 값에 따라 다른 기능을 수행하므로 type에 대한 분기문을 작성하였다.

import json

def lambda_handler(event, context):
    type = event['type']
    if type == 'all':
        return "you'll get all data"
    elif type == 'single':
        return "you'll get single data"
    else:
        return "OOPS! Invalid Path Params"

Integration Request(통합 요청)에 들어가서 Lambda 함수에 경로 파라미터 값을 넘겨주게끔 템플릿을 정의한다. 매핑 템플릿 액세스 로깅 변수 참조를 참고하여 'type' 파라미터에 접근한다. 파라미터 값에 따옴표로 감싼 이유는 Lambda에서 스트링 값으로 전달받아 비교하기로 했기에 스트링으로 변환하기 위해서다.

AWS_Serverless_003_19

외부에서 API 기능 여부 확인

각 리소스들을 클릭한 뒤 작업 버튼을 눌러서 CORS 활성화를 클릭하여 일괄적으로 헤더를 추가한 뒤 API를 배포함으로써 외부 접근이 가능하게끔 하자. 배포된 URL을 사용하여 외부에서 접근해보자.

AWS_Serverless_003_20

POST 메소드는 정상적으로 response 받음을 확인하였다. 코드에서 변경된 점은 Content-Type을 지정하고 JSON 객체를 스트링으로 변환한 것이다. 이전에 API 정의 시 모델의 타임을 application/json으로 지정했기 때문에 type을 설정하였다.

AWS_Serverless_003_21

AWS_Serverless_003_22

DELETE와 GET 메소드도 정상적으로 작동됨을 확인하였다.

+ Recent posts