이 페이지의 이전 버전을 보고 있습니다. 현재 버전 보기.

현재와 비교 페이지 이력 보기

버전 1 다음 »

실습 소개

기존 Amazon Polly를 통한 음성 읽기 서버리스 앱 개발하기 블로그와 동일한 기능을 수행하는 실습입니다.

Amazon Polly(TTS) 서비스가 비동기 방식을 지원하고, 한자를 한글로 변환하는 기능이 들어갔기 때문에 해당 실습을 삭제 하였습니다.

AWS 서버리스 HOL 100 레벨 실습 아키텍처


콘솔 로그인

리전을 서울(우측 상단)로 선택하고, 언어는 영어로 진행(좌측 하단)


DynamoDB 생성

DynamoDB로 이동

Table 생성

테이블 이름(NewsTable) 및 Primary Key(id) 지정

테이블 생성 확인 


SNS 생성

Simple Notification Service로 이동

SNS Topic 하나 생성

NewsTopic으로 생성

Topic ARN 복사 하기: 


Role 생성

Lambda 4개를 위한 Role을 한 개 생성(가능하면 다른 Lambda의 용도에 맞추어서 따로 만드는걸 추천하지만 실습에서는 1개로 진행)

IAM 서비스로 이동

Lambda를 위한 Role을 생성하기 위해 이동

Role을 생성

이 역할을 사용하는 주체는 AWS Service의 Lambda입니다.


정책을 새로 만듭니다.

Visual editor에서 JSON으로 변경합니다.

아래 리소스에 대한 접근 정책을 JSON으로 추가합니다.

{
  "Version":"2012-10-17",
  "Statement":[ 
    { 
      "Effect":"Allow",
      "Action":[ 
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents",
        "polly:StartSpeechSynthesisTask",
        "dynamodb:Query",
        "dynamodb:Scan",
        "dynamodb:PutItem",
        "dynamodb:UpdateItem",
        "dynamodb:DeleteItem",
        "sns:Publish",
        "s3:PutObject",
        "s3:PutObjectAcl",
        "s3:DeleteObject",
        "s3:GetBucketLocation"
      ],
      "Resource":[ 
        "*"
      ]
    }
  ]
}


정책 이름은 NewsPolicy로 지정하고, 하단 우측읜 Create policy 버튼을 클릭합니다.

정책이 만들어 진것을 알 수 있습니다.

원래 창에서 NewsPolicy를 검색합니다.

 나오지 않는다면, 우측 상단의 새로 고침 버튼을 클릭합니다.

Role의 Tag를 입력합니다. NewsApp을 만들기 때문에 Name을 NewsApp으로 정의합니다.

News Role을 생성합니다.

NewsRole이 생성된 것을 확인할 수 있습니다. 클릭해서 ARN 정보를 기록합니다.

arn:aws:iam::551374826607:role/NewsRole


S3 폴더 만들기

S3 서비스 이동

현재 만들어진 버킷을 볼 수 있습니다.


S3 정적 웹 호스팅용 버킷 만들기


MP3 보관을 위한 S3 버킷 생성


S3 버킷 정적 웹 호스팅 기능 활성화

정적 웹 호스팅을 제공할 버킷을 클릭합니다.

2번째 Properties 탭에서 Static Web Hosting 기능을 클릭합니다.

정적 웹 호스팅 기능이 비활성화 되어 있습니다.

Static Website hosting 기능을 켜 주고, index와 error 문서를 지정하고 저장합니다.

정적 웹 호스팅 기능이 활성화 된 것을 볼 수 있습니다.

버켓 접근에 대한 정책을 설정 할 수 있습니다.

S3 버켓에 대한 접근 권한 설정
{ 
  "Version":"2012-10-17",
  "Statement":[ 
    { 
      "Sid":"PublicReadGetObject",
      "Effect":"Allow",
      "Principal":"*",
      "Action":[ 
        "s3:GetObject"
      ],
      "Resource":[ 
        "arn:aws:s3:::BUCKET_NAME/*"
      ]
    }
  ]
}


버켓 정책을 설정하고 저장 합니다.

S3 Bucket이 Public 접근이 가능하다는 메시지가 출력됩니다.


S3 버켓 컨텐츠 업로드 (API 수정)


Lambda 4개 생성 (환경변수 고려)

Lambda 서비스로 이동


Lambda 서비스에서 Function을 하나 생성합니다.


Scratch로 PostNews 이름으로 Lambda 함수를 생성합니다. Runtime은  Python 2.7이고 Role은 이미 만들어둔 NewsRole을 선택하고 Create function 버튼을 클릭 합니다.


Lambda 함수가 정상적으로 만들어진 것을 확인할 수 있습니다.

함수 코드를 입력합니다.

# -*- 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


함수 환경 변수, 실행 시간, Tag를 설정합니다.

아래 Function code 영역에 코드를 다음과 같이 수정합니다.


두 번째 함수 만들기

Create function 버튼을 클릭해서 새로운 함수를 생성합니다.

GetNews 함수를 생성합니다.

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


UpdateNews 함수 생성

코드

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

코드 및 환경 변수 설정

Lambda 함수가 기동되는 이벤트 등록

저장하고 나면 NewsTopic에 의해서 트리거가 활성화 된 것을 볼 수 있습니다.


DeleteNews 함수 등록

함수 생성

4개 람다 함수.


API Gateway 생성 및 배포

API Gateway 



S3 버켓 컨텐츠 업로드 (API 수정)


테스트

  • 레이블 없음