Flask에서 설계 결정(Design decisions)

만약 당신이 Flask가 다른 방식도 아니고 특정한 방식으로 운영되는지 궁금하다면, 이 섹션은 당신을 위한 것이다. 이것은 당신에게 다른 프레임워크와 비교하였을때, 처음에는 임의적이고 놀랍게 보이는 설계 결정들에 대한 아이디어를 줄 것이다.

명시적 어플리케이션 객체(The Explicit Application Object)

WSGI 기반 파이썬 웹 어플리케이션은 실제 어플리케이션으로 구현된 하나의 중앙 컬러블(callable) 객체를 가지고 있어야 한다. 이것은 Flask에서 Flask 클래스의 객체이다. 각각의 Flask 어플리케이션은 자신이 이 클래스의 객체를 생성해야만 하고, 모듈의 이름을 넘여야 한다. 하지만 왜 Flask 자신이 하지 않는 것일까?

다음은 명시적 어플리케이션 객체가 없는 코드는:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello World!'

이것처럼 보이게 될 것이다:

from hypothetical_flask import route

@route('/')
def index():
    return 'Hello World!'

여기에는 세가지 주요한 이유가 있다. 제일 중요한 이유는 내시적 어플리케이션 객체들(implicit application objects)은 매 번마다 하나의 변수를 필요로 하기 때문이다. 다량의 어플리케이션들을 유지하는 것처럼, 하나의 어플리케이션 객체로 여러개의 어플리케이션들을 속이는 방법도 있다. 하지만 여기서는 자세히 설명하지 않는 몇몇의 문제들을 발생시킨다. 지금 질문은 이렇다: 똑같은 상황에 하나의 어플리케이션보다도 더 많이 마이크로프레임워크를 필요로 하는때는 언제인가? 이에 대한 좋은 예시는 단위테스트이다. 당신이 특정한 기능을 테스트할 때 미니멀 어플리케이션을 만드는 것이 매우 도움이 될 것이다. 어플리케이션 객체가 삭제가 된다면, 어플리케이션에 할당된 모든 것은 해제될 것이다.

명시적 객체들이 코드 내에 있을때 가능해진 또 다른 것은 특정 기능을 바꾸기 위해서 기본 클래스를 하위로 분리시킬 수 있다(:class:’~flask.Flask’). 만약 객체가 노출되지 않은 클레스를 기반으로 예정보다 빨리 객체가 만들어졌다면 해킹을 제외하면 이는 불가능하다.

그러나 왜 Flask가 저 클래스의 명시적 인스턴스 생성에 의존하는 또 다른 매우 중요한 이유가 있다. : 패키지 네임. 언제나 Flask 인스턴스를 생성할때마다, 보통 패키지 네임으로서 ‘__name__’으로 전달한다. Flask는 당신의 모듈에 관련된 적절히 리소스를 부르는 것은 앞의 정보에 의존한다. 반영을 위해 Python의 외적인 지원의 도움으로서 어디에 템플릿과 고정(static) 파일들이 저장되었는지 찾기 위해 패키지에 접속하는 것이 가능해진다. (참조 open_resource()). 현재 명확하게도 어떠한 환경 설정도 필요하지 않으며 어플리케이션 모듈에 관련된 템블릿을 불러오는 것이 가능한 프레임워크들이 존재한다. 하지만 그것들은 어디에 어플리케이션이 있는지 구분하기 위해 매우 믿을 수없는 방법으로서 현재 운영되는 디렉토리를 사용해야만 한다. 현재 운영되는 디렉토리는 폭넓은 진행이 가능하며(process-wide) 만약에 하나의 프로세스 안에서 여러개의 어플리케이션을 운영중이라면 (당신이 모르게 웹서버에서 일어 날 수 있다) 경로는 사라진다. 나쁨 : 많은 웹서버들은 직접 당신의 어플리케이션의 디렉토리를 작동시키는 것보다도

똑같은 폴더가 아니어도 되는 다큐먼트 루트(root)를 작동시킨다.

세번째 이유는 “명시적이 내시적인 것보다 좋다”는 것이다. 다른 것들은 기억할 필요 없이, 그 객체는 당신의 WSGI 어플리케이션이다. 혹시 당신이 WSGI 미들웨어를 적용시키고 싶다면, 단순하게 감싸면 끝이다. (비록 당신은 어플리케이션에서의 레퍼런스를 잃지 않기 위해 이것을 하기 위해 더 좋은 방법들이 있다. wsgi_app())

더욱이 이 디자인은 유닛테스팅과 비슷한 것들을 위해 매우 도움이 되는 어플리케이션을 만들기 위해 팩토리 함수 (factory function)을 사용하는 것을 가능하게 만든다. (어플리케이션 팩토리)

루팅 시스템(The Routing System)

Flask는 복합성에 의해 자동적으로 루트를 정렬하기 위해 고안된 Werkzeug 루팅 시스템을 사용한다. 이것은 당신이 임의적인 순서에서 루트들을 선언할 수 있으며 그것들이 기대한대로 여전히 작동할 것이라는 뜻이다. 만약 어플리케이션이 여러개의 모듈로 나뉠때 데코레이터들(decorators)이 한정되지 않은 순서로서 던져지기 때문에 당신이 루팅에 기반한 데코레이터를 제대로 상속을 원한다면 이것은 필수조건이다. 베크저그 루팅 시스템과 함께 또 다른 디자인 결정은 베크저그에서의 루트들은 URL을 특벽하게 보장하려 한다는 것이다. 만약에 루트가 애매하면 이것은 자동적으로 표준 URL로 재연결하는 것이므로 베크저그는 상당히 좋다.

단일 템블릿 엔진(One Template Engine)

Flask는 하나의 템플릿 엔진에 의해 결정된다 : Jinja2. 왜 Flask는 플러거블(pluggable) 템플릿 엔진 인터페이스를 갖고 있지 않은 것일까? 당신은 다른 템플릿 엔진을 분명하게도 쓸 수 있지만, Flask는 여전히 당신에게 Jinja2를 설정할 것이다. 그동안에 Jinja2를 항상 설정하도록 하는 제한은 사라졌지만, 하나의 템플릿 엔진을 포함시키고 사용하도록 하는 결정은 그렇지 않을 것이다.

템플릿 엔진은 프로그래밍 언어와 비슷하고 각각의 이런 엔진들은 어떻게 작동되는지에 대한 확실히 이해가 필요하다. 표면적으로는 그것들은 모두 동일하게 작동한다: 당신은 변수들의 합(set)으로 된 템플릿을 계산하라고 엔진에게 명령할 것이고 스트링(string)으로서 값을 반환받을 것이다.

그러나 저것은 유사성이 끝나는 것에 대한 것이다. 예제를 통한 Jinja2는 템플릿 상속 및 내부 템플릿과 모든 명령어를 유니코드로 사용하는 Python 코드에서도 사용될 수 있는 재활용이 가능한 블럭들(마크로스 macros)을 위한 지원, 반복되는 템플릿 렌더링, 구문 설정 등등을 확실하게 할 수 있는 방법인 광범위한 필터 시스템을 갖고 있다. 한편 Gehshi같은 다른 엔진은 계정 등등의 Xpath의 유효성을 가져감으로 XML 스트림 측정, 템플릿 상속에 기반을 두고 있다. Mako같은 다른 템플릿은 Python 모듈과 비슷한 템플릿으로 처리된다.

어플리케이션이나 프레임워크와 함께 템플릿 엔진을 연결할 때, 단순히 템플릿을 렌더링하는 것보다 더 있다. 예를 들어, Flask는 Jinja2의 광범위한 오토스케이프(autoscaping) 지원을 사용한다. 또한 Jinja2 템플릿으로부터 마크로스에 접속하는 방법들을 제공한다.

템플릿 엔진의 특별한 기능들을 가져가지 않는 템플릿 추상화 레이어는 그것 자체가 과학이다. 그리고 Flask같은 마이크로프레임워크에는 맡기에는 너무 크다.

더욱이 확장은 존재하는 단일 템플릿 언어에 따라 쉽게 좌우된다. 당신은 쉽게 자신의 템플레이팅(templating) 언어를 사용할 수 있지만, 확장은 여전히 Jinja 자체에 의해 좌지우지될 수 있다.

마이크로(Micro)와 의존성

Flask는 그 자체를 마이크로프레임워크라 불리고 그럼에도 두개의 라이브러리 (Werkzeug와 Jinja2)에 좌우되는가? 왜 그래야만 하는가? 우리가 웹 개발환경의 Ruby쪽을 살펴 본다면, 그곳에는 WSGI와 매우 비슷한 프로토콜을 갖고 있다. 그것을 그 곳에서는 Rack이라 불리지만, 게다가 루비를 위한 WSGI 렌디션(rendition)과 무척 닮아 보인다. 그러나 루비에서 거의 모든 어플리케이션들은 Rack에 직접적으로 적용되지 않지만, 동일한 이름으로 라이브러리의 상위로 작동된다. 이 Rack 라이브러리는 Python에서 두가지의 동일점을 가지고 있다: : WebOb (이전에 Paste) 와 Werkzeug. Paste는 여전히 있지만 내가 이해하는 바로는 WebOb에 있어서 사양되는 기술이다. WebOb 와 Werkzeug의 개발환경은 생각속의 비슷한 아이디어들과 함께 시작했다: 다른 어플리케이션들이 이점을 취하기 위해 WSGI의 좋은 구현을 해라.

Flask는 (때때로 복합 기능이 될 수 있는) WSGI와 적절히 접점을 두고 있는 Werkzeug에 의해 이미 완성되어 있는 일의 이득을 얻을 수 있는 프레임워크이다. Python 패키지 기반의 최근 발달에 고맙게도, 패키지 종속성은 더 이상 이슈가 아니며 다른것에 의존하는 라이브러리를 갖는것에 반해서도 거의 적은 이유들이 있다.

스레드 로컬(Thread Locals)

Flask는 (g)에 당신이 당신의 것들을 올릴 수 있는 요청, 세션과 추가 객체들을 위해 스레드 로컬 객체(thread local objects)를 사용한다 (사실은 컨텍스트 로컬 객체이며, 이것들은 그린렛 콘텍스트(greenlet contexts) 또한 지원한다). 왜 그러며 이것은 나쁜 아이디어가 아닌가?

그렇다. 보통 스레드를 로컬에서 사용한다는 것은 생각보다 좋은 아이디어는 아니다. 그것들은 스레드의 컨셉에 기반된 서버에게서 문제가 발생하며 규모가 큰 어플리케이션들을 유지하기 어렵게 만든다. 그렇지만 Flask는 단지 큰 어플리케이션이나 비동기 서버들을 위해 디자인 된것이 아니다. Flask는 빠르게 만들고 싶으며 통상적인 웹 어플리케이션을 쉽게 작성할 수 있기를 원한다.

또한 Flask 기반의 큰 어플리케이션들의 영감을 받고 싶으면 다큐먼트의 크게 만들기 섹션을 보라.

무엇이 Flask이고, 무엇이 Flask가 아닌가

Flask는 절대로 데이터베이스 층을 갖지 않을 것이다. 또한 저 방향으로의 폼(form) 라이브러리나 다른 것들을 갖지 않을 것이다. Flask 자신은 단지 적절한 WSGI 어플리케이션을 도구로서 사용하기 위해 Werkzeug와 템플레이팅을 처리하기 위해 Jinja2에 다리를 놓는 것 뿐이다. 그리고 로깅같은 몇개 없는 흔한 기본 라이브러리 패키지들을 엮는다. 그 이외의 모든 것들은 확장을 위한 것이다.

왜 이 케이스인 것일까? 왜냐하면 사람들은 다른 취향과 요구사항들을 갖고 있으며 Flask는 핵심적으로 어떠한 것들이라도 강요하지 않았다면 이러한 것들을 만나지 못했을 것이다. 다수의 웹 어플리케이션들은 일정한 템플릿 엔진을 필요로 할 것이다. 그렇지만 모든 어플리케이션들이 SQL 데이터베이스를 필요로 하는 것이 아니다.

Flask의 아이디어는 모든 어플리케이션들을 위한 좋은 기반을 만드는 것이다. 이외의 것들은 당신 혹은 확장에 따른 것이다.