본문 바로가기

Programming/Python

웹서버를 시작하기 위해 어떤 프레임 워크를 고를 것인가? 2탄

전편에 이어서.. 6장도 합니다. 대충 해야지..

이거 내가 왜시작했지...


여러분 해석이 개판입니다. 요약만 보세요..





6 Frameworks in Action

For each framework let's take a look at making an app called wut4lunch, a social network to tell the whole internet what you ate for lunch. Free startup idea right there, totally a gamechanger. The application will be a simple interface that allows users to post what they had for lunch and to see a list of what other users ate. The home page will look like this when we're done.

각각의 프레임워크들을 이용해여 wut4lunch (점심머먹지?) 앱을 만들어보자. 인터넷의 사람들이 니가 뭘 점심으로 먹을 지 말해주는 SNS이다. 자, 창업 아이디어가 바로 여기있다. 완전히 gamechanger 이다(?). 이 어플리케이션은 유저가 그들이 점심으로 무엇을 먹었는지 포스팅하고, 다른 사람들이 먹은 리스트를 볼수 있는 간단한 인터페이스를 가진다. 우리가 작업을 끝내면 아래와 같이 보일거다.  

6.1 Demo App with Flask

The shortest implementation clocks in at 34 lines of Python and a single 22 line Jinja template. First we have some housekeeping tasks to do, like initializing our app and pulling in our ORM.

가장 짧은 구현이다. 파이썬 34줄과, 22줄짜리 진자 템플릿을 이용했다. 처음으로, 우리가 ORM을 이용해서 우리의 app을 시작하는 것처럼, 우리는 몇가지 잡일을 좀 해야한다. 


from flask import Flask # For this example we'll use SQLAlchemy, a popular ORM that supports a # variety of backends including SQLite, MySQL, and PostgreSQL from flask.ext.sqlalchemy import SQLAlchemy app = Flask(__name__) # We'll just use SQLite here so we don't need an external database app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db' db = SQLAlchemy(app)


Now let's take a look at our model, which will remain almost the same for our other two examples as well.

이제 우리의 모델을 살펴보자. 다른 두가지의 예에도 이것은 거의 비슷하게 나올것이다. 


class Lunch(db.Model): """A single lunch""" id = db.Column(db.Integer, primary_key=True) submitter = db.Column(db.String(63)) food = db.Column(db.String(255))


Wow, that's pretty easy. The hardest part was finding the right SQLAlchemy data types and picking a length for our String fields in the database. Using our models is also extremely simple, thanks to the SQLAlchemy query syntax we'll see later.

와 이건 엄청나게 쉽다. 가장 어려운 부분은 SQLAlchemy 데이터 타입을 정확하게 쓰고, 우리의 데이터베이스를 위해서 문자열의 길이를 구하는 부분이었다. 우리의 모델이 엄청나게 간단하기때문에 SQLAlchemy 쿼리문법에 감사하자. 나중에 또 봐요~

Building our submission form is just as easy. After importing Flask-WTForms and the correct field types, you can see the form looks quite a bit like our model. The main difference is the new submit button and prompts for the food and submitter name fields.

우리의 제출 form을 만드는 것 또한 쉽다. Flast-WTForms를 이식한 후에, 적당히 타입을 맞추면, 우리는 우리 모델과 정말 비슷한 모양이 된다는걸 알 수 있다. 주요한 차이는 이름과 음식을 제출하는 부분에 새로운 확인버튼과 입력부분이 생겼다는 것이다. 

The SECRET_KEY field in the app config is used by WTForms to create CSRF tokens. It is also used by itsdangerous (included in Flask) to sign cookies and other data.

SECRET_KEY 속성은 WTForms를 이용해 CSRF토큰을 사용해서 만들었다. 이것은 itsdangerous(플라스크에 포함되어있음) 를 이용해서 쿠키나 데이터를 확인할때 쓰인다. 

from flask.ext.wtf import Form from wtforms.fields import StringField, SubmitField app.config['SECRET_KEY'] = 'please, tell nobody' class LunchForm(Form): submitter = StringField(u'Hi, my name is') food = StringField(u'and I ate') # submit button will read "share my lunch!" submit = SubmitField(u'share my lunch!')

Making the form show up in the browser means the template has to have it. We'll pass that in below.

꼭 있어야죠~, 브라우저애서 보여질 폼 부분을 만듭니다. 아래에 보여줄께요. 


from flask import render_template @app.route("/") def root(): lunches = Lunch.query.all() form = LunchForm() return render_template('index.html', form=form, lunches=lunches)


Alright, what just happened? We got a list of all the lunches that have already been posted with Lunch.query.all(), and instantiated a form to let the user post their own gastronomic adventure. For simplicity, the variables are passed into the template with the same name, but this isn't required.

좋습니다, 어떻습니까? 우린 Lunch.query.all()을 이용해서 모든 점심밥을 다 모았네요, 또 유저들이 그들의 위장들의 대모험을 포스팅 할수 있게 하는 폼도 인스턴스화 시켰습니다. 간단히 말해서, 템플릿에 어려 같은 이름으로 변수들이 들어갔지만, 필요가 없네요.


<
html
>
<title>Wut 4 Lunch</title> <b>What are people eating?</b> <p>Wut4Lunch is the latest social network where you can tell all your friends 

about your noontime repast!</p> 


Here's the real meat of the template, where we loop through all the lunches that have been eaten and display them in a <ul>. This almost identical to the looping example we saw earlier.

여기 템플릿에 진짜 먹을것이 있다. 우리는 반복적으로 <ul>안에 그들이 먹은 모든 점심을 나타냈다. 이것은 우리가 앞에서 보여준 루프와 거의 동일하다. 


<ul> {% for lunch in lunches %} <li><strong>{{ lunch.submitter|safe }}</strong> just ate <strong>{{ lunch.food|safe }}</strong> {% else %} <li><em>Nobody has eaten lunch, you must all be starving!</em></li> {% endfor %} </ul> <b>What are YOU eating?</b> <form method="POST" action="/new"> {{ form.hidden_tag() }} {{ form.submitter.label }} {{ form.submitter(size=40) }} <br/> {{ form.food.label }} {{ form.food(size=50) }} <br/> {{ form.submit }} </form> </html>


The <form> section of the template just renders the form labels and inputs from the WTForm object we passed into the template in the root() view. When the form is submitted, it'll send a POST request to the /new endpoint which will be processed by the function below.

템플릿의 <form>섹션은 root() 뷰 안에있는 템플릿을 이용해 WTFrom 객체로 label과 input들을 그려준다. form이 입력되었을때, 아래에서 나오는 함수에 대해 POST요청을 /new 지점에 보내게 된다. 


from flask import url_for, redirect @app.route(u'/new', methods=[u'POST']) def newlunch(): form = LunchForm() if form.validate_on_submit(): lunch = Lunch() form.populate_obj(lunch) db.session.add(lunch) db.session.commit() return redirect(url_for('root'))


After validating the form data, we put the contents into one of our Model objects and commit it to the database. Once we've stored the lunch in the database it'll show up in the list of lunches people have eaten.

데이터 검증이 끝나면, 우리는 내용을 우리의 모델객체에 담고, 데이터베이스에 입력하게 된다. 한번 우리의 점심을 데이터베이스에 입력하면, 그것은 사람들이 먹은 점심 목록에 포함된다. 

if __name__ == "__main__": db.create_all() # make our sqlalchemy tables app.run()

Finally, we have to do a (very) little bit of work to actually run our app. Using SQLAlchemy we create the table we use to store lunches, then start running the route handlers we wrote.

마지막으로, 우리는 우리의 앱이 실제로 동작하기 위해서 약간 해야할 것이 있다.  SQLAlchemy를 사용하여 저장한 점심들을 이용한 표를 만들었는데, 그다음, 우리가 쓴 루트 핸들러를 시작해야 한다. 

6.2 Demo App with Django

The Django version of wut4lunch is similar to the Flask version, but is spread across several files in the Django project. First, let's look at the most similar portion: the database model. The only difference between this and the SQLAlchemy version is the slightly different syntax for declaring a database field that holds text.
 점심머먹지? 에 사용할 장고 버전은 플라스크와 비슷하다. 그러나 여러 장고 프로젝트 파일에 걸쳐서 퍼져있다. 첫째로, 우리는 제일 비슷한 부분부터 살펴볼것이다. 바로 데이타 베이스 모델이다. SQLAlchemy 버전과의 유일한 차이점은, 텍스트를 포함하는 데이터베이스를 선언할때의 문법이 약간 다르다는 것이다. 

# from wut4lunch/models.py from django.db import models class Lunch(models.Model): submitter = models.CharField(max_length=63) food = models.CharField(max_length=255)

On to the form system. Unlike Flask, Django has a built-in form system that we can use. It looks much like the WTForms module we used in Flask with different syntax.

폼 구조를 보자. 플라스크와는 다르게 장고는 우리가 쓸수있는 이미 내장 폼구조가 있다. 이건 우리가 플라스크에서 다른 문법을 사용했던 WTForms 모듈과 비슷하게 생겼다. 


from django import forms from django.http import HttpResponse from django.shortcuts import render, redirect from .models import Lunch # Create your views here. class LunchForm(forms.Form): """Form object. Looks a lot like the WTForms Flask example""" submitter = forms.CharField(label='Your name') food = forms.CharField(label='What did you eat?')


Now we just need to make an instance of LunchForm to pass in to our template.

이제, 우리는 우리의 템플릿을 보여줄, LunchForm 인스턴스를 생성만 하면된다. 


lunch_form = LunchForm(auto_id=False) def index(request): lunches = Lunch.objects.all() return render( request, 'wut4lunch/index.html', { 'lunches': lunches, 'form': lunch_form, } )


The render function is a Django shortcut that takes the request, the template path, and a context dict. Similar to Flask's render_template, but it also takes the incoming request.

render함수는 dict내용과, 템플릿 경로, 요청을 처리해주는 지름길이다. 플라스크의 render_templete와 유사하지만, 이것은 요청도 처리할 수 있다. 


def newlunch(request): l = Lunch() l.submitter = request.POST['submitter'] l.food = request.POST['food'] l.save() return redirect('home')


Saving the form response to the database is different, instead of using a global database session Django lets us call the model's .save() method and handles session management transparently. Neat!


데이터베이스에 반응하여 폼을 저장하는것은 좀 다르다. 글로벌한 데이터베이스를 사용하는 대신에, 장고 세션은 모델에 있는 .save() 메소드를 호출하도록하고, 세션 관리자는 보이지 않는다. 우왁!


Django provides some nice features for us to manage the lunches that users have submitted, so we can delete lunches that aren't appropriate for our site. Flask and Pyramid don't provide this automatically, and not having to write Yet Another Admin Page when making a Django app is certainly a feature. Developer time isn't free! All we had to do to tell Django-admin about our models is add two lines towut4lunch/admin.py.

장고는 유저들이 점심을 제출한것을 관리하기위한, 몇기자 좋은 기능들을 제공하는데, 우리는 그들의 점심이 우리 사이트랑 적절하지 않으면 지울 수 있다. 플라스크와 피라미드는 이런것을 자동화해서 제공하지 않고, 특별히 다른 admin페이지 작성할만한 것을 가지고 있지도 않다. 개발자들의 시간은 공짜가 아니다! 우리는 우리 모델에 관한 장고-admin 이 wut4lunch/admin.py에 두 라인만 추가하면 된다는것을 꼭 말해야 한다!


from wut4lunch.models import Lunch 

admin.site.register(Lunch)


Bam. And now we can add and delete entries without doing any extra work.

Lastly, let's take a look at the differences in the homepage template.

빰! 이제 우리는 어떤 야근도 없이, 추가하고 지울 수 있다. 마지막으로, 우리는 홈페이지 템플릿의 차이를 살펴보자. 


<ul> {% for lunch in lunches %} <li><strong>{{ lunch.submitter }}</strong> just ate <strong>{{ lunch.food }}</strong></li> {% empty %} <em>Nobody has eaten lunch, you must all be starving!</em> {% endfor %} </ul>

Django has a handy shortcut for referencing other views in your pages. The url tag makes it possible for you to restructure the URLs your application serves without breaking your views. This works because the url tag looks up the URL of the view mentioned on the fly.

장고는 우리의 페이지를 다른 방법으로 보여주는 쉬운 지름길이 있다. url태그를 이용하면, 우리가 보는 화면을 망가뜨리지 않고 당신의 어플리케이션이 URL을 재구성해서 제공하는것이 가능하다. 이것은 url태그가 즉석에서 화면의 URL을 찾아내기 때문에 가능하다. 


<form action="{% url 'newlunch' %}" method="post"> {% csrf_token %} {{ form.as_ul }} <input type="submit" value="I ate this!" /> </form>


The form is rendered with different syntax, and we need to include a CSRF token manually in the form body, but these differences are mostly cosmetic.

form은 다른 문법으로 그려지는데, 우리는 CSRF토큰을 수동으로 이용할 필요가 있지만, 이것들의 차이는 거의 예쁘게 꾸미는 부분이다. 


6.3 Demo App with Pyramid

Finally, let's take a look at the same program in Pyramid. The biggest difference from Django and Flask here is the templating. Changing the Jinja2 template very slightly was enough to solve our problem in Django. Not so this time, Pyramid's Chameleon template syntax is more reminiscent of XSLT than anything else.
마지막으로, 우리는 같은 프로그램을 피라미드를 통해 살펴보고자 한다. 장고, 플라스크와 가장 큰 차이점은, 템플릿이다. 진자2템플릿을 아주 천천히 바꿈으로써, 장고에서 있었던 문제들을 충분히 해결했다. 이번에 보여줄 것은 아니지만, 피라미드의 카멜레온템플릿 문법은 다른 어떤것 보다 XSLT를 떠올리게 한다. 

<!-- pyramid_wut4lunch/templates/index.pt --> <div tal:condition="lunches"> <ul> <div tal:repeat="lunch lunches" tal:omit-tag=""> <li tal:content="string:${lunch.submitter} just ate ${lunch.food}"/> </div> </ul> </div> <div tal:condition="not:lunches"> <em>Nobody has eaten lunch, you must all be starving!</em> </div>

Like in Django templates, a lack of the for-else-endfor construct makes the logic slightly more verbose. In this case, we end up with if-for and if-not-for blocks to provide the same functionality. Templates that use XHTML tags may seem foreign after using Django- and AngularJS-style templates that use {{ or {% for control structures and conditionals.
장고템플릿 처럼, for-else-endfor구문을 적게 만드는 것은 로직을 좀 장황하게 보이게 한다. 이 경우에, 우리는 결국 기능적으로는 같은 if-for 구문과 if-not-for 블록을 가지게 된다. XHTML 태그를 이용한 팀플릿은 장고- 와 AngularJS 스타일에서 {{ 나 {%를 사용한 이후에 보니까 좀 이질적으로 보인다. 

One of the big upsides to the Chameleon templating style is that your editor of choice will highlight the syntax correctly, since the templates are valid XHTML. For Django and Flask templates your editor needs to have support for those templating languages to highlight correctly.
카멜레온 템플릿 스타일에서 중요한것 하나는, 당신이 고른 에디터가 문법적으로 맞을때 강조를 해준다는것이다. 장고나 플라스크 템플릿은 맞게 강조하기 위해서, 새로운 템플릿 언어 를 설치 해야 한다. 

<b>What are YOU eating?</b> <form method="POST" action="/newlunch"> Name: ${form.text("submitter", size=40)} <br/> What did you eat? ${form.text("food", size=40)} <br/> <input type="submit" value="I ate this!" /> </form> </html>

The form rendering is slightly more verbose in Pyramid because thepyramid_simpleform doesn't have an equivalent to Django forms' form.as_ulfunction, which renders all the form fields automatically.

form 렌더링은 좀더 장황하게 보인다. 왜냐하면, pyramid_simpleform 은 장고의 form.as_ul 함수처럼 모든 form 속성을 자동으로 그려주는 것 같은 기능을 하는게 없다. 

Now let's see what backs the application. First, we'll define the form we need and render our homepage.

이제 우리는 어플리케이션의 뒷단은 어떤지 볼것이다. 첫번쨰로, 우리는 우리의 홈페이지를 그려주기 위해, form을 정의 할 것이다. 


# pyramid_wut4lunch/views.py class LunchSchema(Schema): submitter = validators.UnicodeString() food = validators.UnicodeString() @view_config(route_name='home', renderer='templates/index.pt') def home(request): lunches = DBSession.query(Lunch).all() form = Form(request, schema=LunchSchema()) return {'lunches': lunches, 'form': FormRenderer(form)}


The query syntax to retrieve all the lunches is familiar from Flask because both demo applications use the popular SQLAlchemy ORM to provide persistent storage. In Pyramid lets you return your template's context dictionary directly instead of needing to call a special render function. The @view_config decorator automatically passes the returned context to the template to be rendered. Being able to skip calling therender method makes functions written for Pyramid views easier to test, since the data they return isn't obscured in a template renderer object.

모든 점심들을 불러오기 위한 쿼리 문법은 플라스크와 비슷해보인다. 이는 데모어플이 저장을 위해서 대중적인 SQLAlchemy ORM을 제공하기 때문이다. 피라미드에서는 , 특별한 render 함수를 호출하는 대신에 바로 당신의 내용이 담긴 템플릿 딕셔너리를 반환해 줍니다. @view_config 데코레이터가 자동으로 그려진 내용을 반환해 줍니다. render메쏘드를 호출하는것을 생략 할 수 있는것은 피라미드의 뷰에서 함수를 만들고 테스트하는것을 더 쉽게 해줍니다. 


@view_config(route_name='newlunch', renderer='templates/index.pt', request_method='POST') def newlunch(request): l = Lunch( submitter=request.POST.get('submitter', 'nobody'), food=request.POST.get('food', 'nothing'), ) with transaction.manager: DBSession.add(l) raise exc.HTTPSeeOther('/')


Form data is easy to retrieve from Pyramid's request object, which automatically parsed the form POST data into a dict that we can access. To prevent multiple concurrent requests from all accessing the database at the same time, the ZopeTransactions module provides context managers for grouping database writes into logical transactions and prevent threads of your application from stomping on each others' changes, which can be a problem if your views share a global session and your app receives a lot of traffic.

폼 데이터는 피라미드의 우리가 접근한 dict안에 POST를 이용해 자동적으로 담아진 요청 객체로부터 검색하기가 쉽다. 여러개의 요청이 동시에 발생해 데이터베이스에 동시에 접근하는 것을 막기 위해, ZopeTransactions 모듈이 데이터베이스를 그룹화하여 논리적인 트랜잭션으로 써서, 여러명의 수정을 합쳐서 뭉개지는것을 막기 위한 context manager를 제공한다.

7 Summary

Pyramid is the most flexible of the three. It can be used for small apps as we've seen here, but it also powers big-name sites like Dropbox. Open Source communities likeFedora choose it for applications like their community badges system, which receives information about events from many of the project's tools to award achievement-style badges to users. One of the most common complaints about Pyramid is that it presents so many options it can be intimidating to start a new project.
피라미드는 셋중에 가장 유연한 구조를 지닌다. 이건 우리가 여기서 살펴본 작은 앱에도 이용될 수 있지만, 드롭박스처럼 큰 사이트도 구동할 수 있다. 페도라 같은 오픈소스 커뮤니티에서, 그들의 커뮤니티 badge system으로 피라미드를 선택했다. 뱃지 시스템은 여러 많은 프로젝트 도구들을 통해 어떤 목표를 달성했는지에 따라 상을 주는 이벤트를 통해  정보를 알려준다. 피라미드의 가장 흔한 단점은 새로운 프로젝트를 즉시 시작하기에 너무 많은 선택지를 제공한다는 것이다. 

By far the most popular framework is Django, and the list of sites that use it is impressive. Bitbucket, Pinterest, Instagram, and The Onion use Django for all or part of their sites. For sites that have common requirements, Django chooses very sane defaults and because of this it has become a popular choice for mid- to large-sized web applications. 
가장 유명한 프레임 워크는 장고이다. 아마 이것을 이용한 사이트 목록을 보면 감명받을것이다. Bitbucket, Pinterest, Instagram, Onion 이 그들의 사이트의 일부분이나 전체를 장고를 사용해 만들었다. 그들의 사이트는 흔한 요청을 가지고 있으며, 장고는 아주 비슷한 기본값들을 골랐고, 그건 그들이 중급-대형의 웹 어플리케이션들의 대중적인 선택이 되었기 때문이다. 


Flask is great for developers working on small projects that need a fast way to make a simple, Python-powered web site. It powers loads of small one-off tools, or simple web interfaces built over existing APIs. Backend projects that need a simple web interface that is fast to develop and will require little configuration often benefit from Flask on the frontend, like jitviewer which provides a web interface for inspecting PyPy just-in-time compiler logs.

플라스크는 파이썬을 이용해 빠르게 간단하고 작은 프로젝트를 개발하는 개발자들에게 아주 좋다. 하나의 간단한 도구만 가지고 구동이 가능하며, 기존의 API를 가지고 간단한 웹 인터페이스를 만들 수 있다. 백엔드 프로젝트에서는 간단한 웹인터페이스가 빠르게 개발 가능하고, 적은 설정만 요구하는 플라스크의 프론트 엔드가 이득을 줄 수 있다. jitviewer같이 PyPy 를 통해 실시간 컴파일러 로그를 볼수 있는 웹 인터페이스 말이다. 


All three frameworks came up with a solution to our small list of requirements, and we've been able to see where they differ. Those differences aren't just cosmetic, and they will change how you design your product and how fast you ship new features and fixes. Since our example was small, we've seen where Flask shines and how Django can feel clunky on a small scale. Pyramid's flexibility didn't become a factor because our requirements stayed the same, but in the real world new requirements are thrown in constantly.

이 세가지 프레임워크를 우리의 작은 요구에 맞춘 솔루션을 통해서 알아보았고, 우리는 어떻게 그것들이 다른지 보았다. 세가지의 다른점은 그저 시각적인 부분이 아니라, 그들은 당신의 생산물을 어떠헥 디자인 할것인지, 얼마나 빠르게 당신의 기능들과 수정들을 출시 해 낼 수 있는지 에도 차이가 있었다. 우리의 예제가 작아서, 우리는 플라스크가 좋아보이고, 장고가 작은 규모에서는 투박하게 느껴진다. 우리의 요구가 계속 일정했기 때문에, 피라미드의 유연성은 와닿지 않았다. 하지만 실제 세계에서는 새로운 요구사항이 지속적으로 전달된다. 


7.1 Credits

Logos in the title image are from the Flask Django and Pyramid project web sites.

This article owes many thanks to its reviewers, Remy DeCausemaker, Ross Delinger, and Liam Middlebrook, for tolerating many early drafts.

In its current form this article incorporates comments and corrections from Adam Chainz, bendwarn, Sergei Maertens, Tom Leo, and wichert. (alphabetical order)

  




크레딧은 해석하지 않았습니다... 

결국 저는 플라스크로 프로젝트를 하는것으로 결정했습니다. 


뭐 별거 있겠어...? 간단하게 하자.. 간단하게...