엔드 투 엔드 예

이 문서에서는 받은편지함에서 직접 메일링 리스트 구독 확인을 요청하는 주석이 달린 이메일을 보내고 Datastore에서 구독을 수집하는 App Engine 앱을 Python으로 빌드하는 방법을 설명합니다.

기본 요건 및 프로젝트 설정

이 가이드에서는 개발자가 이미 App Engine SDK를 설치했고 App Engine 프로젝트를 생성, 실행, 게시하는 방법을 알고 있다고 가정합니다.

먼저 프로젝트 디렉터리를 만듭니다. 애플리케이션의 모든 파일을 이 디렉터리에 넣습니다.

app.yaml라는 파일에 다음 코드를 복사하고 {{ APPID }} 자리표시자를 고유한 App Engine 앱 ID로 바꿉니다.

application: {{ APPID }}
version: 1
runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /.*
  script: main.app

libraries:
- name: jinja2
  version: latest

App Engine 프로젝트 폴더에 main.py라는 파일을 만들고 다음 코드를 복사하여 구독을 수집 및 나열하고 주석이 달린 이메일을 전송하기 위한 핸들러를 설정합니다.

import webapp2

from emailsender import EmailSender
from subscribe import SubscribeHandler

app = webapp2.WSGIApplication([('/', SubscribeHandler), ('/email', EmailSender)], debug=True)

이메일에 구조화된 데이터 추가

사용자에게 메일링 리스트 구독을 확인하도록 요청하는 간단한 이메일부터 시작하겠습니다.

<html>
  <head>
    <title>Please confirm your subscription to Mailing-List XYZ?</title>
  </head>
  <body>
    <p>
      Dear John, please confirm that you wish to be subscribed to the
      mailing list XYZ
    </p>
  </body>
</html>

지원되는 형식 (JSON-LD 또는 마이크로데이터) 중 하나로 구조화된 데이터를 이메일의 head에 추가하여 식당을 정의하고 OneClickAction을 추가할 수 있습니다. Gmail은 OneClickAction를 지원하며 사용자가 받은편지함에서 구독을 확인할 수 있도록 특정 UI를 표시합니다.

다음 마크업을 mail_template.html 파일에 복사합니다.

JSON-LD

<html>
  <head>
  <title>Please confirm your subscription to Mailing-List XYZ?</title>
  </head>
  <body>
    <script type="application/ld+json">
    {
      "@context": "http://schema.org",
      "@type": "EmailMessage",
      "potentialAction": {
        "@type": "ConfirmAction",
        "name": "Confirm Subscription",
        "handler": {
          "@type": "HttpActionHandler",
          "url": "{{ confirm_url }}",
          "method": "http://schema.org/HttpRequestMethod/POST",
        }
      },
      "description": "Confirm subscription to mailing list XYZ"
    }
    </script>
    <p>
      Dear John, please confirm that you wish to be subscribed to the mailing list XYZ.
    </p>
  </body>
</html>

마이크로데이터

<html>
  <head>
    <title>Please confirm your subscription to Mailing-List XYZ?</title>
  </head>
  <body>
    <div itemscope itemtype="http://schema.org/EmailMessage">
      <div itemprop="potentialAction" itemscope itemtype="http://schema.org/ConfirmAction">
        <meta itemprop="name" content="Approve Expense"/>
        <div itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler">
          <link itemprop="url" href="https://myexpenses.com/approve?expenseId=abc123"/>
          <meta itemprop="url" content="{{ confirm_url }}"/>
          <link itemprop="method" href="http://schema.org/HttpRequestMethod/POST"/>
        </div>
      </div>
      <meta itemprop="description" content="Approval request for John's $10.13 expense for office supplies"/>
    </div>
    <p>
      Dear John, please confirm that you wish to be subscribed to the mailing list XYZ.
    </p>
  </body>
</html>

위의 구조화된 데이터는 'XYZ'라는 메일링 리스트와 ConfirmAction에 대해 설명합니다. 이 작업의 핸들러는 url 속성에 지정된 URL로 POST 요청을 전송하는 HttpActionHandler입니다.

사용자에게 구독 요청 전송

다음 코드를 App Engine 프로젝트 폴더에 있는 emailsender.py 파일에 복사합니다.

import jinja2
import os
import webapp2

from google.appengine.api import mail
from google.appengine.api import users

from urlparse import urlparse

class EmailSender(webapp2.RequestHandler):

  def get(self):
    # require users to be logged in to send emails
    user = users.get_current_user()
    if not user:
      self.redirect(users.create_login_url(self.request.uri))
      return

    email = user.email()

    # The confirm url corresponds to the App Engine app url
    pr = urlparse(self.request.url)
    confirm_url = '%s://%s?user=%s' % (pr.scheme, pr.netloc, user.user_id())

    # load the email template and replace the placeholder with the confirm url
    jinja_environment = jinja2.Environment(
        loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
    template = jinja_environment.get_template('mail_template.html')
    email_body = template.render({'confirm_url': confirm_url})

    message = mail.EmailMessage(
        sender = email,
        to = email,
        subject = 'Please confirm your subscription to Mailing-List XYZ',
        html = email_body)

    try:
      message.send()
      self.response.write('OK')
    except:
      self.error(500)

EmailSender 클래스에서 이메일 주소를 가져올 수 있도록 사용자가 로그인해야 합니다. 그런 다음 mail_template.html에서 이메일 본문을 로드하고 그 안의 confirm_url 자리표시자를 App Engine 앱의 루트 URL (https://APP-ID.appspot.com)로 대체하고 현재 로그인한 사용자에게 이메일을 전송합니다.

구독 수집 및 나열

다음 코드를 App Engine 프로젝트 폴더에 있는 subscribe.py 파일에 복사합니다.

import webapp2

from emailsender import EmailSender
from google.appengine.ext import db


class SubscribeHandler(webapp2.RequestHandler):

  def post(self):
    user_id = self.request.get('user')

    # insert the subscription into the Datastore
    subscription = Subscription(user_id=user_id)
    subscription.put()

  def get(self):
    # retrieve up to 1000 subscriptions from the Datastore
    subscriptions = Subscription.all().fetch(1000)

    if not subscriptions:
      self.response.write('No subscriptions')
      return

    count = len(subscriptions)

    for s in subscriptions:
      self.response.write('%s subscribed<br/>' % (s.user_id))

    self.response.write('<br/>')
    self.response.write('%d subscriptions.' % (count))


class Subscription(db.Model):
    user_id = db.TextProperty(required=True)

다음 예와 같이 사용자에 해당하는 SubscriptionsHandlerclass listens to bothPOSTandGETrequests sent to the app root url (https://APP-ID.appspot.com).POSTrequests are used by Gmail to insert new subscriptions including theuser_id` 매개변수입니다.

https://subscribe.appspot.com/?user_id=123abcd

요청 핸들러는 필요한 user_id가 정의되었는지 확인한 후 구독을 Datastore에 저장합니다. 그러면 요청이 성공했음을 알리기 위해 HTTP 200 응답 코드가 Gmail로 다시 전송됩니다. 요청에 필수 필드가 포함되지 않은 경우 요청 핸들러는 잘못된 요청을 알리는 HTTP 400 응답 코드를 반환합니다.

앱 루트 URL에 대한 GET 요청은 수집된 구독을 나열하는 데 사용됩니다. 요청 핸들러는 먼저 Datastore에서 모든 구독을 가져온 후 간단한 카운터와 함께 페이지에 출력합니다.

앱 테스트

App Engine에 앱을 배포하고 https://APP-ID.appspot.com/email (APP-ID을 App Engine 앱 ID로 대체)로 이동하여 주석이 달린 이메일을 자신에게 전송합니다.

Gmail의 작업

앱을 배포하고 일부 구독을 삽입한 후 https://APP-ID.appspot.com에서 앱을 방문하여 구독을 요약한 페이지를 가져옵니다.