실습 소개
기존 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 문서를 지정하고 저장합니다.
정적 웹 호스팅 기능이 활성화 된 것을 볼 수 있습니다.
버켓 접근에 대한 정책을 설정 할 수 있습니다.
{ "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 수정)
테스트