
Flask는 Python으로 작성된 경량 웹 프레임워크로, 간단한 웹 애플리케이션부터 복잡한 API 서버까지 다양한 백엔드 시스템을 구축할 수 있습니다. 이 포스트에서는 Flask를 이용해 다양한 Backend API를 설계하는 방법을 살펴보겠습니다.
Flask 소개
Flask는 2010년 Armin Ronacher가 만든 Python 웹 프레임워크로, “마이크로 프레임워크”라는 특징을 가지고 있습니다. ‘마이크로’라는 단어는 Flask가 최소한의 핵심 기능만을 제공하고, 확장성을 위해 다양한 확장 패키지를 활용할 수 있다는 의미입니다.
Flask는 Werkzeug(WSGI 툴킷)와 Jinja2(템플릿 엔진)를 기반으로 하며, 이를 통해 웹 애플리케이션 개발의 기본적인 기능을 제공합니다. 간결하고 직관적인 API를 제공하면서도 필요에 따라 확장할 수 있는 유연성이 Flask의 가장 큰 장점입니다.
Flask는 Python 3.9 이상부터 사용이 가능합니다.
설치 방법
Flask는 pip를 통해 쉽게 설치할 수 있습니다.
# 기본 설치
pip install flask
# 가상환경 사용 권장
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install flask
추가 기능을 위한 패키지도 설치할 수 있습니다:
# RESTful API 개발을 위한 Flask-RESTful
pip install flask-restful
# 데이터베이스 ORM을 위한 Flask-SQLAlchemy
pip install flask-sqlalchemy
# API 문서화를 위한 Flask-Swagger
pip install flask-swagger
# JWT 인증을 위한 Flask-JWT-Extended
pip install flask-jwt-extended
기본 사용 예시
기본적인 Flask 애플리케이션
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run(debug=True)
간단한 REST API
from flask import Flask, request, jsonify
app = Flask(__name__)
# 메모리 내 데이터
tasks = [
{'id': 1, 'title': 'Task 1', 'done': False},
{'id': 2, 'title': 'Task 2', 'done': False}
]
@app.route('/api/tasks', methods=['GET'])
def get_tasks():
return jsonify({'tasks': tasks})
@app.route('/api/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):
task = next((task for task in tasks if task['id'] == task_id), None)
if task:
return jsonify({'task': task})
return jsonify({'error': 'Task not found'}), 404
@app.route('/api/tasks', methods=['POST'])
def create_task():
if not request.json or 'title' not in request.json:
return jsonify({'error': 'Title is required'}), 400
task = {
'id': tasks[-1]['id'] + 1 if tasks else 1,
'title': request.json['title'],
'done': False
}
tasks.append(task)
return jsonify({'task': task}), 201
if __name__ == '__main__':
app.run(debug=True)
다양한 API 설계 방법
1. Flask-RESTful을 이용한 RESTful API
Flask-RESTful은 Flask에서 RESTful API를 쉽게 구현할 수 있게 해주는 확장 패키지입니다.
from flask import Flask
from flask_restful import Api, Resource, reqparse
app = Flask(__name__)
api = Api(app)
tasks = [
{'id': 1, 'title': 'Task 1', 'done': False},
{'id': 2, 'title': 'Task 2', 'done': False}
]
class TaskListResource(Resource):
def get(self):
return {'tasks': tasks}
def post(self):
parser = reqparse.RequestParser()
parser.add_argument('title', type=str, required=True, help='Title cannot be blank')
args = parser.parse_args()
task = {
'id': tasks[-1]['id'] + 1 if tasks else 1,
'title': args['title'],
'done': False
}
tasks.append(task)
return {'task': task}, 201
class TaskResource(Resource):
def get(self, task_id):
task = next((task for task in tasks if task['id'] == task_id), None)
if task:
return {'task': task}
return {'error': 'Task not found'}, 404
def put(self, task_id):
task = next((task for task in tasks if task['id'] == task_id), None)
if not task:
return {'error': 'Task not found'}, 404
parser = reqparse.RequestParser()
parser.add_argument('title', type=str)
parser.add_argument('done', type=bool)
args = parser.parse_args()
if args['title']:
task['title'] = args['title']
if args['done'] is not None:
task['done'] = args['done']
return {'task': task}
def delete(self, task_id):
global tasks
task = next((task for task in tasks if task['id'] == task_id), None)
if not task:
return {'error': 'Task not found'}, 404
tasks = [task for task in tasks if task['id'] != task_id]
return {'result': 'Task deleted'}
api.add_resource(TaskListResource, '/api/tasks')
api.add_resource(TaskResource, '/api/tasks/<int:task_id>')
if __name__ == '__main__':
app.run(debug=True)
2. Flask Blueprints를 이용한 모듈화
대규모 API를 개발할 때는 Blueprint를 사용하여 코드를 모듈화할 수 있습니다.
# auth.py
from flask import Blueprint, request, jsonify
auth_bp = Blueprint('auth', __name__)
@auth_bp.route('/login', methods=['POST'])
def login():
# 로그인 로직
return jsonify({'token': 'dummy_token'})
@auth_bp.route('/register', methods=['POST'])
def register():
# 회원가입 로직
return jsonify({'message': 'User registered successfully'})
# tasks.py
from flask import Blueprint, request, jsonify
tasks_bp = Blueprint('tasks', __name__)
tasks = [
{'id': 1, 'title': 'Task 1', 'done': False},
{'id': 2, 'title': 'Task 2', 'done': False}
]
@tasks_bp.route('/', methods=['GET'])
def get_tasks():
return jsonify({'tasks': tasks})
@tasks_bp.route('/<int:task_id>', methods=['GET'])
def get_task(task_id):
task = next((task for task in tasks if task['id'] == task_id), None)
if task:
return jsonify({'task': task})
return jsonify({'error': 'Task not found'}), 404
# app.py
from flask import Flask
from auth import auth_bp
from tasks import tasks_bp
app = Flask(__name__)
app.register_blueprint(auth_bp, url_prefix='/api/auth')
app.register_blueprint(tasks_bp, url_prefix='/api/tasks')
if __name__ == '__main__':
app.run(debug=True)
3. Flask-SQLAlchemy를 이용한 데이터베이스 API
데이터베이스를 사용하는 API를 만들어 보겠습니다.
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///tasks.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
done = db.Column(db.Boolean, default=False)
def to_dict(self):
return {
'id': self.id,
'title': self.title,
'done': self.done
}
with app.app_context():
db.create_all()
@app.route('/api/tasks', methods=['GET'])
def get_tasks():
tasks = Task.query.all()
return jsonify({'tasks': [task.to_dict() for task in tasks]})
@app.route('/api/tasks', methods=['POST'])
def create_task():
if not request.json or 'title' not in request.json:
return jsonify({'error': 'Title is required'}), 400
task = Task(title=request.json['title'])
db.session.add(task)
db.session.commit()
return jsonify({'task': task.to_dict()}), 201
@app.route('/api/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):
task = Task.query.get(task_id)
if task:
return jsonify({'task': task.to_dict()})
return jsonify({'error': 'Task not found'}), 404
@app.route('/api/tasks/<int:task_id>', methods=['PUT'])
def update_task(task_id):
task = Task.query.get(task_id)
if not task:
return jsonify({'error': 'Task not found'}), 404
if 'title' in request.json:
task.title = request.json['title']
if 'done' in request.json:
task.done = request.json['done']
db.session.commit()
return jsonify({'task': task.to_dict()})
@app.route('/api/tasks/<int:task_id>', methods=['DELETE'])
def delete_task(task_id):
task = Task.query.get(task_id)
if not task:
return jsonify({'error': 'Task not found'}), 404
db.session.delete(task)
db.session.commit()
return jsonify({'result': 'Task deleted'})
if __name__ == '__main__':
app.run(debug=True)
4. Flask-JWT-Extended를 이용한 인증 API
JWT 인증을 구현해 보겠습니다.
from flask import Flask, request, jsonify
from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity
from datetime import timedelta
app = Flask(__name__)
app.config['JWT_SECRET_KEY'] = 'your-secret-key' # 실제 사용 시 환경 변수로 관리
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1)
jwt = JWTManager(app)
users = {
'user1': {'password': 'password1'},
'user2': {'password': 'password2'}
}
@app.route('/api/login', methods=['POST'])
def login():
if not request.is_json:
return jsonify({'error': 'Missing JSON in request'}), 400
username = request.json.get('username', None)
password = request.json.get('password', None)
if not username or not password:
return jsonify({'error': 'Missing username or password'}), 400
if username not in users or users[username]['password'] != password:
return jsonify({'error': 'Invalid username or password'}), 401
access_token = create_access_token(identity=username)
return jsonify({'access_token': access_token})
@app.route('/api/protected', methods=['GET'])
@jwt_required()
def protected():
current_user = get_jwt_identity()
return jsonify({'logged_in_as': current_user})
if __name__ == '__main__':
app.run(debug=True)
5. Flask-Swagger를 이용한 API 문서화
API 문서화를 위해 Flask-Swagger를 사용해 보겠습니다.
from flask import Flask, jsonify
from flask_swagger import swagger
app = Flask(__name__)
@app.route('/api/tasks', methods=['GET'])
def get_tasks():
"""
Get all tasks
---
responses:
200:
description: A list of tasks
"""
tasks = [{'id': 1, 'title': 'Task 1', 'done': False}]
return jsonify({'tasks': tasks})
@app.route('/api/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):
"""
Get a specific task
---
parameters:
- name: task_id
in: path
type: integer
required: true
description: The ID of the task
responses:
200:
description: Task details
404:
description: Task not found
"""
task = {'id': task_id, 'title': f'Task {task_id}', 'done': False}
return jsonify({'task': task})
@app.route('/spec')
def get_spec():
swag = swagger(app)
swag['info']['title'] = 'Task API'
swag['info']['version'] = '1.0'
return jsonify(swag)
if __name__ == '__main__':
app.run(debug=True)
Flask의 특징 및 장점
- 간결성과 유연성: Flask는 필요한 최소한의 기능만을 제공하며, 개발자는 필요한 기능만 추가할 수 있습니다.
- 학습 곡선이 낮음: 다른 프레임워크에 비해 배우기 쉽고, 간단한 프로젝트를 빠르게 시작할 수 있습니다.
- 확장성: 여러 확장 패키지를 통해 기능을 추가할 수 있습니다.
- 유니코드 지원: 기본적으로 유니코드를 지원하여 다국어 애플리케이션 개발이 용이합니다.
- 테스트 친화적: Flask는 테스트 클라이언트를 제공하여 단위 테스트를 쉽게 작성할 수 있습니다.
- 문서화: 공식 문서가 잘 정리되어 있어 참조하기 쉽습니다.
- 커뮤니티 지원: 활발한 커뮤니티와 다양한 확장 패키지가 있습니다.
- 마이크로서비스 구축에 적합: 가벼운 특성으로 인해 마이크로서비스 아키텍처에 적합합니다.
Flask의 단점
- 기본 기능 제한: 최소한의 기능만 제공하기 때문에 대규모 애플리케이션을 개발할 때 많은 확장 패키지가 필요할 수 있습니다.
- 비동기 처리 제한: Flask는 기본적으로 동기 처리를 기반으로 하며, 비동기 처리를 위해서는 추가 패키지(예: Flask-Async)가 필요합니다.
- 규모 확장성: 대규모 애플리케이션에서는 구조화된 방식으로 코드를 관리하기 위한 추가적인 노력이 필요합니다.
- 성능: 고성능이 요구되는 애플리케이션에서는 다른 프레임워크나 언어가 더 적합할 수 있습니다.
- 기본 보안 메커니즘 부족: 보안 기능은 대부분 확장 패키지를 통해 제공되므로, 개발자가 직접 보안 측면을 고려해야 합니다.
관련 정보 및 참고 자료
- 공식 문서: Flask 공식 문서
- Flask-RESTful: Flask-RESTful 공식 문서
- Flask-SQLAlchemy: Flask-SQLAlchemy 공식 문서
- Flask-JWT-Extended: Flask-JWT-Extended 공식 문서
- Flask-Swagger: Flask-Swagger GitHub
- Flask Mega-Tutorial: Miguel Grinberg의 Flask Mega-Tutorial
- Flask 예제 및 튜토리얼: Real Python Flask 튜토리얼
- Flask 서적: “Flask Web Development” by Miguel Grinberg
- Flask 커뮤니티: Flask 디스커션 포럼
결론
Flask는 간결하고 유연한 Python 웹 프레임워크로, 다양한 Backend API를 설계하는 데 적합합니다. 최소한의 기능만을 제공하는 마이크로 프레임워크이지만, 다양한 확장 패키지를 통해 필요한 기능을 추가할 수 있습니다.
작은 규모의 프로젝트부터 중간 규모의 애플리케이션까지 다양한 상황에서 활용할 수 있으며, 특히 API 서버나 마이크로서비스 구축에 적합합니다. Flask의 단점을 이해하고 프로젝트의 요구사항에 맞게 적절히 활용한다면, 효율적이고 유지보수하기 쉬운 Backend API를 개발할 수 있을 것입니다.
답글 남기기