장고의 한 프로젝트 안에는 여러 개의 앱을 만들 수 있다. 아래처럼 만들고 싶은 앱의 개수만큼 명령어를 실행해주면 된다.
$ python manage.py startapp blogs
$ python manage.py startapp reviews
$ python manage.py startapp polls
이번 포스팅에서는 앱이 여러 개 있을 때, URL에 따라 앱 별로 요청을 전달하는 방법에 대해 살펴볼 것이다.
프로젝트에는 아래와 같이 3개의 앱(blogs, polls, reviews)이 있다.
.
└── mysite
├── blogs
├── manage.py
├── mysite
├── polls
└── reviews
앱을 추가한 뒤에는 INSTALLED_APP
에 앱 config 정보를 추가해야 한다. config 명은 앱 하위의 apps.py를 확인하면 된다.
INSTALLED_APPS = [
...
'blogs.apps.BlogsConfig',
'polls.apps.PollsConfig',
'reviews.apps.ReviewsConfig'
]
앱별로 시작하는 PATH를 다르게 하여 분기 처리할 수 있다.
각각의 앱 디렉토리 하위에 urls.py를 두고 프로젝트 세팅 디렉토리인 mysite에도 urls.py가 있어야 한다.
# mysite/urls.py
from django.contrib import admin
from django.urls import path, include
from xcapp.views import *
from django.contrib.auth import views as auth_views
urlpatterns = [
path('admin/', admin.site.urls),
path('blogs/', include('blogs.urls')),
path('polls/', include('polls.urls')),
path('reviews/', include('reviews.urls'))
]
앱 별로 blogs, polls, reviews라는 프리픽스를 설정하여, 해당 PATH로 시작하는 요청이 들어오면 앱 하위의 urls.py를 임포트하는 방식이다.
앱 별 urls.py는 아래와 같을 것이다.
#polls/urls.py
from django.urls import path
from polls.views import *
urlpatterns = [
path('', IndexView.as_view(), name='polls'), #127.0.0.1:8000/polls/
path('test/', TestView.as_view(), name='polls_test'), #127.0.0.1:8000/polls/test/
]
확인해보면, 아래와 같이 잘 처리되는 것을 확인할 수 있다.
다음은 Middleware
를 사용하여 앱 별로 다른 도메인을 두는 방법을 소개한다.
장고 공식 문서의 요청을 처리하는 법을 살펴보면 아래와 같이 나와있다.
Django는 사용할 루트 URLconf 모듈을 결정합니다. 일반적으로 이것은 ROOT_URLCONF설정 의 값 이지만 들어오는 HttpRequest개체에 urlconf 속성 (미들웨어에 의해 설정 됨)이있는 경우 해당 값이 ROOT_URLCONF설정 대신 사용됩니다 .
기본적으로 ROOT_URLCONF
설정 값을 사용하지만 HttpRequest
에 urlconf
속성이 있으면, ROOT_URLCONF
대신에 사용한다고 설명하고 있다. 이 같은 처리 방식을 사용하여 미들웨어에서 도메인을 확인하고, 도메인에 따라 urlconf
값을 지정할 것이다.
먼저, settings.py에 앱 별 도메인을 정의한다.
(도메인 대신 로컬호스트로 테스트하는 경우, 포트를 달리하여 확인할 수 있습니다.)
#mysite/settings.py
BLOGS_DOMAIN = 'blog-dev.com'
REVIEWS_DOMAIN = 'reviews-dev.com'
POLLS_DOMAIN = 'polls-dev.com'
ALLOWED_HOST
에도 해당 도메인을 추가한다.
ALLOWED_HOSTS = [BLOGS_DOMAIN, REVIEWS_DOMAIN, POLLS_DOMAIN]
그리고 나서, 미들웨어(Middleware)를 작성한다. 미들웨어 명은 VirtualHostMiddleware
로 두었다.
#mysite/middlewares/virtualhostmiddleware.py
from django.conf import settings
virtual_hosts = {
settings.BLOGS_DOMAIN : 'blogs.urls',
settings.REVIEWS_DOMAIN : 'reviews.urls',
settings.POLLS_DOMAIN : 'polls.urls'
}
class VirtualHostMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
host = request.get_host()
request.urlconf = virtual_hosts.get(host)
response = self.get_response(request)
return response
미들웨어는 request 전/후, response 전/후에 호출되어 요청 및 응답에 대한 후크 프레임 워크이다.
self.get_response()
전후로 요청에 대한, 응답에 대한 처리를 할 수 있다. 우리가 해야할 것은 도메인에 따른 분기처리 이므로 view를 호출하기 전에 작업이 필요하다. 그러므로 self.get_response(request) 전에 작업을 처리한다.
코드가 짧으니 살펴보면,
request
의 get_host()
메서드로 요청한 도메인 명을 가져온 뒤, 호스트 별 url 모듈을 urlconf 값으로 지정한다.
정말 간단하다!
새로 작성한 미들웨어는 settings.py에 추가해 준다.
미들웨어는 리스트에 정의된 순서대로 미들웨어를 적용하기 때문에 순서를 고려해야 하는데, 도메인에 따른 분기처리가 필요하기 때문에 맨 위에 두었다. SSL을 적용하는 경우 SecuretiryMiddleware
를 가장 위에 두라니, 2번 째에 두는게 나을 수 것 같기도 하다.
MIDDLEWARE = [
'mysite.middlewares.virtualhostmiddleware.VirtualHostMiddleware',
...
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
도메인으로 호출해 보면, 프리픽스 없이도 분기 처리되는 것을 확인할 수 있다.
최종 프로젝트 구조
.
├── blogs
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── templates
│ │ └── blogs
│ │ └── index.html
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── db.sqlite3
├── manage.py
├── mysite
│ ├── __init__.py
│ ├── asgi.py
│ ├── middlewares
│ │ └── virtualhostmiddleware.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── polls
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── templates
│ │ └── polls
│ │ └── index.html
│ ├── tests.py
│ ├── urls.py
│ └── views.py
└── reviews
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── templates
│ └── reviews
│ └── index.html
├── tests.py
├── urls.py
└── views.py
장고 프로젝트에 앱이 여러개일 때 PATH 별로, 도메인 별로 분기 처리하는 방법을 살펴 보았다. PATH 별은 간단하지만, 도메인 별로 분기처리 하는 방법의 경우, 로컬에서 개발 작업을 진행할 때 앱의 개수 만큼 포트를 열어줘야 돼서 번거로움이 생길 수 있다. 그래서 개발할 때는 PATH로, 스테이징/운영 환경에 배포할 때는 도메인 별로 사용하는 방법도 괜찮을 것 같다.
settings.py 대신, settings/dev.py
와 settings/prod.py
로 환경을 나누고 prod.py에서만 미들웨어를 사용하게 하는 것이다. 개발 환경에서는 ROOT_URLCONF 값인 mysite.url 를 그대로 사용하기 때문에, 프리픽스로 URL에 접근할 수 있을 것이다.
끝:)