cloudwithbass

[AWS]★AWS SES란?, SES + Lambda + DynamoDB 실습해보기 본문

AWS

[AWS]★AWS SES란?, SES + Lambda + DynamoDB 실습해보기

여영클 2024. 7. 7. 20:02

목차

     

     

    1. AWS SES란?

     사용자의 이메일 주소와 도메인을 이용해 이메일을 보내고 받을 수 있는 쉽고 비용 효율적인 방법을 제공하는 이메일 플랫폼입니다.

     
    AWS SES는 Simple Email Service의 약자로, 이메일을 보내는 간단한 서비스입니다.
     
    AWS SES는 다른 AWS의 서비스들과 호환가능합니다.
    가령, Lambda를 트리거해서 이메일을 전송한다거나, 수신한 이메일을 S3 버킷에 저장할 수 있습니다.
     

    2. AWS SES 과금 정책

    AWS SES의 과금 정책은 온디맨드입니다.
    사용한 만큼만 요금을 지불하면 됩니다.
     
    상세 정보는 아래 사진과 같습니다.

    출처 https://aws.amazon.com/ko/ses/pricing/

     

    3. 실습

    실습할 아키텍처입니다.
    취업을 하기 위해 여러 회사에 지원을 넣으려고 하는데요
    잡코리아에서 몇 명이 지원했는지 확인이 가능하다는 것을 알았습니다.
    따라서 지원 전 날에 경쟁률이 얼마나 되는지 알아보기 위해 전 날에 이메일로 알림을 주려고 합니다.
     

    3-1 Amazon SES 구성

    먼저 콘솔의 시작하기를 눌러 이메일 주소를 추가합니다.

    이메일 주소를 추가합니다.
    도메인은 임의로 입력했습니다.

    MAIL FROM 도메인은 이메일이 발생한 위치입니다. 
    Amazon SES를 통해 발송되는 이메일은 amazonses.com의 하위 도메인을 MAIL FROM 도메인으로 사용하는데, 이를 커스텀해서 사용자가 정의할 수 있습니다.
    이는 선택 사항이므로 저는 스킵했습니다. 더 자세한 내용은 Custom mail from domain 문서를 참고해주세요.
     

    메일에서 링크를 클릭해 이메일을 검증합니다.

     
    Amazon SES가 잘 동작하는지 확인하기 위해 테스트 이메일을 보내보겠습니다.

    Amazon SES 좌측의 자격 증명에서 인증된 이메일을 클릭합니다.

     

    상단 테스트 이메일 전송 클릭

     

    시나리오에서 '사용자 지정' 선택 후 수신자에 인증된 이메일을 입력합니다.
     
    이후 테스트 이메일 전송 버튼을 누르면 아래 사진과 같이 이메일이 도착한 것을 확인할 수 있습니다. (스팸 메일함 확인)

     
    혹시 이메일이 도착하지 않는다면 SES 테스트 메일 발송 가이드 문서를 참고해주세요.
     

    3-2 DynamoDB 생성

    DB_for_Lambda_Sending_SES라는 이름의 DynamoDB 테이블을 하나 생성합니다.

     

    테스트를 위해 항목은 아래와 같이 구성했습니다.

    3-3 AWS Lambda 구성

    3-3-1 람다의 IAM 정책 생성

    Lambda_Allowing_Sending_SES 정책

    {
      "Version":"2012-10-17",
      "Statement":[
        {
          "Effect":"Allow",
          "Action":[
            "ses:SendEmail",
            "ses:SendRawEmail"
          ],
          "Resource":"*"
        }
      ]
    }

    SES에서 이메일을 보내는 기능을 허용하는 정책입니다.
    Identity and access management in Amazon SES 문서의 Allowing Access to Email-Sending Actions Only 정책 예시를 사용했습니다.
     
    Lambda_Allowing_Scanning_DynamoDB정책

    {
    	"Version": "2012-10-17",
    	"Statement": [
    		{
    			"Sid": "Statement1",
    			"Effect": "Allow",
    			"Action": [
    				"dynamodb:Scan"
    			],
    			"Resource": [
    				"*"
    			]
    		}
    	]
    }

    DynamoDB의 모든 리소스를 scan할 수 있게 해주는 정책입니다.
     

    3-3-2 람다 역할 생성

     

    3-3-3 역할에 정책 할당

     
     3-3-2에서 생성한 역할에, 3-3-1에서 생성한 정책들을 할당해줍니다.
    이제 이 역할이 할당된 Lambda는  Amazon SES 서비스에서 이메일을 보낼 수 있고, DynamoDB에 scan 동작을 수행할 수 있습니다.


    3-3-5 Lambda 생성

    역할은 3-3-2에서 생성했던 역할을 선택해서 생성합니다.

     

    3-3-4 Lambda 코드 작성

    코드의 내용은 다음과 같습니다.

    1. ap-northeast-2 리전에 DynamoDB와 SES 클라이언트생성합니다.
    2. scan함수를 이용해 DynamoDB의 테이블 내용을 가져옵니다.
    3. 테이블의 Deadline 항목이 내일 날짜와 같다면 send_email 함수를 호출합니다.
    4. send_email 함수는 이메일을 전송합니다.

    주의사항은.. DynamoDB의 항목을 문자열 속성으로 저장했기 때문에 event_name = item['EventName']['S']꼴로 가져와야 합니다. DynamoDB는 key-value 꼴로 데이터를 저장하기 때문에 {"S":"테스트용 공고"}식으로 저장되기 때문입니다.

     

    상세 내용은 주석으로 작성해놓았습니다.

    import boto3
    from datetime import datetime, timedelta
    import json
    
    # DynamoDB와 SES 클라이언트 생성
    dynamodb = boto3.client('dynamodb', region_name='ap-northeast-2')
    ses = boto3.client('ses', region_name='ap-northeast-2')
    
    # DynamoDB 테이블 이름
    TABLE_NAME = 'DB_for_Lambda_Sending_SES'
    
    def lambda_handler(event, context):
        # 오늘 날짜와 내일 날짜 계산
        today = datetime.utcnow().date()
        tomorrow = today + timedelta(days=1)
        
        # DynamoDB에서 모든 이벤트 가져오기
        response = dynamodb.scan(TableName=TABLE_NAME)
        
        events = response['Items']
        
        for item in events:
            event_name = item['EventName']['S']
            event_address = item['Address']['S']
            deadline = item['Deadline']['S']
            
            
            # 날짜 비교를 위해 날짜형으로 변환
            try:
                deadline = datetime.strptime(deadline, '%Y-%m-%d').date()
            except ValueError:
                print(f"Invalid deadline format: {deadline}")
                continue
            
            #내일이면 이메일 전송
            if deadline == tomorrow:
                send_email('yyk0370@naver.com', event_name, event_address)
            else:
                print(f"Skipping item due to invalid deadline date: {deadline}")
    
        return {
            'statusCode': 200,
            'body': json.dumps('Emails sent successfully')
        }
    
    def send_email(recipient_email, event_name, event_address):
        try:
            # 이메일 발송
            response = ses.send_email(
                Source='yyk0370@naver.com',
                Destination={
                    'ToAddresses': [recipient_email]
                },
                Message={
                    'Body': {
                        'Text': {
                            'Charset': 'UTF-8',
                            'Data': f'"{event_name}" 공고가 내일 마감됩니다.\n이 주소를 확인하세요: {event_address}',
                        }
                    },
                    'Subject': {
                        'Charset': 'UTF-8',
                        'Data': '모집일이 하루 남은 공고가 있습니다.',
                    },
                }
            )
            print(response)
        except Exception as e:
            print(f"An error occurred while sending email: {e}")

     
     
     
    테스트를 위해 콘솔에서 Deploy 후 Test를 누르면 면 다음과 같이 람다와 SES가 잘 작동하는 것을 확인할 수 있습니다.

     
    3-4 Labmda 트리거 구성

     
    EventBridge를 이용해 매일 밤 12시에 트리거되도록 크론식을 구성합니다.
    cron의 내용은 각각  분, 시, 날짜, 월, 요일, 년도를 의미합니다.
    따라서 cron(0 12 * * ? *)는 매일 밤 12시가 됩니다.
    -> 수정합니다. 밤 12시는 cron(0 0 * * ? *)이며, 이는 UTC 시간대를 기준으로 합니다.


    '*'는 모든 값을 의미하며, '?'는 특정되지 않음을 의미합니다.
    '?'는 날짜와 요일에만 사용가능한 와일드 카드라서 날짜와 요일 중 하나가 '*'라면 다른 하나는 반드시'?'여야 합니다.
    이와 관련한 사항들은 Cron 표현식 참조 문서를 확인해주세요.
     
    이제 매일 밤 12시 정각마다 람다가 트리거되어 메일을 발송할 수 있습니다.

     

    3-4-1 CRON 관련사항

    자정에 이메일이 정상적으로 전송되지 않은 것을 확인했습니다.

     

    문제를 해결하기 위해 Lambda의 모니터링에 들어가 EventBridge가 정상적으로 동작했는지 확인해봤고, UTC 시간대로 12시에 람다가 트리거된 것을 확인했습니다.

    이를 통해 크론식이 UTC를 기준으로 작동하는 것을 알았습니다.

    UTC의 0시 0분 0초는 KTC(한국 시간)의 15시 0분 0초이므로 크론식을 다음과 같이 수정 후 자정에 다시 확인해보겠습니다.

     


     

    cron을 바꾼 후 오전 12시에 정상적으로 메일을 수신했습니다.


    참고 문서

    Amazon SES란 무엇인가요?
    How do I use Lambda and Amazon SES to send email?