버전 비교

  • 이 줄이 추가되었습니다.
  • 이 줄이 삭제되었습니다.
  • 서식이 변경되었습니다.

...

  1. AWS 관리 콘솔에서  Lambda  서비스로 이동합니다.


  2. Create a function 버튼을 클릭하여 Lambda 함수를 하나 만들겠습니다. 또는 좌측 메뉴를 열고,  Dashboard나 Functions 메뉴를 선택하고, 우측의 Create function 버튼을 클릭합니다.


  3. 새로운 함수의 Name은  PostNews  라고 만듭니다. Runtime은  Python 3.8  을 선택합니다. Role은  Choose an existing role  을 선택하고, 위에서 생성한 IAM Role인  NewsRole  을 선택합니다. 그리고 우측 하단의 Create function   버튼을 클릭합니다.


  4. Lambda 함수가 정상적으로 만들어진 것을 확인할 수 있습니다. Lambda의 실행 구조는 Function Overview 메뉴 에서 볼 수 있습니다. 트리거 되면, PostNews가 실행되고, 코드 안에서 특정 서비스로 접근하기 위한 권한을 Role을 통해서 할당 받은 것을 알 수 있습니다.


  5. Function Overview 탭을 클릭해서 접고, 아래 Code를 보겠습니다. Lambda 함수 코드 편집기는  AWS Cloud9  IDE가 내장되어 있어서 코드 편집을 웹 브라우저에서도 쉽게 할 수 있는 환경을 제공합니다. 좌측 하단의 lambda_function.py 파일을 클릭하면 기본 파이썬 소스 코드가 나오는걸 볼 수 있습니다. Runtime으로 Python 3.8이 선택되어져 있는 것을 볼 수 있습니다. Handler는 함수가 시작하기 위한 파일의 위치와 함수의 이름을 의미합니다.

  6. 아래 코드를 이 Lambda 함수의 코드로 변경합니다. 해당 함수의 로직은 Polly에 TTS 작업을 요청하고, 요청 작업에 대한 ID 값을 DynamoDB에 등록합니다.

    코드 블럭
    # -*- coding: utf-8 -*-
    from __future__ import print_function
      
    import boto3
    import os
    import json
    import uuid
    import datetime
    import re
     
    def lambda_handler(event, context):
        if "body" in event:
            parmas = json.loads(event['body'])
         
        voice = parmas["voice"]
        originText = parmas["text"]
        timbre = parmas["timbre"]
        pitch = parmas["pitch"]
        updateDate = datetime.datetime.now().strftime("%Y%m%d")
      
        polly = boto3.client('polly')
        removeBrackets = re.sub(r'\([^)]*\)', '', originText)
        repTextBlock = re.sub('[·…]', '<break time="100ms"/>', removeBrackets)
        ssmlBlock = "<speak><amazon:effect vocal-tract-length=\"" + timbre + "\"><prosody pitch=\"" + pitch + "\">" + repTextBlock + "</prosody></amazon:effect></speak>"
        ssmlBlock = ssmlBlock.replace('현수', '<amazon:effect vocal-tract-length="+80%"><prosody pitch="-70%">안녕하세요? 저는 서연 친구 현수에요.</prosody></amazon:effect>')
        ssmlBlock = ssmlBlock.replace('귀신', '<amazon:effect name="whispered"><amazon:effect vocal-tract-length="-30%"><prosody volume="loud">나 꿍꼬또, 기싱꿍꼬또</prosody></amazon:effect></amazon:effect>')
        print (ssmlBlock)
          
        response = polly.start_speech_synthesis_task(
            OutputFormat= 'mp3',
            OutputS3BucketName= os.environ['BUCKET_NAME'],
            # OutputS3KeyPrefix='polly/',
            SampleRate='22050',
            SnsTopicArn=os.environ['SNS_TOPIC'],
            Text=ssmlBlock,
            TextType='ssml',
            VoiceId=voice
        )
        print (response)
        data = response['SynthesisTask']
          
        # Creating new record in DynamoDB table
        dynamodb = boto3.resource('dynamodb')
        table = dynamodb.Table(os.environ['DB_TABLE_NAME'])
        table.put_item(
            Item = {
                'id' : data['TaskId'],
                'updateDate': data['CreationTime'].strftime("%Y-%m-%d %H:%M:%S"),
                'voice' : voice,
                'originText': originText,
                'pollyStatus' : data['TaskStatus'],
                'timbre': timbre,
                'pitch': pitch,
                'mp3Url': data['OutputUri'],
                'RequestCharacters': data['RequestCharacters']
            }
        )
         
        result = {
            'statusCode': 200,
            'body': json.dumps({'recordId': data['TaskId']}),
            'headers': {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            }
        }
          
        return result


  7. 아래와 같이 함수 코드를 변경하고 나면 반드시 상단의 Deploy 버튼을 눌러서 환경을 저장합니다. 저장 되면 저장된 정보가 배포가 된 것을 확인 할 수 있습니다.



  8. PostNews Lambda 함수에는 코드에 외부의 환경 변수 값을 넣을 수 있는 방법을 제공합니다. 메타 데이터가 저장될 DynamoDB 테이블( DB_TABLE_NAME ), Polly가 작업을 완료하면 다음 Lambda 함수를 트리거 시키기 위한 SNS Topic( SNS_TOPIC ), Polly에 의해서 만들어지는 mp3가 저장될 S3의 버켓( BUCKET_NAME )의 이름을 지정해야 합니다. 이 값을 환경 변수로 함수에 전달할 수 있는 기능을 사용하기 위하여 Environment variables를 사용할 수 있습니다. 아래와 같이 Configuration 탭으로 이동하고, Environment varialbes를 선택한 후, Edit 버튼을 클릭하면 해당 메뉴로 진입합니다. S3 버킷 이름( polly-mp3.studydev.com )과, NewsTopic 의 ARN 값( , DynamoDB 테이블의 NewsTable 값을 아래와 같이 입력합니다.
  9. Tags 탭에서는 Key 값으로 Name 을 Value 값을 NewsApp  을 입력하고, 우측 상단의 Save  버튼을 클릭하여 환경을 저장합니다.



  10. 추가로 하단의 General configuration 에서 Lambda의 실행 시간을 지정할 수 있습니다. Timeout 값을 1 min 으로 수정하고 Save 합니다.



...

  1. PostNews와 같이 Create function 버튼을 클릭해서 새로운 함수를 생성합니다.
  2. GetNews 함수를 생성합니다. Runtime과 Role 설정은 기존과 동일합니다.
  3. GetNews 의 함수 코드를 아래 코드로 대체합니다. 반드시 Deploy 버튼을 눌러서 저장하세요.

    코드 블럭
    from __future__ import print_function
      
    import boto3
    import os
    import json
    import decimal
    from boto3.dynamodb.conditions import Key, Attr
     
    # https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/GettingStarted.Python.04.html
    # Helper class to convert a DynamoDB item to JSON.
    class DecimalEncoder(json.JSONEncoder):
        def default(self, o):
            if isinstance(o, decimal.Decimal):
                if o % 1 > 0:
                    return float(o)
                else:
                    return int(o)
            return super(DecimalEncoder, self).default(o)
      
    def lambda_handler(event, context):
        if "queryStringParameters" in event:
            parmas = event['queryStringParameters']
        print (parmas)
          
        postId = parmas["postId"]
        dynamodb = boto3.resource('dynamodb')
        table = dynamodb.Table(os.environ['DB_TABLE_NAME'])
       
        if postId == "*":
            items = table.scan()
        else:
            items = table.query(KeyConditionExpression=Key('id').eq(postId))
              
        response = {
            'statusCode': 200,
            'body': json.dumps(items["Items"], cls=DecimalEncoder),
            'headers': {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            }
        }
      
        return response

  4. Environment variables는 key로  DB_TABLE_NAME 에 value로  NewsTable 를 입력하고, Tags에 key에 Name과 value에  NewsApp 을 입력하고, Timeout을 1 min 으로 수정한 후, 상단 우측의 Save 버튼을 클릭합니다.


...

  1. PostNews와 같이  Create function  버튼을 클릭해서 UpdateNews  함수를 생성합니다. Runtime과 Role 설정은 기존과 동일합니다.


  2. UpdateNews 의 함수 코드를 아래 코드로 대체합니다. 반드시 Deploy 버튼을 눌러서 저장하세요.

    코드 블럭
    from __future__ import print_function
     
    import boto3
    import os
    import json
    from contextlib import closing
    from boto3.dynamodb.conditions import Key, Attr
    import re
     
    def lambda_handler(event, context):
        polly_message = event["Records"][0]["Sns"]["Message"]
        print (polly_message)
        polly_response = json.loads(polly_message)
     
        # Updating the item in DynamoDB
        dynamodb = boto3.resource('dynamodb')
        table = dynamodb.Table(os.environ['DB_TABLE_NAME'])
      
        response = table.update_item(
            Key={
                'id': polly_response["taskId"]
            },
            UpdateExpression="set pollyStatus = :s",
            ExpressionAttributeValues={
                ':s': polly_response['taskStatus']
            }
        )
         
        print(response)
         
        s3 = boto3.client('s3')
        s3.put_object_acl(
            ACL = 'public-read',
            Bucket = os.environ['BUCKET_NAME'],
            Key = polly_response["taskId"] + ".mp3"
        )
         
        return


  3. Environment variables는 DB_TABLE_NAME에  NewsTable 값을 입력하고, BUCKET_NAME에는 S3 버킷 이름( polly-mp3.awsdemo.kr )을 지정합니다. Tags에 Name과  NewsApp 을 입력하고, Timeout을  1 min 으로 수정한후, 우측 상단의 Save 버튼을 클릭하여 저장합니다.




  4. UpdateNews Lambda 함수는 SNS Topic에 의해서 트리거 되어 동작이 되어야 합니다.  따라서 Function Overview에서 좌측 하단의 Add trigger 버튼을 클릭합니다.
  5. 트리거를 위하여 SNS를 클릭합니다. SNS topic 으로  NewsTopic  을 지정합니다. 해당 SNS에서 맞는지 확인하고 Add 버튼을 클릭하여 트리거를 등록하고 확인합니다.
     



...

  1. DeleteNews 함수를 하나 더 추가하기 위해서 Lambda 서비스의 Function List가 나오는 화면 우측 상단의 Create function 버튼을 클릭합니다.
    Image RemovedImage Added
  2. DeleteNews  함수를 생성합니다. Runtime과 Role 설정은 기존과 동일합니다.
    Image RemovedImage Added
  3. DeleteNews 의 함수 코드를 아래 코드로 대체합니다. 반드시 Deploy 버튼을 눌러서 저장하세요.

    코드 블럭
    from __future__ import print_function
      
    import boto3
    import os
    import json
    from boto3.dynamodb.conditions import Key, Attr
     
    def lambda_handler(event, context):
        if "body" in event:
            params = json.loads(event['body'])
        print (params)
          
        # Bad Request
        if params["postId"] is None or params["postId"] == "":
            response = {
                'statusCode': 400,
                'body': json.dumps({'message': "An unknown error has occurred. Missing required parameters."}),
                'headers': {
                    'Content-Type': 'application/json',
                    'Access-Control-Allow-Origin': '*'
                }
            }
            return response
          
        postId = params["postId"]
        dynamodb = boto3.resource('dynamodb')
        table = dynamodb.Table(os.environ['DB_TABLE_NAME'])
        table.delete_item(Key={"id":postId})
          
        s3 = boto3.client('s3')
        s3.delete_object(Bucket=os.environ['BUCKET_NAME'], Key= postId + ".mp3")
      
        response = {
            'statusCode': 200,
            'body': json.dumps({'message': "item is deleted : " + postId}),
            'headers': {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            }
        }
          
        return response

    Image Added

  4. Environment variables는 DB_TABLE_NAMENewsTable 값을 입력하고, BUCKET_NAME에는 S3 버킷 이름( polly-mp3.awsdemo.kr )을 지정합니다. Tags에 Name과  NewsApp 을 입력하고, Timeout을  1 min 으로 수정합니다.
    Image RemovedImage Added
    Image Added
    Image Added

  5. 아래와 같이 4개의 람다 함수가 생성된 것을 확인할 수 있습니다.
    Image RemovedImage Added


10. Lambda 함수를 RESTful 웹 서비스로 만들기

마지막으로해야 할 일은 애플리케이션 로직을 RESTful 웹 서비스로 노출시켜 표준 HTTP 프로토콜을 사용하여 쉽게 호출 할 수 있도록 합니다. 이를 위해 Amazon API Gateway를 사용합니다.

  1. API Gateway 서비스로 이동합니다.
    Image RemovedImage Added
  2. API Gateway 콘솔에서  시작 버튼을 눌러서 REST API를 생성을 시작합니다.
    Image RemovedImage Added
  3. 다음과 같이 생성 화면이 나오는 것을 확인할 수 있습니다.
  4. REST 방식으로  New API  를 선택고 API 이름은 newsapi  로 설정하고, Create API     버튼을 클릭합니다. Endpoint Type은 기본 설정과 같이  Regional  을 선택합니다.
    Image Removed Image Added

  5. API가 생성 된 후, 우리는 3 개의 HTTP 메소드( Actions 버튼을 클릭 후 메서드 생성)를 생성하고 CORS 설정을 합니다.
    1. / 패스를 선택한 상태에서, Actions 버튼을 클릭하여 Create Method  를 클릭합니다.
    2. Method 타입을 선택할 수 있습니다.
    3. GET 을 선택합니다. 그리고 다른 메서드를 추가하기 위해서 다시 / 패스를 선택하고 Actions 버튼을 클릭하여 Create Method  를 클릭 합니다.
    4. 같은 방식으로 POST DELETE 도 추가 합니다.
  6. API Gateway에 대한 각 Method 요청에 대해서 Lambda 함수를 연결합니다.
    1. GET Method로 요청이 올 경우, GetNews Lambda 함수를 호출하도록 설정합니다. Use Lambda Proxy Integration 체크 박스 를 클릭합니다. 우측 하단의 Save 버튼을 클릭하면 저장합니다.
    2. API Gateway가 GetNews Lambda 함수를 호출(Invoke) 할 수 있도록 권한을 추가 하냐고 물으면 OK 버튼을 클릭합니다.
    3. POST Method 역시 동일한 방법으로 PostNews Lambda 함수를 호출하도록 지정합니다.
    4. PostNews  로 접근할 수 있는 권한을 부여합니다.
    5. DELETE Method 역시 동일한 방법으로 DeleteNews Lambda 함수를 호출하도록 지정합니다.
    6. 모던 브라우저의 경우 CORS 이슈가 발생할 수 있습니다. 브라우저가 정적 웹 호스팅 중인 S3 버켓에 접속한 상태에서 API Gateway로 배포한 URL로 동적 컨텐츠 호출을 하면 서로 다른 도메인 문제가 발생할 수 있습니다. 이를 해결하기 위해서 / 패스에서 Actions 버튼을 클릭하고 Enable CORS를 클릭합니다.
    7. 모든 설정 값을 기본 설정 그대로 두고, 우측 하단의  Enable CORS and replace existing CORS headers 버튼을 클릭합니다.
    8. 다음과 같이 팝업창이 나오면서 적용을 할 것인지 물어 봅니다. Yes, replace existing values 버튼을 클릭합니다.
    9. 아래와 같이 CORS 설정이 정상적으로 적용되는 것을 확인 할 수 있습니다.
    10. API 설정이 완료 되었습니다. 이제 배포를 해서 애플리케이션에서 호출할 수 있는 URL을 얻습니다.  Actions  에서  Deploy API 를 선택합니다.

    11. 이제 API를 state 스테이지로 배포를 합니다. 개발, 테스트, 프로덕션에 이르기까지 다양한 스테이지로 나누어서 배포가 가능합니다. 여기서는 stage  로 넣고 Deploy 합니다.

    12. 이제 API 배포까지 완료되었습니다. 해당 API를 호출 할 수 있는 URL이 생성되었음을 확인할 수 있습니다. 

      정보

      아래와 같이 Invoke URL을 미리 메모장에 기록해 둡니다. 해당 URL을 동적 컨텐츠 수집을 위한 URL로 활용할 예정입니다. (본인의 계정에서 생성된 URL을 이용하세요.)

      https://7xxxxxxxxxi6.execute-api.ap-northeast-2.amazonaws.com/stage


    13. API Gateway 리소스 관리를 위한 태깅 작업으로 Configure Tags 버튼을 클릭합니다. Tag Editor가 화면에 출력되면 Name과 NewsApp을 각각 넣어 주고 저장합니다.

...