만족은 하되 안주하지는 말자

기록해야 기억한다

프로그래밍/backend&devOps

[AWS] amazon lambda + API Gateway 로 SNS 메시지 보내기 - 2

D36choi 2021. 4. 12. 17:14
728x90

2021.04.12 - [프로그래밍/backend&devOps] - [AWS] amazon lambda + API Gateway 로 SNS 메시지 보내기 - 1

 

[AWS] amazon lambda + API Gateway 로 SNS 메시지 보내기 - 1

먼저 발행된 글들 2021.03.04 - [프로그래밍/backend&devOps] - [AWS] Amazon SNS 란? [AWS] Amazon SNS 란? Amazon SNS 란? Amazon Simple Notification Service (Amazon SNS) is a managed service that provide..

choichumji.tistory.com

 


API Gateway 를 람다의 트리거로 추가하자

람다함수가 동작하기 위해서 Trigger(트리거) 라는 개념이 존재한다.  

 

A trigger is a Lambda resource or a resource in another service that you configure to invoke your function in response to lifecycle events, external requests, or on a schedule. Your function can have multiple triggers. 

 

트리거란 내가 실행하고자 하는 람다 함수를 호출하기 (invoke) 위해 또다른 아마존 서비스에서의 자원이나 람다 자원을 의미한다.

우리가 불을 켜려면 직접 스위치를 누르거나, 타이머를 설정하듯이 트리거를 통해 원하는 함수를 원하는 타이밍에 실행 시키거나 관리할 수 있다.

 

나는 'sns에 메시지를 발행' 하는 함수의 실행을 위해, API Gateway 를 트리거로 사용할 것이다.

만약 누군가, 혹은 내가 적절한 url 주소로 적절한 메서드 (GET,POST,PUT,DELETE ...) 를 통해 API 요청을 날리면 람다함수가 실행되어 sns 에 메시지가 발행될 것이다.

 

지난 게시물에 이어서 계속

내가 만든 람다 함수의 함수 개요를 보면 트리거 추가가 존재한다. 눌러보면 트리거를 생성할 수 있다.

친절하게도 제일 위에 게이트웨이가 있다

serverless, api 등 해당 트리거의 특성 또한 밑에 알려주고 있다. 골라보자.

새 API 생성을 누르면 위와 같이 항목들이 생긴다. 

REST API, 보안은 '열기' 를 골라보았다.

"HTTP API 와 REST API" 의 차이가 궁금하다면 아마존 공식문서를 확인해보자.

docs.aws.amazon.com/ko_kr/apigateway/latest/developerguide/http-api-vs-rest.html

 

HTTP API와 REST API 중에서 선택 - Amazon API Gateway

HTTP API와 REST API 중에서 선택 HTTP API는 AWS Lambda 및 HTTP 엔드포인트를 비롯한 AWS 서비스와의 지연 시간이 짧고 경제적인 통합을 위해 설계되었습니다. HTTP API는 OIDC 및 OAuth 2.0 인증을 지원하며 CORS

docs.aws.amazon.com

요약하자면, "REST API" 의 기능이 더 다양하다 라고 할 수 있겠다. 또한 REST API 는 OAuth2.0 을 지원하지 않는다.

내가 하고자하는 것이 무엇인지에 따라 고르면 될 것 같다.

 

이제 추가 버튼을 눌러보자.

 

람다 함수에 트리거가 연결되었다.

 

이제 API 게이트웨이를 통해 생성된 주소와 리소스 경로에 postman 을 통해 메시지를 담아 보내보자.

트리거의 API 메타데이터를 확인할 수 있다

 

이제 Postman 을 실행해 주소를 위 트리거의 API 엔드포인트를 입력한 뒤

request body 에는 1에서 했던 json data 를 담아서 보내보자.

(topic,subject,body) 를 포함하고 있어야 한다.

 

결과는?

 

아마 런타임 에러가 날 것이다. 왜냐면 event 의 body 가 unicode 문자열 로 들어오면서 파이썬이 event 의 body 문자열을 dict 객체로 파싱하지 못하기 때문이다.

 

( message['topic'] 을 수행할 수 없다. 문자열이니까! )

 

추가된 함수는 'transform_str_to_dict' 이다.

 

  • event에 담긴 body string 을 꺼내 온다.
  • 이 객체를 또 literal_eval 을 통해 가공된 문자열을 json.loads 를 통해 파이썬 객체로 역직렬화 한다.

문자열을 파이썬 Dict 객체로 역직렬화해야만, 이후 진행에서 에러가 나지 않는다. (유니코드문제로 삽질을 계속했는데 허무하다..)

 

밑은 삽질코드인데 나중에 비슷한 이슈 있으면 돌려보려고 적어놓겠다.

더보기
from __future__ import print_function

import json
import urllib
import boto3
import ast

print('Loading event function...')

def transform_unicode_to_dict(event):
    message = event['body']
    # print (type(message),message)
    # payload_json = json.dumps(message,ensure_ascii=False)
    # print (type(payload_json),payload_json)
    # print (type(ast.literal_eval(payload_json)), ast.literal_eval(payload_json))
    # print (type(json.loads(ast.literal_eval(payload_json))),json.loads(ast.literal_eval(payload_json)))
    return json.loads(message)


def send_to_sns(event, context):

    # This function receives JSON input with three fields: the ARN of an SNS topic,
    # a string with the subject of the message, and a string with the body of the message.
    # The message is then sent to the SNS topic.
    #
    # Example:
    #   {
    #       "topic": "arn:aws:sns:REGION:123456789012:MySNSTopic",
    #       "subject": "This is the subject of the message.",
    #       "message": "This is the body of the message."
    #   }
    for k,v in event.items():
        print(k," : ",v)
    
    message = transform_unicode_to_dict(event)
    sns = boto3.client('sns')
    sns.publish(
        TopicArn=message['topic'],
        Subject=message['subject'],
        Message=message['body']
    )

    return {
        'statusCode': '200',
        'headers': {
            'Content-Type': "application/json"
        },
        'body': 'publish success',
        'isBase64Encoded':"false"
    }

 

 

수정된 람다코드 

from __future__ import print_function

import json
import urllib
import boto3

print('Loading event function...')

def transform_str_to_dict(event):
    message = event['body']
    return json.loads(message)


def send_to_sns(event, context):

    # This function receives JSON input with three fields: the ARN of an SNS topic,
    # a string with the subject of the message, and a string with the body of the message.
    # The message is then sent to the SNS topic.
    #
    # Example:
    #   {
    #       "topic": "arn:aws:sns:REGION:123456789012:MySNSTopic",
    #       "subject": "This is the subject of the message.",
    #       "message": "This is the body of the message."
    #   }
    for k,v in event.items():
        print(k," : ",v)
    
    message = transform_str_to_dict(event)
    sns = boto3.client('sns')
    sns.publish(
        TopicArn=message['topic'],
        Subject=message['subject'],
        Message=message['body']
    )

    return {
        'statusCode': '200',
        'headers': {
            'Content-Type': "application/json"
        },
        'body': 'publish success',
        'isBase64Encoded':"false"
    }

 

 

 

또한 리턴 값도 달라졌는데, 단순히 문자열을 반환하는게 아니라 response (응답) 객체를 반환해줘야하는 것이 API 통신의 규격이므로

 

그에 맞춰 'statusCode', 'headers', 'body' 를 간단하게 지정해줘야 요청을 보낸 클라이언트가 적절하게 응답을 해석할 수 있다.

 

코드 200 은 성공을 의미한다. 이제 테스트를 해보자!

 

포스트맨을 켜고 검증해보자

위와 같이 body 를 작성해주고 API 엔드포인트에 명령을 쏴보자

 

request body 에 json 데이터를 담아 보내야 하므로 메소드는 'GET'이 아닌 'POST' 임에 주의하자. (포스트맨이 알아서 알려주겠지만)

 

타이핑한게 아닙니다. 200 OK!

 

정상적으로 에러없이 동작해 200 응답이 온 것을 볼 수 있다. 그럼 메시지가 잘발행되었나 구독자에게 가보자.

 

구독자도 메시지를 잘 받았다.

 

끝맺음

 

이번 시간을 통해 lambda 함수의 간단한 작성법과 API Gateway 의 생성 및 트리거 연결, SNS 로의 메시지 전송등을 익혔다.

 

람다를 통해 서버리스한 인프라 구축을 잘 이용하고 API 정의 또한 잘 해놓으면 힘들게 서버가 죽을까안죽을까 고민을 덜한 채 간편하게 여러 기능을 구현할 수 있을 것 같다고 느꼈다. 슬랙 메시징 등이랑 연계하면 되게 좋을듯?

 

cloudwatch 와 cloudtrail 로 로깅을 S3 bucket 에 저장하는게 아마 다음 단계가 아닐까싶다.

 

끝!