플라스크 애플리케이션에서 웹 폼 사용 방법

저자는 Write for DOnations 프로그램의 일환으로 Free and Open Source Fund에 기부금을 전달하기로 선택했습니다.

서론

텍스트 필드나 텍스트 영역과 같은 웹 양식은 사용자가 데이터를 애플리케이션에 전송하여 작업을 수행하거나, 더 큰 텍스트 영역을 애플리케이션에 전송할 수 있게 해줍니다. 예를 들어, 소셜 미디어 애플리케이션에서는 사용자에게 자신의 페이지에 새 콘텐츠를 추가할 수 있는 상자를 제공할 수 있습니다. 또 다른 예로는 로그인 페이지가 있는데, 여기서 사용자에게 사용자 이름을 입력할 텍스트 필드와 비밀번호를 입력할 비밀번호 필드를 제공합니다. 서버(이 경우 Flask 애플리케이션)는 사용자가 제출한 데이터를 사용하여 데이터가 유효하면 로그인하거나, Invalid credentials!와 같은 메시지로 응답하여 사용자에게 제출한 데이터가 올바르지 않음을 알립니다.

Flask는 경량级的 Python 웹 프레임워크로, Python 언어로 웹 애플리케이션을 만들 때 유용한 도구와 기능을 제공합니다. 이 튜토리얼에서는 웹 폼을 사용하는 방법을 보여주는 작은 웹 애플리케이션을 구축할 것입니다. 이 애플리케이션은 Python 리스트에 저장된 메시지를 표시하는 페이지와 새 메시지를 추가하는 페이지를 가질 것입니다. 또한 사용자가 유효하지 않은 데이터를 제출할 때 오류를 알리기 위해 메시지 플래싱을 사용할 것입니다.

사전 준비사항

1단계 – 메시지 표시

이 단계에서는 파이썬 사전 목록에 저장된 메시지를 표시하기 위한 인덱스 페이지가 있는 Flask 애플리케이션을 만들 것입니다.

먼저 편집을 위해 app.py라는 새 파일을 엽니다:

  1. nano app.py

app.py 파일 안에 다음 코드를 추가하여 단일 경로가 있는 Flask 서버를 만듭니다:

flask_app/app.py
from flask import Flask, render_template

app = Flask(__name__)

messages = [{'title': 'Message One',
             'content': 'Message One Content'},
            {'title': 'Message Two',
             'content': 'Message Two Content'}
            ]

@app.route('/')
def index():
    return render_template('index.html', messages=messages)

파일을 저장하고 닫습니다.

이 파일에서 먼저 flask 패키지에서 Flask 클래스와 render_template() 함수를 가져옵니다. 그런 다음 Flask 클래스를 사용하여 app이라는 새로운 애플리케이션 인스턴스를 만들고, 배후에서 일부 경로를 설정하는 데 필요한 특수 변수인 __name__을 전달합니다. 템플릿 렌더링은 튜토리얼 Flask 애플리케이션에서 템플릿을 사용하는 방법에서 다룹니다.

그런 다음 전역 파이썬 목록인 messages를 만들고, 그 안에 파이썬 사전을 넣습니다. 각 사전에는 메시지의 제목을 나타내는 title과 메시지 내용을 나타내는 content라는 두 개의 키가 있습니다. 이는 데이터 저장 방법의 단순화된 예시입니다. 실제 시나리오에서는 데이터를 영구적으로 저장하고 더 효율적으로 조작할 수 있는 데이터베이스를 사용할 것입니다.

파이썬 리스트를 생성한 후, @app.route() 데코레이터를 사용하여 index()라는 뷰 함수를 생성합니다. 이 함수 내에서는 render_template() 함수를 호출하여 플라스크에게 해당 경로가 HTML 템플릿을 표시해야 함을 알립니다. 이 템플릿의 이름을 index.html로 지정하고(나중에 생성할 것입니다), messages라는 변수를 전달합니다. 이 변수는 이전에 선언한 messages 리스트를 값으로 가지며 HTML 템플릿에서 사용할 수 있게 합니다. 뷰 함수에 대한 자세한 내용은 튜토리얼 Flask와 Python 3를 사용하여 첫 번째 웹 애플리케이션 만들기에서 다룹니다.

다음으로, Flask가 템플릿을 찾는 flask_app 디렉토리에 templates 폴더를 생성한 후, 코드 반복을 피하기 위해 다른 템플릿들이 상속할 base.html이라는 템플릿 파일을 엽니다:

  1. mkdir templates
  2. nano templates/base.html

base.html 파일 안에 다음 코드를 추가하여 내비게이션 바와 콘텐츠 블록이 있는 기본 템플릿을 생성합니다:

flask_app/templates/base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %} - FlaskApp</title>
    <style>
        .message {
            padding: 10px;
            margin: 5px;
            background-color: #f3f3f3
        }
        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }

    </style>
</head>
<body>
    <nav>
        <a href="{{ url_for('index') }}">FlaskApp</a>
        <a href="#">About</a>
    </nav>
    <hr>
    <div class="content">
        {% block content %} {% endblock %}
    </div>
</body>
</html>

파일을 저장하고 닫습니다.

이 기본 템플릿은 다른 템플릿에서 재사용할 모든 HTML 기본 구조를 포함하고 있습니다. title 블록은 각 페이지의 제목을 설정하기 위해 대체되며, content 블록은 각 페이지의 내용으로 대체됩니다. 네비게이션 바에는 두 개의 링크가 있는데, 하나는 url_for() 헬퍼 함수를 사용하여 index() 뷰 함수로 연결되는 인덱스 페이지용이며, 다른 하나는 애플리케이션에 포함하기로 선택한 경우 소개 페이지용입니다.

다음으로, index.html이라는 템플릿을 엽니다. 이 템플릿은 app.py 파일에서 참조한 템플릿입니다:

  1. nano templates/index.html

다음 코드를 추가하세요:

flask_app/templates/index.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Messages {% endblock %}</h1>
    {% for message in messages %}
        <div class='message'>
            <h3>{{ message['title'] }}</h3>
            <p>{{ message['content'] }}</p>
        </div>
    {% endfor %}
{% endblock %}

파일을 저장하고 닫습니다.

이 코드에서는 base.html 템플릿을 확장하고 content 블록의 내용을 대체합니다. 제목 역할도 하는 <h1> 제목을 사용합니다.

{% for message in messages %} 줄에서 Jinja for 루프를 사용하여 messages 목록의 각 메시지를 순회합니다. 메시지의 제목과 내용을 담기 위해 <div> 태그를 사용합니다. 제목은 <h3> 제목으로, 내용은 <p> 태그로 표시합니다.

가상 환경이 활성화된 상태에서 flask_app 디렉토리에 있는 동안, FLASK_APP 환경 변수를 사용하여 Flask에 애플리케이션(app.py 경우)에 대해 알려줍니다:

  1. export FLASK_APP=app

그런 다음 FLASK_ENV 환경 변수를 development로 설정하여 개발 모드에서 애플리케이션을 실행하고 디버거에 액세스합니다. Flask 디버거에 대한 자세한 내용은 Flask 애플리케이션에서 오류를 처리하는 방법을 참조하세요. 다음 명령을 사용하여 이 작업을 수행합니다(Windows에서는 export 대신 set 사용):

  1. export FLASK_ENV=development

다음으로, 애플리케이션을 실행합니다:

  1. flask run

개발 서버가 실행되는 동안 브라우저를 사용하여 다음 URL을 방문하세요:

http://127.0.0.1:5000/

messages 목록의 메시지가 인덱스 페이지에 표시됩니다:

이제 웹 애플리케이션을 설정하고 메시지를 표시했으므로 사용자가 인덱스 페이지에 새 메시지를 추가할 수 있는 방법이 필요합니다. 이는 다음 단계에서 설정할 웹 양식을 통해 수행됩니다.

2단계 — 양식 설정

이 단계에서는 사용자가 웹 양식을 통해 메시지 목록에 새 메시지를 추가할 수 있는 애플리케이션에 페이지를 만듭니다.

개발 서버를 계속 실행하고 새 터미널 창을 엽니다.

먼저 app.py 파일을 엽니다:

  1. nano app.py

파일 끝에 다음 경로를 추가합니다:

flask_app/app.py
# ...

@app.route('/create/', methods=('GET', 'POST'))
def create():
    return render_template('create.html')

파일을 저장하고 닫습니다.

/create 경로는 GETPOST 요청을 모두 수락하기 위해 튜플 ('GET', 'POST')을 가진 methods 매개변수를 갖습니다. GETPOSTHTTP 메서드입니다. 기본적으로 인덱스 페이지나 About 페이지와 같은 데이터를 검색하는 데 사용되는 GET 요청만 수락됩니다. POST 요청은 서버의 데이터를 변경하는 특정 경로에 데이터를 제출하는 데 사용됩니다.

이 예제에서는 GET 요청을 사용하여 create 페이지를 요청합니다. Create 페이지에는 입력 필드와 제출 버튼이 있는 웹 양식이 있습니다. 사용자가 웹 양식을 작성하고 제출 버튼을 클릭하면 POST 요청이 /create 경로로 전송됩니다. 거기서 요청을 처리하고, 사용자가 빈 양식을 제출하지 않았는지 확인하기 위해 제출된 데이터의 유효성을 검사하고, messages 목록에 추가합니다.

create() 뷰 함수는 현재 한 가지 작업만 수행합니다: 일반적인 GET 요청을 받으면 create.html이라는 템플릿을 렌더링합니다. 이제 이 템플릿을 생성한 다음 다음 단계에서 POST 요청을 처리하도록 함수를 편집할 것입니다.

create.html이라는 새 템플릿 파일을 엽니다:

  1. nano templates/create.html

다음 코드를 추가합니다:

flask_app/templates/create.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Add a New Message {% endblock %}</h1>
    <form method="post">
        <label for="title">Title</label>
        <br>
        <input type="text" name="title"
               placeholder="Message title"
               value="{{ request.form['title'] }}"></input>
        <br>

        <label for="content">Message Content</label>
        <br>
        <textarea name="content"
                  placeholder="Message content"
                  rows="15"
                  cols="60"
                  >{{ request.form['content'] }}</textarea>
        <br>
        <button type="submit">Submit</button>
    </form>
{% endblock %}

파일을 저장하고 닫습니다.

이 코드에서는 base.html 템플릿을 확장하고 content 블록을 페이지의 제목 역할을 하는 <h1> 제목으로 대체합니다. <form> 태그에서 method 속성을 post로 설정하여 폼 데이터가 POST 요청으로 서버에 전송되도록 합니다.

폼에는 title이라는 이름의 텍스트 입력 필드가 있습니다. 이는 애플리케이션에서 제목 폼 데이터에 접근하는 데 사용할 이름입니다. <input> 태그에 {{ request.form['title'] }}value를 부여합니다. 이는 사용자가 입력한 데이터를 복원하여 문제가 발생했을 때 데이터가 손실되지 않도록 하는 데 유용합니다. 예를 들어, 사용자가 필수 content 텍스트 영역을 채우는 것을 잊어버린 경우, 요청이 서버로 전송되고 오류 메시지가 응답으로 돌아오지만, 제목의 데이터는 손실되지 않습니다. 왜냐하면 이 데이터는 request 전역 객체에 저장되어 있고, request.form['title']를 통해 접근할 수 있기 때문입니다.

제목 입력 필드 다음에는 앞서 언급한 이유와 동일하게 {{ request.form['content'] }} 값을 가진 content라는 이름의 텍스트 영역을 추가합니다.

마지막으로, 폼의 끝에 제출 버튼이 있습니다.

이제 개발 서버가 실행 중인 상태에서 브라우저를 사용하여 /create 경로로 이동하세요:

http://127.0.0.1:5000/create

메시지 제목의 입력 필드, 메시지 내용의 텍스트 영역, 그리고 제출 버튼이 있는 “새 메시지 추가” 페이지가 나타날 것입니다.

이 양식은 create() 뷰 함수에 POST 요청을 제출합니다. 그러나 아직 함수에 POST 요청을 처리하는 코드가 없으므로 양식을 작성하고 제출한 후에는 아무 일도 일어나지 않습니다. 다음 단계에서는 양식이 제출될 때 들어오는 POST 요청을 처리할 것입니다. 제출된 데이터가 유효한지(비어 있지 않은지) 확인하고 메시지 제목과 내용을 messages 목록에 추가할 것입니다.

3단계 — 양식 요청 처리

이 단계에서는 애플리케이션 측에서 양식 요청을 처리할 것입니다. 이전 단계에서 만든 양식을 통해 사용자가 제출한 양식 데이터에 접근하여 메시지 목록에 추가할 것입니다. 또한 메시지 플래싱을 사용하여 사용자가 유효하지 않은 데이터를 제출할 때 알릴 것입니다. 플래시 메시지는 한 번만 표시되며 다음 요청에서 사라집니다(예를 들어 다른 페이지로 이동하는 경우).

app.py 파일을 편집용으로 엽니다:

  1. nano app.py

먼저 Flask 프레임워크에서 다음을 가져올 것입니다:

  • 이전 단계에서 구축한 HTML 양식을 통해 제출될 들어오는 요청 데이터에 접근하기 위한 전역 request 객체입니다.
  • url_for() 함수를 사용하여 URL을 생성합니다.
  • flash() 함수를 사용하여 요청이 처리될 때 메시지를 플래시합니다 (모든 것이 잘 진행되었음을 사용자에게 알리거나, 제출된 데이터가 유효하지 않은 경우 문제를 알립니다).
  • redirect() 함수를 사용하여 클라이언트를 다른 위치로 리디렉션합니다.

이러한 가져오기를 파일의 첫 번째 줄에 추가하세요:

flask_app/app.py
from flask import Flask, render_template, request, url_for, flash, redirect

# ...

flash() 함수는 플래시된 메시지를 클라이언트의 브라우저 세션에 저장하며, 이를 위해 비밀 키를 설정해야 합니다. 이 비밀 키는 세션을 보호하는 데 사용되며, 세션을 통해 Flask는 새 메시지 페이지에서 인덱스 페이지로 이동하는 등 한 요청에서 다른 요청으로 정보를 기억할 수 있습니다. 사용자는 세션에 저장된 정보에 접근할 수 있지만, 비밀 키가 없으면 수정할 수 없으므로 비밀 키를 누구에게도 노출시키지 마세요. 자세한 내용은 Flask 세션에 대한 문서를 참조하세요.

비밀 키는 긴 무작위 문자열이어야 합니다. os 모듈의 os.urandom() 메서드를 사용하여 비밀 키를 생성할 수 있습니다. 이 메서드는 암호화 용도에 적합한 무작위 바이트 문자열을 반환합니다. 이를 사용하여 무작위 문자열을 얻으려면 새 터미널을 열고 다음 명령을 사용하여 Python 대화형 셸을 엽니다:

  1. python

Python 대화형 셸에서 표준 라이브러리의 os 모듈을 가져오고 다음과 같이 os.urandom() 메서드를 호출합니다:

  1. import os
  2. os.urandom(24).hex()

다음과 유사한 문자열을 얻게 됩니다:

Output
'df0331cefc6c2b9a5d0208a726a5d1c0fd37324feba25506'

이 문자열을 비밀 키로 사용할 수 있습니다.

비밀 키를 설정하려면 app.config 객체를 통해 애플리케이션에 SECRET_KEY 구성을 추가하세요. app 정의 바로 다음에 messages 변수를 정의하기 전에 직접 추가하세요:

flask_app/app.py

# ...
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your secret key'


messages = [{'title': 'Message One',
             'content': 'Message One Content'},
            {'title': 'Message Two',
             'content': 'Message Two Content'}
            ]
# ...

다음으로, create() 뷰 함수를 다음과 같이 정확히 수정하세요:

flask_app/app.py
# ...

@app.route('/create/', methods=('GET', 'POST'))
def create():
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']

        if not title:
            flash('Title is required!')
        elif not content:
            flash('Content is required!')
        else:
            messages.append({'title': title, 'content': content})
            return redirect(url_for('index'))

    return render_template('create.html')

if 문에서 request.method == 'POST' 비교를 통해 요청이 POST 요청일 때만 그 다음 코드가 실행되도록 합니다.

그런 다음 request.form 객체에서 제출된 제목과 내용을 추출합니다. 이 객체는 요청에서 양식 데이터에 접근할 수 있게 해줍니다. 제목이 제공되지 않으면 if not title 조건이 충족됩니다. 이 경우 flash() 함수를 사용하여 사용자에게 제목이 필요하다는 메시지를 표시합니다. 이렇게 하면 메시지가 flashed messages 목록에 추가됩니다. 나중에 이 메시지들을 base.html 템플릿의 일부로 페이지에 표시할 것입니다. 마찬가지로 내용이 제공되지 않으면 elif not content 조건이 충족됩니다. 그럴 경우 'Content is required!' 메시지를 flashed messages 목록에 추가합니다.

제목과 내용이 제대로 제출되면 messages.append({'title': title, 'content': content}) 줄을 사용하여 사용자가 제공한 제목과 내용으로 messages 목록에 새 딕셔너리를 추가합니다. 그런 다음 redirect() 함수를 사용하여 사용자를 인덱스 페이지로 리디렉션합니다. url_for() 함수를 사용하여 인덱스 페이지에 연결합니다.

파일을 저장하고 닫습니다.

이제 웹 브라우저를 사용하여 /create 경로로 이동합니다:

http://127.0.0.1:5000/create

원하는 제목과 내용으로 양식을 작성하세요. 양식을 제출하면 인덱스 페이지에 새 메시지가 나열된 것을 볼 수 있습니다.

마지막으로, flashed messages를 표시하고 “New Message” 페이지에 대한 링크를 base.html 템플릿의 탐색 바에 추가하여 이 새 페이지에 쉽게 접근할 수 있도록 합니다. base 템플릿 파일을 엽니다:

  1. nano templates/base.html

파일을 수정하여 <nav> 태그 내의 FlaskApp 링크 뒤에 새로운 <a> 태그를 추가하세요. 그런 다음 content 블록 바로 위에 새로운 for 루프를 추가하여 네비게이션 바 아래에 플래시된 메시지를 표시하세요. 이 메시지들은 Flask가 제공하는 특별한 get_flashed_messages() 함수에서 사용할 수 있습니다.

flask_app/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %} - FlaskApp</title>
    <style>
        .message {
            padding: 10px;
            margin: 5px;
            background-color: #f3f3f3
        }
        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }

        .alert {
            padding: 20px;
            margin: 5px;
            color: #970020;
            background-color: #ffd5de;
        }

    </style>
</head>
<body>
    <nav>
        <a href="{{ url_for('index') }}">FlaskApp</a>
        <a href="{{ url_for('create') }}">Create</a>
        <a href="#">About</a>
    </nav>
    <hr>
    <div class="content">
        {% for message in get_flashed_messages() %}
            <div class="alert">{{ message }}</div>
        {% endfor %}
        {% block content %} {% endblock %}
    </div>
</body>
</html>

파일을 저장하고 닫은 다음 브라우저에서 https://127.0.0.1:5000을 다시 로드하세요. 이제 네비게이션 바에는 /create 경로로 연결되는 “Create” 항목이 있을 것입니다.

플래시 메시지가 어떻게 작동하는지 보려면 “Create” 페이지로 이동하여 두 필드를 채우지 않고 Submit 버튼을 클릭하세요. 이렇게 하면 다음과 같은 메시지를 받게 됩니다:

인덱스 페이지로 돌아가면 네비게이션 바 아래에 표시된 플래시 메시지가 사라진 것을 볼 수 있습니다. 이들이 플래시 메시지가 아니었다면, 기본 템플릿을 상속받기 때문에 인덱스 페이지에도 표시되었을 것입니다.

양식에 제목을 입력하되 내용을 입력하지 않고 제출해보세요. “내용은 필수입니다!”라는 메시지가 표시됩니다. 네비게이션 바에서 FlaskApp 링크를 클릭하여 인덱스 페이지로 돌아간 다음, 뒤로 버튼을 클릭하여 다시 생성 페이지로 돌아오세요. 메시지 내용이 여전히 남아있는 것을 볼 수 있습니다. 이는 뒤로 버튼을 클릭할 때만 작동하는데, 이전 요청을 저장하기 때문입니다. 네비게이션 바에서 생성 링크를 클릭하면 새로운 요청을 보내어 양식을 초기화하고, 그 결과로 플래시 메시지가 사라집니다.

이제 사용자 입력을 받아들이고, 유효성을 검사하고, 데이터 소스에 추가하는 방법을 알게 되었습니다.

참고:
messages 목록에 추가하는 메시지는 서버가 중지되면 사라집니다. 파이썬 목록은 메모리에만 저장되기 때문입니다. 메시지를 영구적으로 저장하려면 SQLite와 같은 데이터베이스를 사용해야 합니다. Python과 함께 SQLite를 사용하는 방법을 배우려면 Python 3에서 sqlite3 모듈 사용 방법을 확인하세요.

결론

사용자가 인덱스 페이지에 표시되는 메시지 목록에 메시지를 추가할 수 있는 Flask 애플리케이션을 만들었습니다. 웹 양식을 생성하고, 사용자가 양식을 통해 제출한 데이터를 처리하고, 메시지 목록에 추가했습니다. 또한 플래시 메시지를 사용하여 사용자가 유효하지 않은 데이터를 제출할 때 알리는 방법도 사용했습니다.

Flask에 대해 더 알아보고 싶다면, Flask 시리즈의 다른 튜토리얼을 확인해보세요.

Source:
https://www.digitalocean.com/community/tutorials/how-to-use-web-forms-in-a-flask-application