파이썬을 공부하다보면 장고(Django)나 플래스크(Flask)에 대해 뭔지는 몰라도 듣게 된다.
이 둘은 파이썬을 상요해서 웹 어플리케이션을 작성할 수 있도록 돕는 툴이라고 할 수 있다.
특히 flask는 유연성이 좋고 제작이 쉽다는 장점이 있어서 간단한 API 어플리케이션을 만들기 용이하다.
가볍고 작은 용량으로 (공부할 때처럼) 간단한 웹 어플리케이션을 제작할 때 좀 더 편리한 flask를 먼저 정리하고 나중에 장고를 공부할 것 같다.
flask의 공식 홈페이지 ➡️ Flask
Flask는 마이크로 웹 프레임워크(Micro Web Framework)이다.
프레임워크는 집을 지을 때 가장 기본이 되는 뼈대를 의미한다.
웹 프레임워크는 웹 어플리케이션을 개발할 수 있도록 웹 서비스(Web Service)나 웹 API(Web API) 등을 제공하고 웹 개발과 배포를 할 수 있는 특정 방법을 제공한다.
즉, 웹을 만들어낼 수 있는 도구 모음들을 제공하는 것이다.
Flask는 이러한 도구 모음들 중 하나로 웹 프레임워크들 중 하나인데 이러한 도구 모음들이 최소한의 크기로 줄여진 것이다. 네일 아트 하기 전에 기본 케어할 때 쓰는 손톱 관리 세트같은 것이다. flask는 그 중에서도 최소한의 도구들을 모았고 그 도구들로 다른 도구모음들 못지 않게 표현할 수 있다고 한다.(플라스크 개발자가 말하기를, 마이크로는 여러분의 웹 애플리케이션의 하나의 파이썬 파일로 개발해야 한다는 걸 말하는게 아니며, 기능적으로 부족하다는 걸 의미하지도 않는다고 설명한다.) Flask를 사용해서 만들어진 대표적인 웹사이트에는 Disqus, Pinterest, Twilio 등이 있다.
flask는 기본적으로 프레임워크에 따라 패키지와 라이브러리 등 모듈들의 콜렉션이 있어서 개발이 수월하도록 도와준다.
flask를 설치한 뒤 install 된 리스트를 보면 Jinja, Werkzeug, Flask-SQKAlchemy 등이 같이 설치된 것을 확인할 수 있는데 이처럼 다양한 패키지들과 라이브러리들이 존재한다.
먼저 해당 환경에 Flask를 설치한다.
$ pip install flask
Flask 어플리케이션을 저장할 폴더를 새로 생성해준다.
폴더의 이름은 주로 어플리케이션 이름으로 지정해준다.
그리고 그 안에 __init__.py
이라는 파일을 하나 만들어준다.
만약 폴더의 이름이 test_app이라면 test_app이라는 폴더 안에 __init__.py
라는 파일이 들어가 있는 것이다.
❓ __init__.py
은 왜 항상 있을까?
이제 __init__.py
안에 코드를 입력하여 웹 어플리케이션을 만드는 첫 단추를 꿸 수 있다.
Flask를 사용해서 웹 어플리케이션을 만드는 방법은 아래와 같다.
# __init__.py 폴더 내 파이썬 코드
from flask import Flask #대소문자 주의
app = Flask(__name__)
Flask(__name__)
은 해당 어플리케이션의 이름을 지정해주고 있다.
아무것도 없지만 Flask 웹 어플리케이션을 생성한 것이다.
CLI 명령어로 실행할 때에는 해당 프로젝트가 있는 폴더의 상위 디렉토리에서 아래의 명령어로 실행시켜주면 된다.
$ FLASK_APP=test_app flask run
그러면 아래와 같은 터미널 화면이 보인다.
위의 안내문에서 가장 마지막에 적혀있는 문장에 따르면 웹 어플리케이션을 http://127.0.0.1:5000/
를 통해 접속할 수 있다는 것을 알려주고 있다. 이 때, 127.0.0.1
는 local host이고 5000
은 포트의 번호로 해당 포트에서 작동하고 있다는 것을 의미한다.Flask의 포트번호가 5000이다. 사용자의 로컬(127.0.0.1
)에서 포트 5000을 쓰고 그 뒤는 사용자의 선택에 따라 들어갈 수 있는 것이다.
127.0.0.1 집주소, 5000은 아파트 호수, / 뒤의 그 방으로 들어가라 라는 지도같은 것으로 읽을 수 있다.
아무튼, 위에서 만들어준 웹 어플리케이션에 웹 브라우저를 키고 접속해보면 접근할 수 있지만 아무 설정이 없기 때문에 404 (Not Found) 페이지를 마주하게 된다.
어플리케이션을 종료할 때는 터미널의 마지막 줄 괄호에 나와있는 방법으로 종료를 해야 정상적으로 종료된다.
만약 터미널만 종료했다면 프로세스를 따로 찾아서 종료를 해주어야 한다.
정상적으로 웹 어플리케이션 종료하기 : CTRL+C
라우트
라우팅: 도메인에서 하부 URL로 이동하는 것
플라스크는 복잡한 URL을 쉽게 함수로 연결하는 방법을 제공한다. 직접 URL을 지정해서 사용하거나 동적으로 변화하게 만들 수 있는데 이 때 쓰이는 기능이 route(라우트)와 routing(라우팅)이다. 해당 기능인 route()
함수를 사용하면 쉽고 빠르게 원하는 URL을 처리하는 기능을 만들 수 있다.
위의 예의 파일을 가져와 작업을 해서 내용을 넣어보면 아래와 같다.
# __init__.py 폴더 내 파이썬 코드
from flask import Flask #대소문자 주의
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World!'
@app.route('/')
는 어플리케이션의 루트 주소('/')에 접속했을 때에 실행하라는 뜻이다.
URL에 따라 실행하게 될 함수를 지정하는 역할이다. 즉, 엔드포인트(endpoint)를 설정하게 되는데 이 때 엔드포인트란 주소가 어떻게 끝나는지를 의미한다.
위의 예에서는 '/'로 끝나기 때문에 기본 URL(Base URL)인 127.0.0.1:5000
에 라우트 주소인 /
가 더해진 구조로 127.0.0.1:5000/
가 된다. 만약 다른 엔드포인트인 /about
을 설정하게 되면 127.0.0.1:5000/about
이 되는 것이다. 기본적으로 Base URL 뒤에는 슬래시(/)가 붙어야하며 이 슬래시가 없이 라우트를 설정하고 Flask를 실행하면 에러를 일으킨다.
실제로 실행하게 되는 함수는 index함수이다. 그리고 그 index 함수가 실행되면서 리턴되는 값 Hello World!가 웹페이지에 보여지게 된다.
이후 CLI에서 FLASK_APP=flask_app flask run
으로 실행한 뒤 127.0.0.1:5000/
에 접속하게 되면 아래와 같은 화면을 볼 수 있다.
❗️ 동일한 라우트가 여러 개 있다면 Flask에서는 HTTP Request를 받고 처리할 때에 가장 먼저 엔드포인트 조건이 맞는 라우트를 사용하게 된다.
Flask에서는 라우트를 다양하게 설정할 수 있다.
앞서 했던 것처럼 기본적으로 엔드포인트를 지정할 때도 있지만 URL 내에서 변수를 받을 상황도 있을 수 있다. 그리고 시각적인 부분 없이 접속되는 라우트들도 있을 수 있다.
기본적으로 Flask의 라우트 데코레이터를 사용하게 되면 3가지 HTTP Request 메소드를 허용한다.
GET, HEAD, OPTION이다.
하지만 이외에 POST, PUT, PATCH, DELETE 등 다른 메소드들을 통과하도록 설정하기 위해서는 데코레이터 함수에 methods
라는 인수를 추가해주면 된다.
예를 들어 @app.route('/')
에 POST 메소드를 추가하려면 다음처럼 할 수 있다.
@app.route('/', methods=['POST', 'GET'])
def index():
return 'Hello World!'
이제 해당 함수에는 POST와 GET이라는 메소드가 통과된다.
HEAD와 OPTIONS라는 메소드들은 이제 사용이 불가능하다.
URL을 보다보면 슬래쉬(/)로 이어진 긴 엔드포인트들이 있다.
예를 들어 아래와 같은 긴 엔드포인트를 가진 URL이 있다고 하자.
https://studyroom.MySchool.com/course/621/curriculum
홈페이지로 보이는 주소 뒤에 course, 621, curriculum이라는 단어가 잇따라 온다.
이처럼 URL을 세부적으로 지정하고 주소에서 값을 전달할 수도 있다.
작업하는 Flask 어플리케이션에 index 라우트를 만들고 index의 번호에 따라 해당 번호가 포함된 Welcome to Index { 번호 }
리턴한다고 해볼 수 도 있다. http://127.0.0.1:5000/index/1
에 접속하면 Welcome to Index 1
이 보이게 되는 것이다.
다음과 같이 라우트를 설정하면 엔드포인트에 꺽쇠(<>)모양으로 어떠한 변수를 받겠다고 설정할 수 있다.
그리고 꺽쇠 안에는 변수의 이르믕ㄹ 지정해주고 함수에서는 변수를 그대로 받아 사용할 수 있다.
@app.route('/index/<num>')
def index_number(num):
return 'Welcome to Index %i' %int(num)
위의 코드가 동작하는 과정을 살펴본다.
1️⃣ 먼저 <num>
으로 num이라는 이름의 변수를 만들어 받게 되어있다.
사용자가 입력하는 값과 상관없이 변수로 받게 된다. 만약 index/hello
를 넘겨주면 hello
가 값이 된다. 따라서 변수의 타입을 신경써야 하기도 한다.
2️⃣ 다음은 int(num)
으로 들어오는 변수값을 타입 변환해주고 있다.
그 이유는 기본적으로 URL을 통해 들어오게 되는 값은 문자열 타입이기 때문이다. 따라서 이를 숫자타입으로 표현하기 위해서는 숫자로 변환해주는 작업이 필요하다.
+) 받자마자 정수로 타입을 지정하고자 한다면 이렇게 할 수도 있다. @app.route('/index/<int:num>')
이렇게 설정한 뒤에 http://127.0.0.1:5000/index/1
에 접속하면 Welcome to Index 1
을 볼 수 있다.
그렇다면 숫자를 넘겨주지 않았을 때 기본으로 주어지는 값을 설정해주려면 어떻게 해야할까?
@app.route('/index/', defaults={'num':0})
@app.route('/index/<num>')
def index_number(num):
return 'Welcome to Index %i' % int(num)
기능이 많아질수록 라우트도 많아진다.
그렇기 때문에 라우트들을 하나의 파일로 모아서 사용하지 않고 기능별로 나누어서 블루프린트 기능을 사용한다.
블루프린트는 Flask에서 여러 개의 라우트를 한 곳에 묶어둘 수 있는 기능이 있다.
블루프린트를 사용하는 방법은 아래와 같다.
1️⃣ 라우트를 모을 폴더를 만들고 그 안에 user_routes.py라는 파이썬 파일을 만든다.
프로젝트의 구조를 도식화하면 아래와 같다.
flask_app
├── __init__.py
└── routes
└── user_routes.py
2️⃣ 그리고 user_routes.py
파일에는 아래와 같은 코드를 작성하여 블루프린트를 만들어준다.
#user_routes.py
from flask import Blueprint
bp = Blueprint('user',__name__, url_prefix='/user')
@bp.route('/')
def index():
return 'User Index Page'
Blueprint('user',__name__, url_prefix='/user')
에서 확인할 수 있는 인자값들은 아래와 같다.
'user'
: 블루프린트의 명칭__name__
: 블루프린트의 import 이름url_prefix='/user'
: URL 접두어 설정(해당 블루프린트의 라우트는 URL 앞에 자동으로 '/user/가 붙게 된다.)3️⃣ 이렇게 블루프린트를 설정한 뒤 __init__.py
파일에서 해당 파일을 불러와 사용해준다.
#__init__.py 파일
from flask import Flask
from flask_app.routes import user_routes
app = Flask(__name__)
app.register_blueprint(user_routes.bp)
@app.route('/')
def index():
return 'Hello World!'
##후략##
4️⃣ CLI에서 실행한 뒤 http://127.0.0.1:5000/user
로 접하게 되면 User Index Page
라는 문구를 볼 수 있다.
from flask import Flask #flask 모듈에서 Flask 함수를 가져옴
app = Flask(__name__) #Flask 객체를 app 변수에 할당(Flask 함수로 Flask 객체를 할당, 객체로 모든 플라스크 기능 사용 가능)
@app.route("/") #app 객체를 이용해 라우팅 경로 설정, 어플리케이션의 루트 경로(/) 호출 시 함수 실행
def hello(): #해당 라우팅 경로로 요청이 오면 실행할 함수 정의(문자열 출력), 뷰함수(특정 URL 호출 시 호출되는 함수)에 속함
return "Hello World!"
if __name__ == "__main__": #메인 모듈로 실행될 때 플라스크 서버 구동
app.run() #파이썬에 인터프리터에 의해 메인 모듈로 실행됐는지 확인 후 앱 실행
코드를 실행하면 터미널에 보이는 문구를 해석하면 아래와 같다.
Serving Flask app "hello" (lazy loading)
: Flask 앱인 "hello"를 옮겨 로딩 (파일명: hello.py)Environment: production
: 환경에 대한 명시, 경고 메시지는 개발 서버이니 배포에 사용하지 말 것을 당부한다.Debug mode: off
: 디버그 모드가 활성화 되어있지 않음.Running on http://127.0.0.1:5000/(Press CTRL+C to quit)
: 해당 주소에 서버 활성화, crtl+c를 통해 나가기.[url] - - [시각] "요청" [HTTP 코드] -
의 형태로 나온다.플라스크 어플리케이션의 실행 과정은 아래의 그림처럼 나타낼 수 있다.
이미지 출처 링크
1️⃣ Flask는 특정 URL이 호출되며 실행된다.
2️⃣ 특정 URL이 호출되면 그 URL에 mapping된(대응하는) 함수가 실행된다.
3️⃣ 요청한 URL이나 (HTTP GET 요청을 통해 가져올 글 요청), 내용(HTTP POST 요청을 통해 내용 요청)을 분석해서 비즈니스 로직인 논리를 실행한다. 이 때 상황에 따라 요청의 상태를 유지할 경우 쿠키나 세션을 사용한다. 또한 프로그램의 상태를 기록하기 위해 로깅을 하고 오류가 발생한 경우에 처리할 로직을 별도로 제공할 수도 있다.
4️⃣ 논리가 처리되면 결과를 응답으로 템플릿에 반환한다.
5️⃣ 응답으로 전송할 값을 HTML에 표현한다.
Flask 를 통해서 어플리케이션을 만드는 것도 주요하지만 프로젝트가 커지고 파일들이 많아지면 import를 사용할 일이 많아진다. 이 때 파이썬에서 circular import(순환 import로 import 명령어로 서로 불러오게 해서 실행이 안되는 것)를 피하기 위해서 Flask에서는 어플리케이션 팩토리 패턴을 추천하고 있다.
Application Factory는 말 그대로 어플리케이션을 만드는 공장을 만드는 것이다.
예시에서는 app을 선언하고 사용해왔지만 여러 개의 어플리케이션을 동시에 사용하거나 app이 선언되어 있는 파일의 일부분만 필요할 때에도 문제가 발생할 수 있다. 이러한 잠재적인 문제점들을 피하기 위해 사용하는 방법이 함수를 따로 만드는 것이다.
기본적인 패턴은 아래와 같다.
1️⃣ __init__.py
파일에 다음과 같은 코드를 넣어준다.
# __init__.py 파일
from flask import Flask
def create_app():
app = Flask(__name__)
from yourapplication.views.admin import admin
from yourapplication.views.frontend import frontend
app.register_blueprint(admin)
app.register_blueprint(frontend)
if __name__ == "__main__":
app = create_app()
app.run()
함수 안에 어플리케이션을 생성해준다면 해당 함수를 실행하기만 하면 원하는 어플리케이션이 만들어진다.
이제 다른 파일에서 import를 해도 잠재적인 문제들을 어느정도 해결할 수 있을 것이다.
블루프린트도 함수 내에서 import를 해온 뒤 어플리케이션에 추가해준다.