Flask로 python 백엔드 만들기

Flask 이해하기

Flask는 대표적인 Python 백엔드 프레임워크로 간단한 서버 구축에 유리하다.

ROOT
 |-- app.py
 |-- templates
      |-- index.html
      |-- result.html
  • app.py: 백엔드
  • templates/*.html: 프론트엔드

프론트엔드에 사용될 템플릿은 templates/ 폴더에 저장한다.

오늘 살펴볼 과정은 "index.html"에서 입력을 받아 "result.html"에 출력하는 앱이다.

HTML은 서버에 POST 요청을 보낸다. 서버는 전달받은 데이터를 처리한 뒤, HTML 파일로 내보낸다. 예를 들어, 제곱 계산기를 만든다고 가정하자. 사용자가 입력한 "3"을 POST 하면 "<div>9</div>"를 반환하는 식이다.

Flask에는 GET 요청과 POST 요청이 있다. GET은 URL로 정보를 전달한다. 반면 POST는 HTML에 있는 폼을 활용해 정보를 전달한다. 본 글에서는 다양한 데이터를 전달할 수 있는 POST 방식에 대해 설명한다.

Flask 구현

정수를 입력받아 제곱해 주는 예제를 만들어 보겠다.

입력 템플릿

<!-- index.html -->
<form action="/result" method="POST">
  <label>숫자: </label>
  <input type="number" name="input-number"></input>
  <input type="submit" value="제출" />
</form>

"/result"를 향해 POST 요청을 보내는 폼이다. 4번 라인 input 태그 안에 사용자가 정보를 입력한다. name 속성에 따라 서버에는 "input-number"라는 이름으로 입력이 전달된다.

입력 템플릿 출력

from flask import Flask, render_template, request

app = Flask(__name__)  # 현재 위치에서 템플릿 탐색

# "Base-URL/" 접근 시 동작
@app.route("/")
def index():
    return render_template("index.html")
  • Flask(__name__)을 통해 현재 위치에서 템플릿을 탐색한다.
  • @app.route("/")는 "/"로 접근했을 때 실행할 함수다. Flask 로컬 서버의 경우, "http://127.0.0.1:5000/"로 접근했을 때를 뜻한다.
  • render_template는 화면에 보여줄 HTML을 만든다. (자세한 건 뒤에서)
  • return을 통해 출력한다.

앞서 만든 입력 템플릿을 화면에 출력하는 부분이다.

POST 요청 처리

# "/result" 접근 시 동작
@app.route("/result", methods=["POST"])
def power():
    if request.method == "POST":
        # POST 요청이 있을 때
        n = request.form["input-number"]
        n = int(n) ** 2
        n = str(n)
    # n을 "result"라는 이름으로 html에 전달
    return render_template("result.html", result=n)
  • POST 요청이 들어왔을 때 "/result" 화면을 다룬다. 참고로 입력 폼에서 action 속성을 통해 "result"로 요청을 보냈었다.
  • request.form은 입력 폼에 작성한 정보가 담겨있다. 참고로 입력 폼에서 name 속성을 통해 데이터 이름을 설정했었다.
  • render_template을 통해 템플릿에 데이터를 전달한다. 파라미터 이름은 자유롭게 설정할 수 있다. ("결과 템플릿" 참고)
  • 데이터를 전달할 때 문자열 형식을 사용한다.

결과 템플릿 출력

<!-- result.html -->
<div>출력: {{ result }}</div>

{{ }}를 통해 render_template에 전달했던 데이터를 삽입할 수 있다. 변수명은 render_template 파라미터명과 같다. 

흐름 정리

처음 보면 입력 데이터가 전달되는 과정이 헷갈릴 수 있다. 정리하면 다음과 같다.

  • (입력 템플릿) 입력 폼에서 action으로 POST 요청을 보낸다.
  • (입력 템플릿) 입력된 데이터 이름(key)은 <input> 태그의 name 속성이다.
  • (서버) render_template에 key=value로 데이터를 전달한다.
  • (출력 템플릿) 전달받은 데이터는 HTML에서 "{{ key }}"로 사용할 수 있다.

실행

if __name__ == "__main__":
    app.run(debug=True)

run으로 서버를 실행한다. debug 옵션은 오류가 발생했을 때 유용한 정보를 제공한다.

$ python app.py
* Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit

python 파일을 실행하면 "http://127.0.0.1:5000"에서 실행된다. 브라우저를 열고 주소를 입력하면 화면을 볼 수 있다.


wtforms 활용

위 예시는 HTML <form> 태그를 이용해 데이터를 입력했다. 이번에는 wtforms를 통해 정보를 입력 및 검증하는 방법이다.

from flask import Flask, render_template, request
from wtforms import Form, IntegerField, validators

app = Flask(__name__)

class IntForm(Form):
    # 정수를 입력으로 받는 폼
    number = IntegerField("", [validators.InputRequired()])

@app.route("/")
def index():
    form = IntForm(request.form)
    # IntForm 객체를 "form"이라는 이름으로 html에 전달
    return render_template("index2.html", form=form)

@app.route("/result", methods=["POST"])
def power():
    form = IntForm(request.form)
    if request.method == "POST" and form.validate():
        # IntegerField를 만족할 때
        n = request.form["number"]
        n = int(n) ** 2
        res = str(n)
    else:
        res = "올바르지 않은 입력"
    return render_template("result.html", result=res)


if __name__ == "__main__":
    app.run(debug=True)
<form action="/result" method="POST">
  <label>숫자: </label> 
  {{ form.number }} <!-- form에 맞게 input 태그 생성 -->
  <input type="submit" value="제출" />
</form>

예시는 정수를 입력받는 IntegerField를 사용했지만, StringField 등 다양한 Field가 있고, Field마다 여러 조건을 검사할 수 있도록 API를 제공한다.


실제 적용

사용자가 이미지를 업로드하면, 얼굴에 블러 처리해 주는 앱을 간단히 구현해 봤다.

 

deep-learning-codes/dlib-flask at main · denev6/deep-learning-codes

Contribute to denev6/deep-learning-codes development by creating an account on GitHub.

github.com