[Django] 프로젝트 App이 여러 개일 때 URL 분기 처리하기

reowjd·2021년 4월 12일
2

Django 시리즈

목록 보기
5/5
post-thumbnail

장고의 한 프로젝트 안에는 여러 개의 앱을 만들 수 있다. 아래처럼 만들고 싶은 앱의 개수만큼 명령어를 실행해주면 된다.

$ 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'
]

방법1. PATH로 분기 처리

앱별로 시작하는 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/
]

확인해보면, 아래와 같이 잘 처리되는 것을 확인할 수 있다.



방법2. 도메인으로 분기 처리 - Middleware

다음은 Middleware를 사용하여 앱 별로 다른 도메인을 두는 방법을 소개한다.
장고 공식 문서의 요청을 처리하는 법을 살펴보면 아래와 같이 나와있다.

Django는 사용할 루트 URLconf 모듈을 결정합니다. 일반적으로 이것은 ROOT_URLCONF설정 의 값 이지만 들어오는 HttpRequest개체에 urlconf 속성 (미들웨어에 의해 설정 됨)이있는 경우 해당 값이 ROOT_URLCONF설정 대신 사용됩니다 .

기본적으로 ROOT_URLCONF 설정 값을 사용하지만 HttpRequesturlconf 속성이 있으면, 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) 전에 작업을 처리한다.
코드가 짧으니 살펴보면,
requestget_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.pysettings/prod.py로 환경을 나누고 prod.py에서만 미들웨어를 사용하게 하는 것이다. 개발 환경에서는 ROOT_URLCONF 값인 mysite.url 를 그대로 사용하기 때문에, 프리픽스로 URL에 접근할 수 있을 것이다.

끝:)

profile
개발계발

0개의 댓글