이 문서는 파이썬 기반의 마이크로 웹 프레임워크인 Flask를 사용하여 웹 애플리케이션을 개발하는 방법에 대한 포괄적인 가이드입니다. Flask는 가볍고 유연하며, 빠르고 효율적인 웹 개발을 가능하게 합니다. 이 가이드를 통해 Flask의 기본적인 개념부터 실제 애플리케이션 개발에 필요한 핵심 요소들을 단계별로 학습할 수 있습니다.
Flask는 파이썬으로 작성된 마이크로 웹 프레임워크입니다. '마이크로'라는 이름은 Flask가 핵심 기능(라우팅, 요청 처리 등)만을 제공하며, 데이터베이스 추상화 계층, 양식 유효성 검사 등과 같은 고급 기능은 확장(extension)을 통해 제공한다는 것을 의미합니다. 이러한 설계 철학 덕분에 개발자는 필요한 기능만을 선택하여 사용할 수 있어 높은 유연성을 확보할 수 있습니다.
Flask는 다음과 같은 장점들로 인해 많은 개발자에게 사랑받고 있습니다:
Flask 애플리케이션 개발을 시작하기 전에 필요한 개발 환경을 설정해야 합니다.
가장 먼저 파이썬이 시스템에 설치되어 있어야 합니다. 파이썬 공식 웹사이트에서 최신 버전을 다운로드하여 설치하는 것을 권장합니다.
www.python.org
python3 --version
가상 환경(Virtual Environment)은 프로젝트별로 독립적인 파이썬 환경을 제공하여, 패키지 충돌을 방지하고 의존성을 관리하는 데 도움을 줍니다. Flask 프로젝트를 시작할 때마다 가상 환경을 설정하는 것이 좋습니다.
python3 -m venv venv
venv
라는 이름의 가상 환경을 생성합니다.source venv/bin/activate
venv\Scripts\activate.bat
.\venv\Scripts\Activate.ps1
(venv)
와 같은 표시가 나타납니다.
가상 환경이 활성화된 상태에서 pip
를 사용하여 Flask를 설치합니다.
pip install Flask
pip show Flask
가장 기본적인 Flask 웹 애플리케이션을 만들어보고 실행해 봅시다.
Flask 애플리케이션의 최소한의 구조는 단일 파이썬 파일로 구성될 수 있습니다. 일반적으로 app.py
또는 wsgi.py
와 같은 이름을 사용합니다.
Flask에서 라우팅은 특정 URL 경로에 대한 요청을 어떤 파이썬 함수가 처리할지 연결하는 과정입니다. 이는 @app.route()
데코레이터를 사용하여 정의합니다.
개발 서버를 사용하여 Flask 애플리케이션을 실행할 수 있습니다.
app.py
파일을 생성하고 다음 코드를 작성합니다.
# app.py from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!' @app.route('/greet/<name>') def greet(name): return f'Hello, {name}!' if __name__ == '__main__': app.run(debug=True)
from flask import Flask
: Flask
클래스를 임포트합니다.app = Flask(name)
: Flask
애플리케이션 인스턴스를 생성합니다. name
은 현재 모듈의 이름을 나타내며, Flask가 리소스(템플릿, 정적 파일)를 찾을 때 사용됩니다.@app.route('/')
: 루트 URL (/
)에 대한 요청이 hello_world
함수로 라우팅되도록 지정합니다.def hello_world():
: 요청이 들어왔을 때 실행될 함수입니다. 이 함수가 반환하는 문자열은 웹 브라우저에 표시됩니다.@app.route('/greet/<name>)
: <name>
은 URL 경로의 가변 부분(variable part)을 나타냅니다. 이 값은 greet
함수의 인자로 전달됩니다.if name == 'main':
: 이 스크립트가 직접 실행될 때만 app.run()
이 호출되도록 합니다.app.run(debug=True)
: Flask 개발 서버를 실행합니다. debug=True
는 개발 중 유용한 디버깅 모드를 활성화합니다.python app.py
Flask CLI
를 사용할 수도 있습니다.export FLASK_APP=app.py # macOS/Linux # set FLASK_APP=app.py # Windows CMD flask run
http://127.0.0.1:5000/
또는 http://localhost:5000/
로 접속하여 “Hello, World!” 메시지를 확인합니다.http://127.0.0.1:5000/greet/DokuWiki
로 접속하면 “Hello, DokuWiki!”를 볼 수 있습니다.대부분의 웹 애플리케이션은 동적인 HTML 페이지를 생성해야 합니다. Flask는 Jinja2 템플릿 엔진을 사용하여 이를 지원합니다.
템플릿은 HTML 구조를 포함하며, Flask 애플리케이션에서 전달된 데이터를 삽입하여 동적인 웹 페이지를 생성하는 데 사용되는 파일입니다. 이를 통해 파이썬 코드와 HTML 마크업을 분리하여 웹 페이지의 유지보수성을 높일 수 있습니다.
Flask에서 템플릿을 사용하려면 render_template
함수를 사용합니다. 이 함수는 기본적으로 애플리케이션 루트 디렉토리 내의 templates
폴더에서 템플릿 파일을 찾습니다.
render_template
함수에 키워드 인자(keyword arguments)를 통해 템플릿으로 데이터를 전달할 수 있습니다. 전달된 데이터는 Jinja2 템플릿 내에서 변수로 접근할 수 있습니다.
프로젝트 루트 디렉토리에 templates
폴더를 생성하고 그 안에 HTML 파일을 저장합니다.
your-flask-project/ ├── app.py └── templates/ └── index.html
templates/index.html
:<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <title>{{ title }}</title> </head> <body> <h1>{{ heading }}</h1> <p>{{ message }}</p> {% if items %} <h2>목록:</h2> <ul> {% for item in items %} <li>{{ item }}</li> {% endfor %} </ul> {% else %} <p>표시할 항목이 없습니다.</p> {% endif %} </body> </html>
app.py
수정:# app.py from flask import Flask, render_template app = Flask(__name__) @app.route('/') def index(): page_title = 'Flask 템플릿 예제' page_heading = '환영합니다!' welcome_message = '이것은 Flask와 Jinja2 템플릿을 사용한 웹 페이지입니다.' data_items = ['항목 1', '항목 2', '항목 3'] return render_template('index.html', title=page_title, heading=page_heading, message=welcome_message, items=data_items) if __name__ == '__main__': app.run(debug=True)
{{ variable }}
: 템플릿으로 전달된 변수의 값을 출력합니다.{% control_structure %}
: if
문, for
루프와 같은 제어 흐름을 정의합니다.웹 애플리케이션은 HTML 외에도 CSS, JavaScript, 이미지 파일과 같은 정적 리소스를 필요로 합니다. Flask는 이들을 효율적으로 제공하는 메커니즘을 가지고 있습니다.
Flask는 기본적으로 애플리케이션 루트 디렉토리 내의 static
폴더에서 정적 파일을 찾습니다.
HTML 템플릿에서 정적 파일을 참조할 때는 url_for()
함수를 사용하여 해당 파일의 URL을 생성합니다.
url_for('static', filename='path/to/your/file.css')
static
폴더 구조:your-flask-project/ ├── app.py ├── templates/ │ └── index.html └── static/ ├── css/ │ └── style.css └── img/ └── logo.png
static/css/style.css
:body { font-family: Arial, sans-serif; background-color: #f4f4f4; color: #333; margin: 20px; } h1 { color: #007bff; }
templates/index.html
수정:<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> </head> <body> <h1>{{ heading }}</h1> <p>{{ message }}</p> <img src="{{ url_for('static', filename='img/logo.png') }}" alt="로고 이미지"> <!-- ... (이전 내용 유지) ... --> </body> </html>
static/img/logo.png
파일이 해당 경로에 있어야 합니다.사용자로부터 입력을 받기 위해 HTML 양식(form)을 사용하고, Flask에서 이 양식 데이터를 처리하는 방법을 알아봅니다.
웹 양식은 주로 GET
또는 POST
HTTP 메서드를 사용하여 데이터를 전송합니다.
Flask에서는 request
객체를 통해 들어오는 요청 데이터에 접근할 수 있습니다.
request.method
: 현재 요청의 HTTP 메서드를 확인합니다.request.form
: POST
요청으로 전송된 양식 데이터에 접근합니다. 딕셔너리와 유사한 객체입니다.request.args
: GET
요청으로 전송된 쿼리 문자열 데이터에 접근합니다.app.py
수정:# app.py from flask import Flask, render_template, request, redirect, url_for, flash app = Flask(__name__) app.secret_key = 'your_secret_key' # 세션 및 플래시 메시지를 위해 필요 @app.route('/') def index(): return render_template('index.html', title='Flask 양식 예제', heading='환영합니다!', message='양식 제출을 테스트해 보세요.') @app.route('/submit', methods=['GET', 'POST']) def submit(): if request.method == 'POST': # POST 요청 처리 username = request.form['username'] email = request.form.get('email') # .get()을 사용하여 키가 없을 경우 오류 방지 flash(f'제출되었습니다! 사용자 이름: {username}, 이메일: {email}') return redirect(url_for('index')) else: # GET 요청 처리 (선택 사항, 양식 페이지를 보여줄 때 사용) return render_template('form.html') if __name__ == '__main__': app.run(debug=True)
templates/form.html
:<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <title>양식 제출</title> <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> </head> <body> <h1>양식 제출</h1> <form action="{{ url_for('submit') }}" method="post"> <label for="username">사용자 이름:</label><br> <input type="text" id="username" name="username" required><br><br> <label for="email">이메일:</label><br> <input type="email" id="email" name="email"><br><br> <input type="submit" value="제출"> </form> <p><a href="{{ url_for('index') }}">홈으로 돌아가기</a></p> </body> </html>
templates/index.html
수정 (플래시 메시지 표시):<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> </head> <body> <h1>{{ heading }}</h1> {% with messages = get_flashed_messages() %} {% if messages %} <ul class="flashes"> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} <p>{{ message }}</p> <p><a href="{{ url_for('submit') }}">양식 제출 페이지로 이동</a></p> <!-- ... (이전 내용 유지) ... --> </body> </html>
flash()
함수는 사용자에게 일회성 메시지를 보여주는 데 사용됩니다. 이를 사용하려면 app.secret_key
를 설정해야 합니다.
대부분의 동적 웹 애플리케이션은 데이터를 저장하고 관리하기 위해 데이터베이스를 사용합니다. Flask는 특정 데이터베이스를 강제하지 않지만, Flask-SQLAlchemy
와 같은 확장을 통해 ORM
(Object-Relational Mapping)을 쉽게 통합할 수 있습니다.
Flask-SQLAlchemy
는 Flask 애플리케이션에서 SQLAlchemy
(파이썬의 인기 있는 ORM
)를 사용하기 위한 편리한 래퍼(wrapper)입니다. SQLite
는 가볍고 파일 기반의 데이터베이스로, 개발 단계에서 사용하기에 매우 적합합니다.
pip install Flask-SQLAlchemy
app.py
에 추가):# app.py (기존 App 코드에 추가) from flask import Flask, render_template, request, redirect, url_for, flash from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.secret_key = 'your_secret_key' # 세션 및 플래시 메시지를 위해 필요 # 데이터베이스 설정 (SQLite 사용) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # 경고 메시지 방지 db = SQLAlchemy(app) # 데이터베이스 모델 정의 class User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) email = db.Column(db.String(120), unique=True, nullable=False) def __repr__(self): return f'<User {self.username}>' # 데이터베이스 테이블 생성 (최초 1회 실행) @app.before_first_request def create_tables(): db.create_all() # 사용자 추가 라우트 @app.route('/add_user', methods=['GET', 'POST']) def add_user(): if request.method == 'POST': username = request.form['username'] email = request.form['email'] try: new_user = User(username=username, email=email) db.session.add(new_user) db.session.commit() flash(f'사용자 {username}이(가) 성공적으로 추가되었습니다!', 'success') return redirect(url_for('list_users')) except Exception as e: db.session.rollback() flash(f'사용자 추가 오류: {e}', 'error') return render_template('add_user.html') # 사용자 목록 라우트 @app.route('/users') def list_users(): users = User.query.all() return render_template('users.html', users=users) # ... (기존 라우트 유지) ... if __name__ == '__main__': app.run(debug=True)
templates/add_user.html
:<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <title>사용자 추가</title> <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> </head> <body> <h1>새 사용자 추가</h1> {% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} <ul class="flashes"> {% for category, message in messages %} <li class="{{ category }}">{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} <form action="{{ url_for('add_user') }}" method="post"> <label for="username">사용자 이름:</label><br> <input type="text" id="username" name="username" required><br><br> <label for="email">이메일:</label><br> <input type="email" id="email" name="email" required><br><br> <input type="submit" value="사용자 추가"> </form> <p><a href="{{ url_for('list_users') }}">사용자 목록 보기</a></p> </body> </html>
templates/users.html
:<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <title>사용자 목록</title> <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> </head> <body> <h1>등록된 사용자</h1> {% if users %} <table> <thead> <tr> <th>ID</th> <th>사용자 이름</th> <th>이메일</th> </tr> </thead> <tbody> {% for user in users %} <tr> <td>{{ user.id }}</td> <td>{{ user.username }}</td> <td>{{ user.email }}</td> </tr> {% endfor %} </tbody> </table> {% else %} <p>등록된 사용자가 없습니다.</p> {% endif %} <p><a href="{{ url_for('add_user') }}">새 사용자 추가</a></p> <p><a href="{{ url_for('index') }}">홈으로 돌아가기</a></p> </body> </html>
create_tables()
함수가 호출되어 site.db
파일과 User
테이블을 생성합니다.Flask의 '마이크로' 특성 덕분에 다양한 확장 기능을 통해 필요한 기능을 손쉽게 추가할 수 있습니다. 다음은 몇 가지 인기 있는 확장 기능입니다.
CSRF
(Cross-Site Request Forgery) 보호도 포함됩니다.pip install Flask-WTF
pip install Flask-Login
RESTful API
를 빠르고 쉽게 구축하기 위한 도구를 제공합니다.pip install Flask-RESTful
Alembic
을 사용하여 데이터베이스 스키마 마이그레이션을 관리합니다.pip install Flask-Migrate
pip install Flask-Mail
이 확장 기능들은 Flask 생태계의 일부이며, 특정 기능을 구현할 때 개발 시간을 크게 단축할 수 있습니다.
개발 서버는 개발 목적으로만 사용해야 하며, 실제 서비스 환경(Production)에는 적합하지 않습니다. 안정적인 운영을 위해서는 적절한 배포 전략이 필요합니다.
Flask는 WSGI
(Web Server Gateway Interface) 표준을 따릅니다. 따라서 배포 시에는 WSGI
서버가 필요합니다.
Unicorn
프로젝트에서 파생된 파이썬 WSGI HTTP
서버입니다. 가볍고 빠르며, 설정이 비교적 간단합니다.pip install gunicorn
gunicorn -w 4 app:app
(4개의 워커 프로세스로 app.py
파일의 app
인스턴스 실행)WSGI
서버입니다. 설정이 복잡할 수 있습니다.pip install uwsgi
WSGI
서버는 주로 애플리케이션을 실행하는 역할을 하며, HTTP 요청을 직접 처리하는 데는 한계가 있습니다. 따라서 Nginx
나 Apache
와 같은 웹 서버를 리버스 프록시로 사용하여 WSGI
서버 앞에 두는 것이 일반적입니다.
mod_wsgi
모듈을 통해 Flask 애플리케이션을 호스팅할 수 있습니다.민감한 정보(예: 데이터베이스 비밀번호, API 키)나 환경별 설정(예: 디버그 모드)은 코드에 직접 포함하지 않고 환경 변수로 관리하는 것이 좋습니다.
FLASK_ENV=production
또는 DATABASE_URL='postgresql://user:pass@host/db
python-dotenv
라이브러리를 사용하여 개발 환경에서 .env
파일로 환경 변수를 로드할 수 있습니다.이 가이드를 통해 파이썬 Flask 웹 애플리케이션 개발의 핵심적인 개념과 기술을 살펴보았습니다. 우리는 Flask의 소개, 개발 환경 설정, 기본적인 “Hello, World!” 애플리케이션 구축, Jinja2 템플릿 사용, 정적 파일 처리, 양식 데이터 처리, 그리고 데이터베이스 연동 및 배포 고려사항까지 다루었습니다.
Flask는 그 유연성과 확장성 덕분에 소규모 프로젝트부터 대규모 RESTful API
서버 구축에 이르기까지 다양한 용도로 활용될 수 있습니다. 이 가이드는 Flask 여정의 시작점이며, 더 깊이 있는 학습을 위해 다음 주제들을 탐구해 볼 것을 권장합니다:
Celery
와 같은 비동기 태스크 큐를 사용하는 방법.지속적인 학습과 실습을 통해 Flask 전문가로 성장하시길 바랍니다.