메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

IT/모바일

구글 앱 엔진(Google App Engine)으로 웹 애플리케이션 시작하기(2)

한빛미디어

|

2008-07-28

|

by HANBIT

11,596

제공 : 한빛 네트워크
저자 : Noah Gift
역자 : 김인희
원문 : Getting Started with the Google App Engine

실전 돌입

이제 helloworld를 뒤로 하고, 교육용 예제 애플리케이션의 소스 코드를 열고 자세히 파헤쳐보자. 우선, 서브버젼에서 소스를 체크아웃하고 예제코드가 있는 디렉토리로 이동한 후에 다음과 같이 입력하면 애플리케이션 실행이 가능하다.
dev_appserver.py .
터미널 윈도우에 애플리케이션을 띄워놓으면 소스코드를 변경했을 때 그 내용이 자동으로 반영된다. 각자가 선호하는 에디터로 change.py를 다른 창으로 열어보면, 이 파일이 애플리케이션의 대부분을 차지함을 알 수 있다. 이 파일의 코드는 다음과 같다.
#!/usr/bin/env python2.5
    #Noah Gift

    import decimal
    import wsgiref.handlers
    import os

    from google.appengine.api import users
    from google.appengine.ext import webapp
    from google.appengine.ext import db
    from google.appengine.ext.webapp import template

    class ChangeModel(db.Model):
        user = db.UserProperty()
        input = db.IntegerProperty()
        date = db.DateTimeProperty(auto_now_add=True)

    class MainPage(webapp.RequestHandler):
        """Main Page View"""

        def get(self):
            user = users.get_current_user()

            if users.get_current_user():
                url = users.create_logout_url(self.request.uri)
                url_linktext = "Logout"
            else:
                url = users.create_login_url(self.request.uri)
                url_linktext = "Login"

            template_values = {
            "url": url,
            "url_linktext": url_linktext,
            }
            path = os.path.join(os.path.dirname(__file__), "index.html")
            self.response.out.write(template.render(path, template_values))

    class Recent(webapp.RequestHandler):
        """Query Last 10 Requests"""

        def get(self):

            #collection
            collection = []
            #grab last 10 records from datastore
            query = ChangeModel.all().order("-date")
            records = query.fetch(limit=10)

            #formats decimal correctly
            for change in records:
                collection.append(decimal.Decimal(change.input)/100)

            template_values = {
            "inputs": collection,
            "records": records,
            }

            path = os.path.join(os.path.dirname(__file__), "query.html")
            self.response.out.write(template.render(path,template_values))

    class Result(webapp.RequestHandler):
        """Returns Page with Results"""
        def __init__(self):
            self.coins = [1,5,10,25]
            self.coin_lookup = {25: "quarters", 10: "dimes", 5: "nickels", 1: "pennies"}

        def get(self):
            #Just grab the latest post
            collection = {}

            #select the latest input from the datastore
            change = db.GqlQuery("SELECT * FROM ChangeModel ORDER BY date DESC LIMIT 1")
            for c in change:
                change_input = c.input

            #coin change logic
            coin = self.coins.pop()
            num, rem  = divmod(change_input, coin)
            if num:
                collection[self.coin_lookup[coin]] = num
            while rem > 0:
                coin = self.coins.pop()
                num, rem = divmod(rem, coin)
                if num:
                    collection[self.coin_lookup[coin]] = num

            template_values = {
            "collection": collection,
            "input": decimal.Decimal(change_input)/100,
            }

            #render template
            path = os.path.join(os.path.dirname(__file__), "result.html")
            self.response.out.write(template.render(path, template_values))

    class Change(webapp.RequestHandler):

        def post(self):
            """Printing Method For Recursive Results and While Results"""
            model = ChangeModel()
            try:
                change_input = decimal.Decimal(self.request.get("content"))
                model.input = int(change_input*100)
                model.put()
                self.redirect("/result")
            except decimal.InvalidOperation:
                path = os.path.join(os.path.dirname(__file__), "submit_error.html")
                self.response.out.write(template.render(path,None))

    def main():
        application = webapp.WSGIApplication([("/", MainPage),
                                            ("/submit_form", Change),
                                            ("/result", Result),
                                            ("/recent", Recent)],
                                            debug=True)
        wsgiref.handlers.CGIHandler().run(application)

    if __name__ == "__main__":
        main()

실용적인 면을 강조하는 튜토리얼답게, 우선 http://greedycoin.appspot.com이나 로컬에서 돌아가는 http://localhost:8080/를 살펴보는 것으로 시작해보자. 호박 색으로 뒤덮인 두 개의 플로팅(floating) 박스가 있는데, 왼쪽은 교환하고자 하는 돈의 액수를 넣는 곳이고, 오른쪽은 메뉴 박스이다. 화려할 수도 이상하게도 보일 수 있는 색과 레이아웃은 장고 템플릿과 CSS를 단순히 조합하여 만들어졌다. 장고 템플릿은 메인 디렉토리에, CSS 파일은 스타일시트에 위치해있다. 장고 템플릿은 GAE와는 상관이 없기 때문에, 이에 대해 잘 알지 못한다면 참고자료에 있는 장고 템플릿 링크를 따라가보기 바란다.

예제 코드를 보았으니, 이제는 GAE 명세서를 좀 더 자세히 알아볼 차례이다. 오른쪽 메뉴의 “Login”는 뛰어난 기능을 갖춘 사용자 인증 API로 제작되었다. 다음은 실제 인증을 담당하는 부분이다.
class MainPage(webapp.RequestHandler):
        """Main Page View"""

        def get(self):
            user = users.get_current_user()

            if users.get_current_user():
                url = users.create_logout_url(self.request.uri)
                url_linktext = "Logout"
            else:
                url = users.create_login_url(self.request.uri)
                url_linktext = "Login"

            template_values = {
            "url": url,
            "url_linktext": url_linktext,
            }
            path = os.path.join(os.path.dirname(__file__), "index.html")
            self.response.out.write(template.render(path, template_values))
Mainpage 클래스는 webapp.RequestHandler를 상속받았으며, get 메소드를 정의하면 사용자의 로그인 여부를 판단할 수 있는 페이지를 만들 수가 있다. 인증 부분 아래를 보면 알겠지만, 템플릿 시스템으로 넘어간 사용자 정보는 장고 템플릿을 통해 index.html로 렌더링된다. 한 가지 놀라운 사실은 페이지 인증을 거치기 위해 구글 유저 계정 데이터베이스를 이용한다 것이 너무나도 쉽다는 점이다. 바로 다음과 같이 말이다.
user = users.get_current_user()

            if users.get_current_user():
이 코드로 이것저것 시도해보면서 인증된 사용자에게만 적용될 수 있는 코드를 추가해보도록 하자. 지금 당장 코드가 어떻게 돌아가는지 이해할 필요는 없고, 기존의 조건문을 활용해서 추가작업을 해보자.
TAG :
댓글 입력
자료실

최근 본 책0