확장 가능한 웹 애플리케이션을 위한 Flask 및 Django 모듈화
Lukas Schneider
DevOps Engineer · Leapcell

모놀리식 웹 애플리케이션의 탄생
웹 애플리케이션 개발은 종종 단일 파일에서 시작됩니다. Flask의 경우 일반적으로 app.py이고, Django의 경우 앱 내의 views.py 파일 또는 중앙 urls.py가 상당히 커질 수 있습니다. 이 접근 방식은 소규모 프로젝트나 프로토타입에는 완벽하게 적합합니다. 그러나 애플리케이션의 복잡성이 증가하고, 새로운 기능이 추가되며, 코드베이스가 확장됨에 따라 이 단일 파일은 빠르게 모놀리식 거인이 됩니다. 인증 로직부터 데이터 검색, 템플릿 렌더링, 복잡한 비즈니스 규칙 처리에 이르기까지 모든 것을 포함하는 수천 줄에 달하는 app.py 또는 views.py 파일을 상상해 보세요.
이렇게 크고 구별되지 않는 파일은 상당한 어려움을 야기합니다. 탐색하기 어렵고, 이해하기 힘들며, 유지보수하기는 더욱 어렵습니다. 잦은 병합 충돌로 인해 개발자 간의 협업이 악몽이 됩니다. 개별 구성 요소를 테스트하는 것은 번거롭고, 애플리케이션을 확장하거나 코드의 일부를 재사용하는 것은 극복할 수 없는 작업처럼 보입니다. 이것이 바로 Flask Blueprints와 Django Routers(종종 앱 수준 URL 및 뷰를 통해 구현됨)가 해결하도록 설계된 문제입니다. 이들은 애플리케이션을 더 작고 관리하기 쉬우며 재사용 가능한 구성 요소로 분해하는 구조화된 방법을 제공하여, 확장 가능하고 유지보수하기 쉬운 웹 애플리케이션으로 나아가는 길을 열어줍니다.
모놀리식 디자인 해체
실제 구현에 들어가기 전에 Flask와 Django에서 모듈화를 촉진하는 핵심 개념을 명확하게 이해해 봅시다.
Flask Blueprint: Flask에서 Blueprint는 관련 뷰, 정적 파일 및 템플릿 그룹을 단일 모듈화된 단위로 구성하는 방법입니다. 이는 독립 실행형 애플리케이션이 아니라 애플리케이션의 일부를 구축하기 위한 청사진입니다. 이를 대규모 Flask 애플리케이션과 함께 등록할 수 있는 미니 애플리케이션으로 생각하십시오. 이를 통해 Blueprint 내에서 라우트, 오류 처리기 및 기타 애플리케이션별 로직을 정의하여 관심사를 분리할 수 있습니다.
Django 앱 수준 URL 및 뷰(라우터): Django에는 이름으로 직접적인 "Blueprint" 동등물이 없지만, Django 프로젝트 내에서 각각 자체 views.py 및 urls.py를 갖는 별도의 "앱"을 만드는 개념은 동일한 목적을 수행합니다. 각 Django 앱은 특정 기능(예: 사용자, 제품, 주문)에 대한 자체 포함 모듈입니다. 앱 내의 urls.py는 해당 모듈의 라우터 역할을 하며, 해당 views.py 파일에 정의된 뷰에 URL을 매핑합니다. 이러한 앱 수준 URL 구성은 프로젝트의 메인 urls.py에 포함되어 효과적으로 모듈식 라우팅 시스템을 만듭니다.
두 접근 방식의 핵심 원칙은 관심사 분리입니다. 각 모듈(Blueprint 또는 Django 앱)은 특정 기능 집합을 담당합니다. 이는 코드 구성을 개선하고, 디버깅을 용이하게 하며, 여러 개발자가 서로의 작업을 방해하지 않고 동시에 다른 모듈에서 작업할 수 있도록 하여 팀 협업을 촉진합니다.
Flask에서 모듈화 구현
Blueprint를 사용하여 모놀리식 Flask 애플리케이션을 변환하는 방법을 보여드리겠습니다.
모놀리식 app.py:
# app.py (모놀리식 예제) from flask import Flask, render_template, request, redirect, url_for app = Flask(__name__) # 사용자 관련 라우트 @app.route('/users') def list_users(): return "Listing all users (from monolithic app)" @app.route('/users/<int:user_id>') def get_user(user_id): return f"User details for user ID: {user_id} (from monolithic app)" # 제품 관련 라우트 @app.route('/products') def list_products(): return "Listing all products (from monolithic app)" @app.route('/products/<int:product_id>') def get_product(product_id): return f"Product details for product ID: {product_id} (from monolithic app)" @app.route('/') def home(): return "Welcome to the monolithic app!" if __name__ == '__main__': app.run(debug=True)
Blueprint를 사용한 모듈식 Flask:
먼저 다음과 같은 디렉토리 구조를 만듭니다.
my_flask_app/
├── app.py
├── users/
│ ├── __init__.py
│ └── routes.py
├── products/
│ ├── __init__.py
│ └── routes.py
└── templates/
└── index.html
users/routes.py:
# users/routes.py from flask import Blueprint users_bp = Blueprint('users', __name__, url_prefix='/users') @users_bp.route('/') def list_users(): return "Listing all users (from users blueprint)" @users_bp.route('/<int:user_id>') def get_user(user_id): return f"User details for user ID: {user_id} (from users blueprint)"
products/routes.py:
# products/routes.py from flask import Blueprint products_bp = Blueprint('products', __name__, url_prefix='/products') @products_bp.route('/') def list_products(): return "Listing all products (from products blueprint)" @products_bp.route('/<int:product_id>') def get_product(product_id): return f"Product details for product ID: {product_id} (from products blueprint)"
업데이트된 app.py:
# app.py (모듈식 예제) from flask import Flask, render_template from users.routes import users_bp from products.routes import products_bp app = Flask(__name__) # Blueprint 등록 app.register_blueprint(users_bp) app.register_blueprint(products_bp) @app.route('/') def home(): return "Welcome to the modular Flask app!" if __name__ == '__main__': app.run(debug=True)
app.py가 훨씬 깔끔해졌으며 애플리케이션 설정 및 Blueprint 등록에만 집중합니다. 실제 라우트 정의 및 로직은 해당 Blueprint 파일 내에 캡슐화되어 있습니다. Blueprint 정의의 url_prefix는 해당 Blueprint 내에 정의된 모든 라우트에 자동으로 /users 또는 /products를 접두사로 붙여 URL 관리를 단순화합니다.
Django에서 모듈화 구현
Django는 프로젝트 및 앱 구조를 통해 본질적으로 모듈성을 장려합니다. 이것이 실제로 어떻게 작동하는지 살펴보겠습니다.
모놀리식 myproject/urls.py 및 myapp/views.py (가정된 나쁜 관행):
모든 뷰 로직을 단일 myapp/views.py에, 모든 URL 패턴을 myproject/urls.py에 쑤셔 넣은 시나리오를 상상해 보세요.
# myproject/urls.py (모놀리식 예제 - 나쁜 관행) from django.contrib import admin from django.urls import path from myapp import views # 모든 뷰를 여기에 가져온다고 가정 urlpatterns = [ path('admin/', admin.site.urls), path('', views.home, name='home'), path('users/', views.list_users, name='list_users'), path('users/<int:user_id>/', views.get_user, name='get_user'), path('products/', views.list_products, name='list_products'), path('products/<int:product_id>/', views.get_product, name='get_product'), ]
# myapp/views.py (모놀리식 예제 - 나쁜 관행) from django.http import HttpResponse def home(request): return HttpResponse("Welcome to the monolithic Django app!") def list_users(request): return HttpResponse("Listing all users (from monolithic view)") def get_user(request, user_id): return HttpResponse(f"User details for user ID: {user_id} (from monolithic view)") # ... 제품에 대한 추가 등 def list_products(request): return HttpResponse("Listing all products (from monolithic view)") def get_product(request, product_id): return HttpResponse(f"Product details for product ID: {product_id} (from monolithic view)")
별도 앱을 사용한 모듈식 Django:
먼저 별도 앱과 함께 일반적인 Django 프로젝트 구조를 설정합니다.
my_django_project/
├── my_django_project/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py # 프로젝트 수준 URL
│ └── wsgi.py
├── manage.py
├── users/
│ ├── migrations/
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py # 앱 수준 URL
│ └── views.py
├── products/
│ ├── migrations/
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py # 앱 수준 URL
│ └── views.py
└── templates/
└── index.html
users/views.py:
# users/views.py from django.http import HttpResponse def list_users(request): return HttpResponse("Listing all users (from users app)") def get_user(request, user_id): return HttpResponse(f"User details for user ID: {user_id} (from users app)")
users/urls.py:
# users/urls.py from django.urls import path from . import views app_name = 'users' # 이 앱의 URL 네임스페이스 urlpatterns = [ path('', views.list_users, name='list'), path('<int:user_id>/', views.get_user, name='detail'), ]
products/views.py:
# products/views.py from django.http import HttpResponse def list_products(request): return HttpResponse("Listing all products (from products app)") def get_product(request, product_id): return HttpResponse(f"Product details for product ID: {product_id} (from products app)")
products/urls.py:
# products/urls.py from django.urls import path from . import views app_name = 'products' # 이 앱의 URL 네임스페이스 urlpatterns = [ path('', views.list_products, name='list'), path('<int:product_id>/', views.get_product, name='detail'), ]
업데이트된 my_django_project/urls.py (메인 프로젝트 urls.py):
# my_django_project/urls.py (모듈식 예제) from django.contrib import admin from django.urls import path, include # include 가져오기 from . import views_main # 일반적인 프로젝트 수준 뷰가 있다면 가정 urlpatterns = [ path('admin/', admin.site.urls), path('', views_main.home, name='home'), # 일반적인 홈 뷰가 여기에 있을 수 있습니다. path('users/', include('users.urls')), # 앱 수준 URL 포함 path('products/', include('products.urls')), # 앱 수준 URL 포함 ]
# my_django_project/views_main.py (프로젝트 수준 홈 뷰 예제) from django.http import HttpResponse def home(request): return HttpResponse("Welcome to the modular Django app!")
Django에서는 include() 함수가 핵심입니다. 이를 통해 특정 앱 내의 다른 urls.py 파일에 URL 라우팅을 위임할 수 있습니다. app/urls.py의 app_name 속성은 여러 앱 간의 URL 이름 충돌을 방지하는 데 중요한 네임스페이스를 제공합니다.
애플리케이션 시나리오 및 이점
이러한 모듈화 기법을 사용하여 애플리케이션을 분해하면 다음과 같은 많은 이점을 얻을 수 있습니다.
- 향상된 유지보수성: 더 작은 파일은 읽고 이해하고 디버깅하기 쉽습니다. 한 모듈의 변경이 다른 모듈을 중단시킬 가능성은 적습니다.
- 향상된 확장성: 애플리케이션이 성장함에 따라 기존 모듈을 복잡하게 만들지 않고 새 모듈을 쉽게 추가할 수 있습니다.
- 더 나은 협업: 팀은 병합 충돌 위험을 줄여 동시에 다른 모듈에서 작업할 수 있습니다.
- 증가된 재사용성: Blueprint와 Django 앱은 자체 포함되도록 설계되어 다른 프로젝트나 동일한 대규모 애플리케이션의 다른 부분에서 구성 요소를 재사용하기 쉽습니다.
- 더 쉬운 테스트: 개별 모듈을 격리하여 테스트하여 더 강력하고 신뢰할 수 있는 애플리케이션을 만들 수 있습니다.
- 더 명확한 구조: 모듈 간의 정의된 경계는 보다 체계적이고 논리적인 코드베이스를 강제합니다.
이 접근 방식은 수십 개의 라우트 또는 뷰를 초과할 것으로 예상되는 모든 애플리케이션이나 여러 개발자가 관련된 모든 프로젝트에 강력히 권장됩니다.
확장 가능한 웹 개발을 향한 길
Flask Blueprints 또는 Django의 앱 수준 라우팅을 사용하는 모듈식 구조로 단일 모놀리식 파일에서 전환하는 것은 확장 가능하고 유지보수하기 쉬운 웹 애플리케이션을 구축하는 데 중요한 단계입니다. 이는 단순히 코드를 구성하는 것이 아니라 명확성, 재사용성 및 협업 개발을 우선시하는 사고방식을 채택하는 것입니다. 이러한 아키텍처 패턴을 채택함으로써 웹 프로젝트가 우아하게 성장하고, 새로운 요구 사항에 적응하며, 시간이 지남에 따라 지속될 수 있는 잠재력을 열어줍니다. 잘 구조화된 애플리케이션은 단순한 기능 모음이 아니라 미래 혁신의 기반입니다.

