목록
목차 영역 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
서버리스 서비스를 이용한 웹 애플리케이션 구축하기 Hands-on Lab
...
이번 실습을 진행하고 나면 아래와 같은 것을 직접 할 수 있게 됩니다.
...
실습 소개
실습 목적
- Cloud9을 이용하여 서버리스 애플리케이션을 SAM(Serverless Application Model) 기반으로 개발/배포할 수 있습니다.
- Cloud9 IDE
- SAM을 이해하고 서버리스 서비스에 필요한 서비스를 직접 기술 할 수 있습니다.
- API Gateway, Lambda, DynamoDB, S3, SNS
- 서버리스 애플리케이션을 AWS Code 시리즈를 이용하여 배포 프로세스를 제작할 수 있습니다.
- CodePipeline, CodeCommit, CodeBuild, CloudFormation(SAM), CodeDeploy
실습 비용
프리티어 기준으로 비용이 발생하지 않는 범위내에서 실습을 진행할 수 있습니다. 해당 실습을 위해서는 별도의 쿠폰이 제공됩니다.
Service | Sub Service | Unit | Pricing | Use | free tier | etc |
---|---|---|---|---|---|---|
Cloud9 | EC2 - t2.micro | 1 h | $0.0116/Hour | t2.micro 2hour | 750hour/month | |
EBS - 8GB | 1 m | $0.1/GB | 8GB 2hour | 30GB/month | ||
Lambda | - | 128MB | $0.000000208/100ms | 1,000번 이하 | 100만번 요청 400GB-초 | 1년 후 지속 제공 |
API Gateway | - | 1백만 API 호출당 $3.50 처음 10TB에 대해 $0.09/GB | 1,000번 이하 | 호출 100만건 | ||
DynamoDB | - | WCU당 최소 $0.47 RCU당 최소 $0.09 GB당 최소 $0.25 | WCU 5 RCU 5 스토리지 100M | 매달 2억건 요청 | 1년 후 지속 제공 | |
SNS | - | 처음 1GB/월 $0.000 GB당 최대 10TB/월 $0.090 GB당 | 100M 이하 | 매달 15GB의 데이터 전송 | ||
Polly | - | 1백만 문자당 $4 | 1,000 문자 이하 | 매달 문자 500만개 | ||
CodeStar | CodePipeline | 월별 활성 파이프라인*당 $1 | 1개 | 매월 활성 파이프라인 1개 | ||
CodeCommit | 최초 5명 초과시 월별 $1 GB당 $0.06/월 (10GB/월 초과) Git 요청당 $0.001 (1000회/월 초과) | 1개 | 최초 5명까지 매달 50GB의 스토리지 매달 10,000건의 Git 요청 | 1년 후 지속 제공 | ||
CodeBuild | build.general1.small $0.005/분 | 30분 | 매월 100 빌드 분의 build.general1.small | 1년 후 지속 제공 | ||
CodeDeploy | EC2 또는 Lambda에 배포 무료 | 30회 | - | |||
CloudFormation | - | - | - | |||
s3 | - | PUT, COPY, POST 또는 LIST 요청 $0.005/1,000건 GET, SELECT 및 기타 모든 요청 $0.0004/1,000건 처음 50TB/월 $0.023/GB | Put 100번 내외 GET 2,000번 내외 | 매달 5GB 스토리지 Get 요청 20,000건 Put 요청 2,000건 |
실습 종료 후 리소스 삭제
실습이 종료되고 나면 리소스를 삭제해야 합니다. 해당 핸즈온은 CloudFormation 기반으로 진행됩니다. 배포 Stack을 삭제하고, 실습에 사용한 Cloud9과 같은 Stack을 삭제하면, 배포되어 있는 리소스도 함께 삭제가 됩니다.
...
구축하고자 하는 서버리스 웹 애플리케이션에 대한 소개
아래 다이어그램은 실습에서 구축하고자 하는 아래는 실습에 구축할 서버리스 웹 애플리케이션의 아키텍처 입니다다이어그램입니다. 서버리스 서비스를 이용하기 때문에, 프로비저닝, 패치, 확장에 대해 고민할 필요가 없으며, 사용한 만큼은 만큼만 비용을 지불할 수 있습니다지불합니다.
또한, AWS 서버리스 서비스는 완전 관리형 서비스이기 때문에 AWS가 이를 관리하기 때문에 우리는 애플리케이션 개발에만 , 개발자는 애플리케이션 개발 자체에만 집중할 수 있습니다.
draw.io Diagram | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
애플리케이션 소개
이 애플리케이션은 다섯 가지 영역으로 나뉘어서 제작할 수 있습니다.
- 첫 번째는 HTML, CSS, Javascript 및 이미지를 S3를 이용하여 정적 웹 페이지로 호스팅 합니다.
- 두 번째는 새로운 게시물에 대한 텍스트 정보를 등록하고, 비동기 처리를 위해서 SNS에 DynamoDB에 등록한 id 값을 Publish 합니다.
- 세 번째는 SNS에 의해서 트리거된 ConvertToAudio 함수에 의하여 DynamoDB에 등록된 텍스트를 수집하고, Polly를 이용하여 MP3 파일로 변환하고 S3 버켓에 파일을 업로드 하고, 최종 결과를 DynamoDB에 업데이트 합니다.
- 네 번째는 게시물에 대한 정보(S3 버킷에 저장된 MP3 파일에 대한 링크 포함)를 검색하는 것입니다.
- 다섯 번째는 사전에 등록한 게시물과 생성한 MP3 파일을 삭제하는 것입니다.
News 등록/수집/삭제는 Amazon API Gateway를 통해 RESTful API 서비스로 제공됩니다. 로직은 Lambda 서비스를 통해서 구현되며 애플리케이션이 어떻게 상호 작용 하는지 살펴 보겠습니다구분할 수 있습니다.
정적 웹페이지 구현
- 이 시나리오는 Amazon S3(Simple Storage Service)에서 호스팅되는 정적 웹 페이지를 기반으로 실행합니다.
새로운 뉴스를 등록
- MP3로 생성할 텍스트 정보는 Amazon API Gateway에 의해 노출된 RESTful API로 수신합니다.
- Amazon API Gateway는 MP3 파일 생성 프로세스를 초기화하는 전용 Lambda 함수인 "New Post"를 설정합니다.
- "New Post" Lambda 함수는 News에 대한 메타 정보를 DynamoDB 테이블에 저장합니다.
- TTS 변환 프로세스를 비동기적으로 실행하기 위해 Amazon SNS의 Topic(NewsTopic)에 DynamoDB(NewsTable)에 등록한 새로운 id 값을 Publish 합니다.
텍스트를 MP3로 변환
- Amazon SNS Topic에 의해서 텍스트를 오디오 파일로 변환하기 위한 Lambda 함수인 "ConvertAudio"는 에서 트리거 되도록 설정합니다.
- "ConvertAudio" Lambda 함수는 Amazon Polly를 사용하여 텍스트를 지정된 언어의 음성을 이용하여 오디오 파일로 변환합니다.
- 생성된 오디오 파일인 MP3 파일은 전용 S3 버킷에 저장합니다.
- 텍스트가 MP3로 변환된 S3 버킷에 대한 참조 URL 정보 및 해당 게시물 처리 상태에 대한 정보는 DynamoDB 테이블에 업데이트 합니다.
등록된 뉴스 정보 검색
- RESTful 웹 서비스는 Amazon API Gateway를 사용하여 배포합니다. Amazon API Gateway는 게시물에 대한 정보를 검색하는 방법을 제공합니다.
이 방법은 게시물의 텍스트와 MP3 파일이 저장되는 S3 버킷에 대한 링크가 포함됩니다. 이 시나리오에서 이 웹 서비스는 Amazon S3에서 호스팅되는 정적 웹 페이지에서 호출됩니다. - Amazon API Gateway는 뉴스를 검색하는 Lambda 함수인 "GetNews"를 호출합니다.
- "Get Post" Lambda 함수는 DynamoDB 테이블에서 게시물에 대한 정보(Amazon S3에 대한 참조 URL을 포함)를 검색합니다.
기존 뉴스 삭제
- RESTful API의 Delete 메서드를 이용하여 삭제를 요청합니다.
- API Gateway는 뉴스 데이터를 삭제하는 Lambda 함수인 "DeleteNews"를 호출합니다.
...
Cloud9 IDE 환경 생성
- Cloud9 서비스로 이동합니다.
- Cloud9 서비스 환경을 생성합니다.
- Cloud9 이름을 생성합니다.
- Cloud9의 환경을 설정합니다. t2.micro를 선택하면 프리티어 범위를 사용할 수 있습니다.
- 가나다
- 가나다
- 가나다
Application 및 "PostNews" Lambda 함수 생성
- 새로운 애플리케이션 및 람다 함수 생성합니다. Cloud9 IDE 우측 네비게이션의 AWS Resources 탭을 클릭하고, Lambda 아이콘을 클릭하면 새로운 함수를 생성할 수 있습니다.
- Application name에는 WebApp을 Function name에는 PostNews를 입력하고 Next 버튼을 클릭합니다.
- Runtime은 Python 2.7을 선택하고, Blueprint는 hello-world-python을 선택하고 Next 버튼을 클릭합니다.
- 가나다
- 가나다
- 가나다
- 가나다
가나다
코드 블럭 language py theme RDark title PostNews linenumbers true # -*- coding: utf-8 -*- from __future__ import print_function import boto3 import os import json import uuid import datetime def lambda_handler(event, context): if "body" in event: event = json.loads(event['body']) print (event) recordId = str(uuid.uuid4()) voice = event["voice"] originText = event["text"] timbre = event["timbre"] pitch = event["pitch"] updateDate = datetime.datetime.now().strftime("%Y%m%d") print('Generating new DynamoDB record, with ID: ' + recordId) # Create the item in DynamoDB table dynamodb = boto3.resource('dynamodb') table = dynamodb.Table(os.environ['DB_TABLE_NAME']) table.put_item( Item={ 'id' : recordId, 'originText': originText, 'postDate': int(updateDate), 'pollyVoice' : voice, 'pollyStatus' : "PROCESSING", 'pollyTimbre': timbre, 'pollyPitch': pitch } ) # Sending notification about new post to SNS client = boto3.client('sns') client.publish( TopicArn = os.environ['SNS_TOPIC'], Message = recordId ) response = { 'statusCode': 200, 'body': json.dumps({'recordId': recordId}), 'headers': { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' } } return response
- 가나다
가나다
코드 블럭 language yml theme RDark title PostNewsTemplate_01 linenumbers true AWSTemplateFormatVersion: '2010-09-09' Transform: 'AWS::Serverless-2016-10-31' Description: >- Building Serverless development environment and CI/CD process for DevOps based on Cloud9 Globals: Function: Runtime: python2.7 Handler: lambda_function.lambda_handler MemorySize: 128 Timeout: 60 Resources: PostNews: Type: 'AWS::Serverless::Function' Properties: CodeUri: PostNews Description: Post news text to convert from text to speech Events: PostNewsApi: Type: Api Properties: Path: /news Method: POST Policies: - Version: '2012-10-17' Statement: - Effect: Allow Action: - 'logs:PutLogEvents' - 'logs:CreateLogStream' - 'dynamodb:PutItem' - 'sns:Publish' Resource: '*'
- 배포하기
필수 리소스를 SAM에 추가하고 리소스 확인
가나다
코드 블럭 language yml theme RDark title PostNewsTemplate_02 linenumbers true AWSTemplateFormatVersion: '2010-09-09' Transform: 'AWS::Serverless-2016-10-31' Description: >- Building Serverless development environment and CI/CD process for DevOps based on Cloud9 Globals: Function: Runtime: python2.7 Handler: lambda_function.lambda_handler MemorySize: 128 Timeout: 60 Environment: Variables: DB_TABLE_NAME: Ref: NewsTable SNS_TOPIC: Ref: NewsTopic BUCKET_NAME: Ref: PollyMp3Bucket Resources: NewsTable: Type: 'AWS::Serverless::SimpleTable' Properties: PrimaryKey: Name: id Type: String ProvisionedThroughput: ReadCapacityUnits: 5 WriteCapacityUnits: 5 NewsTopic: Type: 'AWS::SNS::Topic' Properties: DisplayName: NewsTopic PollyMp3Bucket: Type: 'AWS::S3::Bucket' StaticWebBucket: Type: 'AWS::S3::Bucket' Properties: AccessControl: PublicRead WebsiteConfiguration: IndexDocument: index.html ErrorDocument: error.html PostNews: Type: 'AWS::Serverless::Function' Properties: CodeUri: PostNews Description: Post news text to convert from text to speech Events: PostNewsApi: Type: Api Properties: Path: /news Method: POST Policies: - Version: '2012-10-17' Statement: - Effect: Allow Action: - 'logs:PutLogEvents' - 'logs:CreateLogStream' - 'dynamodb:PutItem' - 'sns:Publish' Resource: '*'
- 가나다
- 가나다
"ConvertAudio" Lambda 함수 생성
- 가나다
- 가나다
- 가나다
- 가나다
- 가나다
- 가나다
- 가나다
"GetNews" Lambda 함수 생성
- 가나다
- 가나다
- 가나다
"DeleteNews" Lambda 함수 생성
- 가나다
- 가나다
- 가나다
S3 정적 컨텐츠 업로드
- 가나다
정적 웹 호스팅 파일 다운로드 받기
코드 블럭 language bash theme RDark linenumbers true wget https://s3.ap-northeast-2.amazonaws.com/polly.awsdemokr.com/301_static_web.zip
압축 풀고 폴더 이동
코드 블럭 language bash theme RDark linenumbers true unzip 301_static_web.zip cd 301_static_web
Cloud9에서 scripts.js 파일 열어서 CloudFormation Stack에 배포된 Output의 APIEndpointURL 값을 소스코드에 반영 (WebsiteURL이 아니므로 주의)
코드 블럭 language js theme RDark linenumbers true var API_ENDPOINT = "https://xxxxxxxxxx.execute-api.ap-southeast-1.amazonaws.com/Prod/news/"; if (API_ENDPOINT === "") { alert("scripts.js 파일의 상단에 API Gateway에 배포한 URL을 등록하고 실행하세요."); }
정적 웹 포스팅하고자 하는 S3 버킷에 public-read 권한으로 파일을 업로드 (CloudFormation Stack에 배포된 Output의 S3WebBucket 값을 아래에 대체)
코드 블럭 language bash theme RDark linenumbers true aws s3 sync . s3://cloud9-webapp-staticwebbucket-xxxxxxxxxxxx --acl public-read
- 웹 브라우저로 정적 웹 페이지에 접속 (CloudFormation Stack에 배포된 Output의 WebsiteURL 값을 웹 브라우저 주소창에 입력)
...
정적 웹 호스팅 파일 다운로드 받기
코드 블럭 language bash theme RDark linenumbers true wget https://s3.ap-northeast-2.amazonaws.com/polly.awsdemokr.com/301_static_web.zip
압축 풀고 폴더 이동
코드 블럭 language bash theme RDark linenumbers true unzip 301_static_web.zip cd 301_static_web
Cloud9에서 scripts.js 파일 열어서 CloudFormation Stack에 배포된 Output의 APIEndpointURL 값을 소스코드에 반영 (WebsiteURL이 아니므로 주의)
코드 블럭 language js theme RDark linenumbers true var API_ENDPOINT = "https://xxxxxxxxxx.execute-api.ap-southeast-1.amazonaws.com/Prod/news/"; if (API_ENDPOINT === "") { alert("scripts.js 파일의 상단에 API Gateway에 배포한 URL을 등록하고 실행하세요."); }
정적 웹 포스팅하고자 하는 S3 버킷에 public-read 권한으로 파일을 업로드 (CloudFormation Stack에 배포된 Output의 S3WebBucket 값을 아래에 대체)
코드 블럭 language bash theme RDark linenumbers true aws s3 sync . s3://cloud9-webapp-staticwebbucket-xxxxxxxxxxxx --acl public-read
- 웹 브라우저로 정적 웹 페이지에 접속 (CloudFormation Stack에 배포된 Output의 WebsiteURL 값을 웹 브라우저 주소창에 입력)
- https://cloud9-webapp-staticwebbucket-xxxxxxxxxxxx.s3.amazonaws.com
웹 페이지 동작 확인
- 텍스트를 등록
- 검색
- 재생
- 텍스트를 옵션을 주어서 등록
- 검색
- 재생
- 삭제
...
- DynamoDB 콘솔에서 " posts "라고 하는 단일 테이블을 만듭니다. 기본 키 id 는 문자열이고, "New Post" Lambda 함수에 의해 새 레코드는 posts 에 자동으로 추가 생성합니다.
NoSQL인 DynamoDB를 사용하므로 스키마를 사전에 정의하지는 않겠지만, 사용하게 될 어트리뷰트가 어떤 것이 있는지 살펴 보겠습니다.
key value id 게시물 ID (UUID로 자동 생성) voice 오디오 파일을 생성하는데 사용된 Amazon Polly 음성 status 처리 상태에 따라서 PROCESSING 또는 UPDATED로 구분 text 원문 텍스트 replaceText 변환될 텍스트 mp3Url Polly에 의해서 생성된 mp3 updateDate 수정된 시간
...
- 콘솔의 서비스에서 IAM을 찾은 다음 역할 메뉴를 선택하고, 역할 만들기 버튼을 눌러 새 역할을 만들기 위한 마법사를 엽니다. 이 역할을 사용할 서비스인 AWS 서비스 의 Lambda 를 선택하고 다음:권한 버튼을 클릭합니다.
- 역할은 정책과 연결되거나 inline으로 정책을 작성하여 연결할 수 있습니다. 여기서는 새로운 정책을 생성하기 위해서 정책 생성 버튼을 클릭 합니다.
정책 생성 새 탭이 나타나면, JSON 편집기 탭을 선택하고 아래와 같이 AWS 권한을 정의하는 코드 를 붙여 넣고, Review policy 버튼을 클릭합니다.
코드 블럭 title Lambda 함수를 위한 정책 JSON 코드 linenumbers true { "Version":"2012-10-17", "Statement":[ { "Effect":"Allow", "Action":[ "polly:SynthesizeSpeech", "dynamodb:Query", "dynamodb:Scan", "dynamodb:PutItem", "dynamodb:UpdateItem", "sns:Publish", "s3:PutObject", "s3:PutObjectAcl", "s3:GetBucketLocation", "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", "lambda:InvokeFunction" ], "Resource":[ "*" ] } ] }
- 정책 이름을 LambdaPostsPolicy 로 등록하고, Create policy 버튼을 클릭합니다.
- 다시 역할 만들기 화면으로 이동한 다음, 새로 고침 버튼으로 정책을 갱신해준 다음 새롭게 만들어진 정책( LambdaPostsPolicy )을 검색해서 선택 하고, 다음:검토 버튼을 클릭합니다.
- 역할 이름은 LambdaPostsRole 로 지정하고 설정된 정책을 확인한 후 역할 만들기 를 실행합니다.
생성된 LambdaPostsRole 역할은 앞으로 만들 Lambda 함수에 연결하여, 정책에 정의되어 있는 서비스들에 접근하여 읽기 또는 쓰기 작업을 수행할 수 있는 권한을 부여하는데 사용합니다.
...
- AWS 관리 콘솔에서 Lambda 를 선택하고 함수 만들기 버튼을 이용해서 함수 만들기 버튼을 선택합니다.
- 새로운 함수의 이름은 PostReader_NewPost 라고 합시다. 런타임에서는 Python 2.7 을 선택합니다. 역할은 기존 역할 선택 을 선택하고, 위에서 생성한 LambdaPostsRole 을 연결합니다. 그리고 함수 생성 버튼을 클릭합니다.
아래 코드를 이 Lambda 함수의 코드로 변경합니다. 한자를 한글로 변환할 수 있는 다른 Lambda 함수를 호출(invoke)할 수 있습니다. 전처리가 필요할 경우, 함수에 추가 하거나 별도의 함수를 작성하여 호출하도록 구성할 수 있습니다. 항상 해당 함수를 호출하지 않아도 되거나, 다른 언어(여기서는 Java로 구현된 한자 변환 함수)로 구현한 함수를 연결할 수 있습니다.
코드 블럭 language py title PostReader_NewPost linenumbers true import boto3 import os import json import uuid import datetime def lambda_handler(event, context): recordId = str(uuid.uuid4()) voice = event["voice"] originText = event["text"] hanja = event["hanja"] updateDate = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") print('Generating new DynamoDB record, with ID: ' + recordId) print('Input Text: ' + originText) print('Selected voice: ' + voice) # Hanja to Korean if hanja: lambda_client = boto3.client('lambda') invoke_response = lambda_client.invoke( FunctionName = "HanjaToKorean", InvocationType = 'RequestResponse', Payload = json.dumps({"inputText": originText}) ) data = invoke_response['Payload'].read() resultText = json.loads(data) replaceText = resultText['outputText'] print('Hanja to Korean Text: ' + replaceText) else: replaceText = originText # Creating new record in DynamoDB table dynamodb = boto3.resource('dynamodb') table = dynamodb.Table(os.environ['DB_TABLE_NAME']) table.put_item( Item={ 'id' : recordId, 'voice' : voice, 'text': originText, 'replaceText': replaceText, 'status' : "PROCESSING", 'updateDate': updateDate } ) # Sending notification about new post to SNS client = boto3.client('sns') client.publish( TopicArn = os.environ['SNS_TOPIC'], Message = recordId ) return recordId
- Lambda 함수 코드 편집기는 AWS Cloud9 IDE가 내장되어 있어서 코드 편집을 웹 브라우저에서도 쉽게 할 수 있는 환경을 제공합니다.
- 위 Lambda 함수 코드는 아래와 같은 기능을 수행합니다.
- 세 개의 입력 매개 변수를 검색합니다.
- voice - Amazon Polly에서 지원하는 목소리 중 하나
- text - 오디오 파일로 변환하려는 게시물의 텍스트
- hanja - 한자를 한글로 변경할지 여부 확인 플래그
- 한자를 한글로 번역해야 할 경우 한글로 변환합니다.
- 새 게시물에 대한 정보가 있는 DynamoDB 테이블에 새 레코드를 만듭니다.
- 새 게시물에 대한 정보를 SNS에 게시합니다 (DynamoDB 항목의 id인 게시물 id가 메시지로 게시됩니다)
- 사용자에게 DynamoDB 항목의 id를 반환합니다.
- 세 개의 입력 매개 변수를 검색합니다.
- 또한 PostReader_NewPost Lambda 함수는 DynamoDB 테이블 및 SNS 주제의 이름을 알아야 합니다. 이 값을 제공하기 위해 다음 환경 변수를 사용합니다.
- DB_TABLE_NAME - DynamoDB 테이블의 이름 (여기에서는 posts )
- SNS_TOPIC - 우리가 만든 SNS 주제의 Amazon Resource Name, ARN은 SNS 서비스의 주제에서 찾을 수 있습니다.
코드 바로 아래에 환경 변수에서 값을 넣어 줍니다.
실행 역할 에서 LambdaPostsRole 이 지정되어 있는지 확인하고, 제한 시간은 1분 으로 변경합니다.(Hanja to Korean 실행에서 발생할 수 있는 지연시간 고려)
" PostReader_NewPost " Lambda 기능이 준비되었습니다. 만약 테스트를 하려면, 다음 입력 데이터를 입력 데이터로 호출합니다.
코드 블럭 language js title PostReader_NewPost Lambda 함수 테스트 이벤트 구성 linenumbers true { "voice": "Seoyeon", "text": "안녕, 난 서연이야. Polly 서비스에서 텍스트를 읽어주는 서비스를 제공하고 있어.", "hanja": false }
- 테스트를 위하여 테스트 이벤트 구성을 시작합니다.
아래와 같이 테스트 이벤트를 구성합니다.
- 테스트 전 저장 을 하고, 테스트 를 수행하면 다음과 같은 실행 결과 를 볼 수 있습니다.
- 테스트 결과, DynamoDB의 posts 테이블에서 새 레코드로 등록되는 것을 확인할 수 있습니다.
...
- 한자를 한글로 변환하는 소스코드를 다운로드 받고 압축을 해제한 후 파일 구조를 확인합니다: 다운로드 받기
- Maven을 CLI에서 실행하기 위하여 다음과 같이 설치합니다.
Linux 일 경우 패키지 관리자를 이용해서 다음과 같이 설치합니다.
코드 블럭 sudo apt-get install maven
만약 Homebrew를 사용하는 경우에는 아래와 같이 설치합니다.
코드 블럭 brew install maven
프로젝트를 빌드 하기 위해서는 HanjaToKorean 폴더에서 다음의 명령을 수행합니다. Maven을 수행하기 전 JDK가 설치되어 있지 않다면, JDK를 설치합니다.
코드 블럭 mvn package
- 새로 작성 하는 Lambda 함수의 이름은 HanjaToKorean 이고, 런타임은 Java 8 을 선택하고, 기존 역할 선택 에서 LambdaPostsRole 을 지정하고 함수 생성 버튼을 클릭합니다.
- 핸들러에 hanja.HanjaToKorean::handleRequest (hanja 패키지의 HanjaToKorean 클래스에 있는 handlerRequest 함수가 시작 함수를 의미)을 지정하고, 함수 패키지에 Maven에서 빌드한 결과로 생성한 lambda-java-hanja-1.0-SNAPSHOT.jar 파일을 업로드 합니다. (Maven 빌드한 target 폴더에 존재)
- Lambda 실행 제한 시간을 1분 으로 설정 합니다.
- 함수 저장 및 테스트하기
- 함수의 모든 설정이 완료되면 저장하고 테스트를 합니다. 테스트 이벤트 구성은 아래와 같습니다.
- 테스트 이벤트를 구성합니다. (식사 前後의 운동 효과)
- 테스트 결과를 확인 합니다. 한자가 한글로 정상적으로 변환되는 것을 확인합니다.
- PostReader_NewPost Lambda 함수 테스트로 다시 돌아가서 한자가 정상적으로 한글로 변환되는지 테스트 합니다.
- 다음의 문장을 테스트 합니다. (식사 前後의 운동 효과에 대해서 알아 봅시다.)
- 한자가 한글로 정상적으로 변환이 되는지 확인합니다.
- 함수의 모든 설정이 완료되면 저장하고 테스트를 합니다. 테스트 이벤트 구성은 아래와 같습니다.
...
- 새로운 함수 생성 을 합니다. 새로운 함수의 이름은 PostReader_ConvertToAudio , 런타임은 Python 2.7 , 기존 역할 선택 은 LambdaPostsRole 을 설정하고 함수 생성 버튼을 클릭합니다.
- PostReader_ConvertToAudio Lambda 함수는 SNS 주제에 의해서 트리거 됩니다. Designer 에서 SNS 를 클릭하여 함수 좌측편에 SNS을 트리거로 추가합니다. 트리거 구성에 SNS 주제는 new_posts 를 설정합니다. 아래 트리거 활성화 에 체크하고 추가 버튼을 클릭합니다.
Lambda 함수 코드를 작성하기 위해서 다시 PostReader_ConvertToAudio 를 클릭하고 함수 코드를 아래에 있는 코드로 대체합니다.
코드 블럭 language py title PostReader_ConvertToAudio linenumbers true import boto3 import os from contextlib import closing from boto3.dynamodb.conditions import Key, Attr def lambda_handler(event, context): postId = event["Records"][0]["Sns"]["Message"] print "Text to Speech function. Post ID in DynamoDB: " + postId #Retrieving information about the post from DynamoDB table dynamodb = boto3.resource('dynamodb') table = dynamodb.Table(os.environ['DB_TABLE_NAME']) postItem = table.query( KeyConditionExpression=Key('id').eq(postId) ) text = postItem["Items"][0]["replaceText"] voice = postItem["Items"][0]["voice"] rest = text #Because single invocation of the polly synthesize_speech api can # transform text with about 1,500 characters, we are dividing the # post into blocks of approximately 1,000 characters. textBlocks = [] while (len(rest) > 1100): begin = 0 end = rest.find(".", 1000) if (end == -1): end = rest.find(" ", 1000) textBlock = rest[begin:end] rest = rest[end:] textBlocks.append(textBlock) textBlocks.append(rest) #For each block, invoke Polly API, which will transform text into audio polly = boto3.client('polly') for textBlock in textBlocks: response = polly.synthesize_speech( OutputFormat='mp3', Text = textBlock, VoiceId = voice ) #Save the audio stream returned by Amazon Polly on Lambda's temp # directory. If there are multiple text blocks, the audio stream # will be combined into a single file. if "AudioStream" in response: with closing(response["AudioStream"]) as stream: output = os.path.join("/tmp/", postId) with open(output, "a") as file: file.write(stream.read()) s3 = boto3.client('s3') s3.upload_file('/tmp/' + postId, os.environ['BUCKET_NAME'], postId + ".mp3") s3.put_object_acl(ACL='public-read', Bucket=os.environ['BUCKET_NAME'], Key= postId + ".mp3") location = s3.get_bucket_location(Bucket=os.environ['BUCKET_NAME']) region = location['LocationConstraint'] if region is None: url_begining = "https://s3.amazonaws.com/" else: url_begining = "https://s3-" + str(region) + ".amazonaws.com/" \ url = url_begining \ + str(os.environ['BUCKET_NAME']) \ + "/" \ + str(postId) \ + ".mp3" #Updating the item in DynamoDB response = table.update_item( Key={'id':postId}, UpdateExpression= "SET #statusAtt = :statusValue, #urlAtt = :urlValue", ExpressionAttributeValues= {':statusValue': 'UPDATED', ':urlValue': url}, ExpressionAttributeNames= {'#statusAtt': 'status', '#urlAtt': 'mp3Url'}, ) return
- 함수 코드는 아래와 같은 기능을 수행합니다.
입력 메시지 (SNS 이벤트)에서 오디오 파일로 변환해야하는 DynamoDB 항목의 ID (게시물 ID)를 검색합니다.
- DynamoDB에서 변환에 필요한 텍스트를 추출합니다. synthesize_speech API의 입력 텍스트 크기 제한이 1,500 자이기 때문에 1,000 자의 블록으로 나누어서 호출합니다. 각각의 블록은 오디오 스트림으로 변환 한 후 다시 결합합니다.
- 텍스트를 오디오 스트림으로 변환합니다.
- 오디오(MP3) 파일을 S3 버킷에 배포합니다.
- S3 버킷 및 새 상태에 대한 참조로 DynamoDB 테이블을 업데이트합니다.
- "New Post" Lambda 함수와 마찬가지로, 이 Lambda 함수와 상호 작용할 수 있는 서비스를 알릴 필요가 있습니다. 이러한 값을 제공하기 위해 다음 환경 변수와 값을 사용합니다:
- DB_TABLE_NAME – DynamoDB 테이블의 이름 (이 경우에는 posts 입니다)
- BUCKET_NAME – MP3 파일을 저장하기 위해 만든 S3 버킷의 이름 (이 예제에서의 버킷 이름은 polly-mp3.awsdemokr.com 입니다.)
- 변환하려는 게시물이 상당히 클 수 있으므로 단일 코드 실행의 최대 길이인 5분 으로 연장합니다.
- "New Post" 기능을 다시 테스트하면, SNS에 주제에서 트리거 되면서 "Convert to Audio"기능도 실행됩니다. Amazon Polly에서 생성한 MP3 파일은 S3 버킷에 저장됩니다. 모든 설정이 완료되었으면 저장합니다.
...
- PostReader_GetPost 라는 함수를 만들것이고, 이전과 마찬가지로 Python 2.7 을 런타임으로 사용하고 기존 역할 로 LambdaPostsRole 을 선택하고 트리거는 지정하지 않습니다.
이번 코드는 매우 짧습니다. 이 함수는 게시물 id (DynamoDB 항목의 id)를 얻고 이 id를 기반으로 모든 정보(오디오 파일이있는 경우 S3 링크 포함)를 반환합니다. 입력 매개 변수가 별표(*)인 경우 좀 더 사용자 친화적인 것으로 만들기 위해 Lambda 함수는 데이터베이스에서 모든 항목을 반환합니다. (항목이 많을 경우 성능이 저하 될 수 있고 오랜 시간이 걸릴 수 있으므로 테스트용으로 사용하지만, 이 방법을 추천하지 않습니다.)
코드 블럭 language py title PostReader_GetPost linenumbers true import boto3 import os from boto3.dynamodb.conditions import Key, Attr def lambda_handler(event, context): postId = event["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)) return items["Items"]
- 여기에서도 DynamoDB 테이블의 이름인 posts 를 함수의 환경 변수로 제공합니다.
다음을 입력 데이터로 함수를 실행하여 함수를 테스트하십시오.
코드 블럭 { "postId": "*" }
- 현재 DynomoDB를 scan 한 결과를 확인할 수 있습니다.
...
- API Gateway 콘솔에서 시작 버튼을 눌러서 API를 생성을 시작합니다.
- 새 API 를 생성할 것이고 API의 리소스 이름은 PostReader 로 할당하고, API 생성 버튼을 클릭합니다.
- API가 생성 된 후, 우리는 두 개의 HTTP 메소드(작업 버튼을 클릭 후 메서드 생성)를 생성하고 CORS 설정을 합니다.
- POST 메서드는 PostReader_NewPost Lambda 함수를 호출합니다.
- GET 메소드의 경우 API는 PostReader_GetPost Lambda 함수를 호출합니다.
- 마지막은은 CORS 활성화(교차 출처 자원 공유) 설정입니다. 이 메소드를 사용하면 다른 호스트 이름이 있는 웹 사이트에서 API를 호출 할 수 있습니다.
- POST 메서드는 PostReader_NewPost Lambda 함수를 호출합니다.
- 반환해야하는 게시물의 ID에 대한 정보를 제공하는 쿼리 매개 변수인 postId에 대해 GET 메서드를 구성합니다.
- GET 메서드를 클릭하고 메서드 요청을 클릭합니다.
- GET 메서드 요청에서 URL 쿼리 문자열 파라미터를 클릭해서 postId 대한 쿼리 문자열 정보를 추가합니다.
- Lambda 함수(PostReader_GetPost)는 입력 데이터를 JSON 형식으로 수신하기 때문에 쿼리 문자열 변수를 JSON 형식으로 매핑하도록 API를 구성해야 합니다. 이를 위해 통합 요청 을 클릭합니다.
쿼리 문자열 파라미터인 postId를 Lambda가 인식할 수 있는 JSON 형태로 다음과 같이 매핑 테이블을 작성하고 저장합니다.
코드 블럭 language js title 쿼리 문자열 변수를 JSON으로 매핑 linenumbers true { "postId" : "$input.params('postId')" }
- GET 메서드를 클릭하고 메서드 요청을 클릭합니다.
API 설정이 완료 되었습니다. 배포를 해서 애플리케이션에서 호출할 수 있는 URL을 얻습니다. 작업 에서 API 배포 를 선택합니다.
API를 Dev 스테이지로 배포를 합니다. 개발, 테스트, 프로덕션에 이르기까지 다양한 스테이지로 나누어서 배포가 가능합니다. 여기서는 dev 스테이지로 배포합니다.
API 배포까지 완료되었습니다. 해당 API를 호출 할 수 있는 URL이 생성되었음을 확인할 수 있습니다.
배포가 완료되면 해당 API를 호출할 수 있는 URL이 표시됩니다. 앞으로 동적 컨텐츠 API 호출은 이 URL로 호출할 것이기에 해당 URL을 메모합니다.
...
- 클라이언트 PC에 패키지 파일을 다운로드 받고 압축을 해제합니다.
- 기존에 생성한 Amazon API Gateway의 URL을 연결하기 위해서 scripts.js 파일을 열어서 API를 배포하고 받은 URL로 수정합니다.
- Amazon S3 버킷을 하나 생성합니다. (S3 버킷 이름은 모든 리전에 유일해야 하므로 이름을 작성할 때 주의합니다.)
- 생성한 S3 버킷에 위에서 제공한 3개의 파일을 업로드 합니다.
- 버킷의 속성에서 정적 웹 사이트 호스팅을 선택하여 활성화 하고, 색인 파일의 이름을 index.html 입력하십시오.
마지막 단계는 우리 웹 사이트에 모든 사람이 액세스 할 수 있도록 버킷의 권한을 변경하는 것입니다. 권한 탭에서 다음 정책을 추가하여 버킷 정책을 편집하십시오. 12번째 줄에 BUCKET_NAME 을 방금 생성한 S3 버킷의 이름으로 교체 하십시오.
코드 블럭 language js title S3 버켓에 대한 접근 권한 설정 { "Version":"2012-10-17", "Statement":[ { "Sid":"PublicReadGetObject", "Effect":"Allow", "Principal":"*", "Action":[ "s3:GetObject" ], "Resource":[ "arn:aws:s3:::BUCKET_NAME/*" ] } ] }
...
- 먼저 음성으로 변환하고자 하는 목소리를 선택합니다. 한국어 지원이 되는 Seoyeon을 선택합니다. 그리고 텍스트를 입력합니다. 한자 변환이 필요할 경우 체크 합니다. 마지막으로 음성 변환 시작 버튼을 누릅니다. API Gateway를 통해서 POST 메서드로 등록 메시지가 전달 됩니다. Lambda 함수에서 바로 인식할 수 있도록 POST Data는 JSON 형태로 전송합니다.
- 게시물이 등록되면 게시물 등록 번호가 자동으로 생성됩니다. 등록된 게시물의 텍스트는 SNS 주제가 트리거 되면서 실행되는 Lambda 함수에 의해 MP3를 생성하고 다음과 같이 게시물 등록 번호로 조회가 가능합니다. 입력하는 텍스트의 크기에 따라 오디오 파일로 변환하는 데 몇 초 또는 몇 분이 걸릴 수 있습니다. 제공한 게시물 정보를 검색하려면 게시물 등록 번호 검색에 게시물 ID 또는 *를 입력하십시오. 조회를 하면 텍스트를 확인하거나 음성으로 변환된 MP3를 재생할 수 있습니다.
...