ㄷ목차

목차

0. 설문조사 관련 아키텍처


1. 설문 데이터 수집 만들기

관련 링크: https://github.com/tstachlewski/serverless-survey/

AWS 관리 콘솔의 Lambda 함수를 만들 때 SAM(Serverless Application Model)을 이용해서 기존에 만들어진 Survey를 차용하여 사용할 수 있습니다.

https://y85teb42e2.execute-api.us-east-1.amazonaws.com/Prod/newsurvey

Lambda 함수 코드 1

setup.py
#-*- coding: utf-8 -*-
import yaml
import sys
import json
from yattag import Doc

def lambda_handler(event, context):
    # print(event['requestContext']['queryStringParameters']['email'])
    print(json.dumps(event))
    if event['queryStringParameters'] is not None:
        if event['queryStringParameters'].has_key('email'):
            email = event['queryStringParameters']['email']
        else:
            email = 'anonymous'
    else:
        email = 'anonymous'
    sourceIp = event['requestContext']['identity']['sourceIp']
    requesTime = event['requestContext']['requestTimeEpoch']

    configuration = yaml.load(open("config.yaml").read())
    questions = configuration['Questions'];
    title = configuration['Title'];
    author = configuration['Author'];
    image1 = configuration['Image1'];
    image2 = configuration['Image2'];
    theme = configuration['Theme'];
    
    questionsNames = list()
    for questionIterator in questions:
        questionsNames.append(questionIterator)
    questionsNames.sort()
    
    doc, tag, text = Doc().tagtext()

    with tag('html'):
        with tag('body'):
                            
            doc.stag('br')
            
            with tag('div', align='center'):
                with doc.tag('div', style="font-size: medium;font-weight: bold; font-family: verdana; color:#" + str(theme) + ";"): 
                    text(title)
                    doc.stag('br')
                with doc.tag('div', style="font-size: small; font-weight: bold; font-family: verdana;"):
                    text("by " + author)
                    doc.stag('br')
                    doc.stag('img', src=image1, width="300")
                    doc.stag('br')
                    doc.stag('br')
            
            with tag('form', action = "submitsurvey", style="margin-left: auto; margin-right: auto; width: 70%;"):
                doc.input(name = 'title', type = 'hidden', value = title)
                doc.input(name = 'email', type = 'hidden', value = email)
                doc.input(name = 'sourceIp', type = 'hidden', value = sourceIp)
                doc.input(name = 'requesTime', type = 'hidden', value = requesTime)
                
                for questionName in questionsNames:
                    with tag('div'):
                        questionLabel = questions[questionName]['Label']
                        questionType = questions[questionName]['Type']
                        questionNumber = questions[questionName]['Qnumber']

                        #doc.stag('font', size="4", style="font-weight: bold; font-family: verdana; color:#" + str(theme) + ";")    
                        with doc.tag('div',style="font-size: medium;font-weight: bold; font-family: verdana; color:#" + str(theme) + ";"):
                            with doc.tag('p'):
                                with doc.tag('span', style="font-family: arial, helvetica, sans-serif;padding: 3px 10px 3px 10px;border-radius: 25px;text-align: center;width: 50px;background-color: #377F9F;color: white;font-size : 14px;"):
                                    doc.asis("Q " + questionNumber)
                                with doc.tag('span'):
                                    doc.asis("  " + questionLabel)
                        
                        if (questionType == "Label"):
                            value = questions[questionName]['Value']
                            with doc.tag('a', href=value, target="_blank",):
                                doc.asis(value)
                                doc.stag('br')
                                pass
                        
                        if (questionType == "Text"):
                            with doc.textarea(name = questionNumber, style="width: 100%; border-color: #" + str(theme) + "; " , rows="5", placeholder="최대 한글 300자까지 가능합니다."):
                                pass
                            
                        if (questionType == "ShortText"):  
                            with doc.textarea(name = questionNumber, style="width: 100%; border-color: #" + str(theme) + "; " , rows="1"):
                                pass
                            
                        if (questionType == "Radio"):
                            values = questions[questionName]['Values']
                            with doc.tag('table', style="table-layout: fixed;width: 100%;border-collapse: collapse;border: 1px solid gray;"):
                                with doc.tag('tr'):
                                    for valueIterator in values:
                                        value = questions[questionName]['Values'][valueIterator]
                                        with doc.tag('td', style="font-family: arial, helvetica, sans-serif;text-align: center;width:20%;border: 1px solid gray;padding:10;"):
                                            doc.asis(value);
                                with doc.tag('tr'):
                                    for valueIterator in values:
                                        value = questions[questionName]['Values'][valueIterator]
                                        with doc.tag('td', style="font-family: arial, helvetica, sans-serif;text-align: center;width:20%;border: 1px solid gray;padding:10;"):
                                            doc.input(name = questionNumber, type = 'radio', value = value, style="border-color: #" + str(theme) + "; ")
                            # for valueIterator in values:
                            #     value = questions[questionName]['Values'][valueIterator]
                            #     with doc.tag('div', style="font-size: small; font-weight: normal; font-family: verdana; color:black;"):
                            #         doc.input(name = questionName, type = 'radio', value = value, style="border-color: #" + str(theme) + "; ")
                            #         text(" "+str(value))
                            #         doc.stag('br')
                                
                        if (questionType == "CheckBox"):
                            with tag('fieldset',style="border: 0px; padding: 0px; font-size: small; font-weight: normal; font-family: verdana; color:black;"):
                                values = list(questions[questionName]['Values'])
                                for valueIterator in values:
                                    value = questions[questionName]['Values'][valueIterator]
                                    field_name = questionName + "_" + "".join([ c if c.isalnum() else "_" for c in value.lower() ])
                                    doc.input(name = questionNumber, type = 'hidden', value = "0",style="border-color: #" + str(theme) + "; ")
                                    doc.input(name = questionNumber, id = field_name ,  type = 'checkbox', value = "1", style="border-color: #" + str(theme) + "; ")
                                    text(" "+str(value))
                                    doc.stag('br')
                            
                        doc.stag('br')
                        doc.stag('br')
                with doc.tag('div', style="text-align: center;"):
                    doc.stag('input', type = "submit", value = "설문 완료", style="background-color:#50AEEB;border:none;color:white;padding:10px 30px;text-align: center; text-decoration: none; display: inline-block;font-size: 16px;margin: 4px 2px;border-radius: 12px;cursor: pointer;")




    htmlResult = doc.getvalue()

    return {
            'statusCode': "200",
            'body': htmlResult,
            'headers': {
                'Content-Type': 'text/html; charset=utf-8',
            }
        }

Lambda 함수 코드 2

config.yaml
Title: AWS 2020년 2월 19일 미팅 설문조사
Author: Solutions Architect 김현수
Image1: https://a0.awsstatic.com/libra-css/images/logos/aws_logo_smile_1200x630.png
Image2: https://a0.awsstatic.com/libra-css/images/logos/aws_logo_smile_1200x630.png
Theme: 282828
Questions:
    q1:
        Type: Radio
        Qnumber: 1-1
        Label: AWS와의 금일 미팅에 대해서 만족하십니까?
        Values:
            Value5: 매우 만족
            Value4: 만족
            Value3: 중립
            Value2: 불만족
            Value1: 매우 불만족
    q2:
        Type: Text
        Qnumber: 1-2
        Label: AWS와의 미팅에서 만족한 이유 또는 개선해야 할 점이 있다면 적어주세요.
    q3:
        Type: Radio
        Qnumber: 2-1
        Label: 미팅 시간은 적절하였나요?
        Values:
            Value5: 매우 적절
            Value4: 적절
            Value3: 중립
            Value2: 시간 부족
    q4:
        Type: CheckBox
        Qnumber: 3-1
        Label: 현재 프로젝트와 관련하여 어떤 서비스가 도움이 될 것이라고 생각하십니까?
        Values:
            Value8: "잘 모르겠다"
            Value7: "Amazon Pinpoint"
            Value6: "Amazon API Gateway"
            Value5: "AWS Lambda"
            Value4: "Amazon DynamoDB"
            Value3: "Amazon QuickSight"
            Value2: "Amazon Comprehend"
            Value1: "기타"
    q5:
        Type: Radio
        Qnumber: 4-1
        Label: AWS의 Solutions Architect와의 미팅을 다른 고객들에게 추천하시겠습니까?
        Values:
            Value5: 적극 추천
            Value4: 추천
            Value3: 중립
            Value2: 비추천
            Value1: 절대 비추천
    q6:
        Type: Text
        Qnumber: 4-2
        Label: AWS의 Solutions Architect와의 미팅을 추천하는 이유 또는 개선해야 할 점이 있다면 적어주세요.
    q7:
        Type: Text
        Qnumber: 5-1
        Label: AWS에 요청하고 싶은 내용이 있다면 적어주세요.
    q8:
        Type: Label
        Qnumber: 6-1
        Label: 아래 링크에서 금일 미팅 관련 정보를 제공해 드립니다.
        Value: http://wiki.studydev.com/pages/viewpage.action?pageId=48923317


추가 개선 가능한 부분

동시에 여러명의 서베이 요청을 수행하기 위해서는 DynamoDB의 WCU를 고려해야 합니다.
따라서 아래와 같은 형태의 아키텍쳐를 고려할 수 있습니다.
REST API 2

관련 블로그: Things to Consider When You Build REST APIs with Amazon API Gateway


2. 캠페인 영역

2.1 Segment를 만듭니다.

csv, json 포맷으로 작성하고 등록하면 됩니다. (아래와 같은 형태)


2.2 이메일 템플릿을 제작합니다.

이메일 템플릿
<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
    <font face="Arial">
        <table cellpadding="0" cellspacing="0" width="100%">
            <tbody>
                <tr>
                    <td width="100">
                        <img src="https://a0.awsstatic.com/libra-css/images/logos/aws_logo_smile_1200x630.png" alt="Amazon Web Services Logo" height="50">
                    </td>
                    
                    <td style="text-align:right;padding-right:10px">
                        <font size="2"><a href="https://aws.amazon.com/">aws.amazon.com</a>
                        </font>
                    </td>
                </tr>
            </tbody>
        </table>
        <hr>
        <br>
        <div style="height: 200px; overflow: hidden;text-align:center">
            <img src="https://d1.awsstatic.com/product-marketing/Pinpoint/Pinpoint%20Web%20Illustrations_EngagementManagement-Editorial.08da4e3b599fbbcd18ab5a85b991b4b8077fe509.png" alt="Aamazon Pinpoint Journey" style="height:200px; margin:0px 0 0 0;">
        </div>
        <br>
        <div style="padding:20px;">
            <h4>AWS와의 미팅은 즐거우셨나요?</h4>
            <div> 고객님의 소중한 의견을 모아 더 나은 서비스로 보답하고자 설문조사를 실시하고 있습니다. 잠시만 시간을 내주시면 감사하겠습니다. </div>
            <br>
            <h4>We Hope you had a pleasant trip with Amazon Web Services.</h4>
            <div> Please answer this short survey to share your experience with us. Your valuable feedback will be userd to futher enhance our services. </div>    
        </div>
        
        <br>
        <div style="text-align:center">
            <a href="https://y85teb42e2.execute-api.us-east-1.amazonaws.com/Prod/newsurvey?email={{Address}}">
                <button style="background-color:#50AEEB;border:none;color:white;padding:15px;text-align: center; text-decoration: none; display: inline-block;font-size: 16px;margin: 4px 2px;border-radius: 12px;">설문조사 바로가기 / Start Survey</button>
            </a>
            
        </div>
        <br>
        <font color="white">
            <table bgcolor="#757F88" cellpadding="0" cellspacing="0" width="100%" style="font-size:75%">
                <tbody>
                    <tr>
                        <th width="20"> </th>
                        <th> </th>
                        <th width="20"> </th>
                    </tr>
                    <tr>
                        <td> </td>
                        <td>
                            <ul style="padding-left:20px;">
                                <li> <b>E-MAIL 발송 정보</b> </li>
                                <div> 본 이메일은 금번 미팅에 참석해 주신 분들께만 발송되었습니다. 메일을 더 이상 받지 않으시려면 [<a href="https://aws.amazon.com/">수신거부</a>]를 눌러주시기 바랍니다. </div>
                            </ul>
                            <or>
                            </or>
                        </td>
                        <td> </td>
                    </tr>
                    <tr>
                        <td></td>
                        <td>
                            <div style="white-space:nowrap;text-align: right;">
                            <a href="https://www.facebook.com/amazonwebservices.ko/?brand_redir=153063591397681" target="_blank"><img src="https://www.koreanair.com/etc/clientlibs/koreanair/images/components/footer/icon-fb.png" alt="AWS 페이스북"></a>
                            <a href="https://twitter.com/awscloud" target="_blank"><img src="https://www.koreanair.com/etc/clientlibs/koreanair/images/components/footer/icon-tw.png" alt="AWS 트위터"></a>
                            </div>
                        </td>
                        <td></td>
                    </tr>
                    <tr>
                        <td colspan="3"> </td>
                    </tr>
                    <tr>
                        <td colspan="3"> </td>
                    </tr>
                </tbody>
            </table>
        </font>
    </font>
</body>
</html>


2.3 캠페인을 생성합니다. (채널: 이메일)

동일한 캠페인을 보냈지만, 서로 상이한 이름과 지역 미팅 목적으로 다르게 메시지가 나가는 것을 확인 할 수 있습니다.

설문 조사를 수행 합니다.


2.4 이메일 수신 여부를 확인합니다. 설문 조사 결과도 저장 되는지 확인 합니다.

결과는 DynamoDB Table에서 확인 가능합니다.


2.5 캠페인을 모니터링 합니다.

총 3개의 Endpoint 중에서 Email 주소가 담긴 2개로만 이메일이 발송 되었고, 2개의 이메일이 오픈 되었습니다.
이메일에 있는 링크도 각각 한번씩 눌러서 2개가 클릭 된 것으로 확인됩니다.

필터링 조건을 통해서 데이터를 분석하여 볼 수 있습니다.


3. 캠페인 데이터 수집

캠페인을 진행해서 발송한 고객 중 응답하는 고객이 있을 수 있고 그렇지 않은 고객이 있을 수 있습니다.

- 문자의 경우 스팸 처리가 되거나 또는 읽지 않거나 읽더라도 설문 조사를 참여하지 않는 경우입니다.

- 이메일의 경우 스팸 처리가 되거나 또는 읽더라도 설문 조사를 참여하지 않는 경우입니다. 해당 사용자가 몇 번의 캠페인 요청에도 응답이 없다면 다음 캠페인에서도 동일한 반응이 예상될 수 있습니다. 이럴 때는 발송을 하지 않거나 다시 돌아올 수 있도록 프로모션을 하는 것이 더 좋은 방법이 될 수 있습니다.

- 100만명의 고객이 있다 하더라도 실제 Active하게 반응 하는 고객을 추려 낼 수 있으며, RAW 데이터를 보고 싶을 경우, 이메일 캠페인에 대한 분석 설정을 진행할 수 있습니다.

실습 문서:


4. 캠페인 / 설문 데이터 분석

설문 조사 결과를 분석하고 QuickSight를 통하여 시각화를 할 수 있습니다. 예시로 아래와 같은 방법을 활용할 수 있습니다.

관련 블로그: https://aws.amazon.com/blogs/database/how-to-perform-advanced-analytics-and-build-visualizations-of-your-amazon-dynamodb-data-by-using-amazon-athena/

참조 할 만한 정보

관련 이미지



DynamoDB와 Federated Query

AWS Amazon Athena와 DynamoDB와 QuickSight를 활용하는 방법: https://aws.amazon.com/jp/blogs/news/athena-federated-query-dynamodb-quicksight/

https://athena-in-action.workshop.aws/40-federatedquery/402-dynamodb-connector.html



  • 레이블 없음