Python/Django

Django 2.2 | 프로젝트 디렉토리 구조

코닝구 2020. 9. 29. 17:39

프로젝트 레이아웃

이번 포스팅에서는 프로젝트의 디렉토리 구조인 레이아웃에 대해 말하려 한다. 프로젝트 레이아웃은 장고로 개발된 많은 프로젝트마다 그리고 장고 개발자들 사이에서도 의견이 분분하다. 무엇이 모범사례이고, 정답인지는 정확히 모르겠지만 개인적으로 적지 않은 장고 프로젝트를 진행하면서 익숙해진 나름의 프로젝트 레이아웃에 대해 말하려 한다. 정답은 아니기에 참고만 해주길 바란다.

바로 앞에 작성한 포스팅의 startproject와 startapp을 실행하면 기본적인 레이아웃은 아래와 같을 것이다.

learn.django
  coninggu_project/ # 프로젝트 디렉토리
    __init__.py
    settings.py
    urls.py
    wsgi.py
  coninggu/ # 앱 디렉토리
    __init__.py
    admin.py
    apps.py
    models.py
    tests.py
    views.py
  manage.py

위 레이아웃이 기본 레이아웃인데 불편한 점이 한 두가지가 아니다.

  • 환경별로 settings을 분리하고 싶다면?
  • view 코드가 views.py 파일 하나로 작성하기엔 라인수가 많다면?
  • model도 view와 마찬가지라면?

이를 해결할 수 있는 방법으로는 앱으로 구분짓는 것이다. 그리고 이것이 일반적으로 가장 많이 쓰이는 레이아웃이다. 다만, 이 또한 개인적으론 불편한 점이 생기더라. 이 디렉토리가 앱 디렉토리인지부터 view, model 코드가 여럿 디렉토리에서 작성되다 보니 찾아가기 힘들다는 점 등이다. 또한, git와 패키지 관리 도구인 setup.py, README, .gitignore 등이 위치할 디렉토리 레벨도 필요하다.


다시 작성하는 프로젝트 레이아웃

그래서 내가 선호하는 프로젝트 레이아웃은 아래와 같다.

learn.django
  .gitignore
  setup.py
  README.md
  VERSION
  Dockerfile
  docker-compose.yml
  boot/ # 가상환경
  coninggu/
    coninggu/ # 프로젝트 디렉토리
      settings/ # 장고 settings 
        __init__.py
        base.py
        dev.py
        prod.py
        staigng.py
        test.py
      __init__.py
      urls.py
      wsgi.py
    coningguapp/ # 앱 디렉토리
      forms/
      migrations/
      models/
      templatetags/
      tests/
      utils/
      views/
      __init__.py
      admin.py
      apps.py
      urls.py
    media/ # 개발시 미디어 파일이 저장되는 디렉토리
    static/ # 정적 파일이 위치하는 디렉토리
    templates/ # 템플릿 디렉토리

최상위 레벨에는 앞서 말한 장고 프로젝트와는 무관하지만 개발시에 필요한 파일들이 위치해 있다. 두번째 레벨은 장고 프로젝트의 소스들이 위치한 디렉토리이다. 세번째 레벨이 개발시에 사용하게 될 메인 디렉토리이다. 장고 프로젝트와 장고 앱이 위치해 있고, templates, static, media 디렉토리가 위치해 있다. templates 디렉토리를 앱 안에 위치하지 않고 밖으로 뺀 이유는 개발자보단 디자이너를 위한 디렉토리이고, 개발의 편의성 때문이다. 장고 앱은 오직 하나만 생성하였고, views, models, forms, tests는 디렉토리를 생성하여 관리하도록 하였다. urls도 디렉토리화하여 작성할 수 있다. 다만, 프로젝트의 규모에 따라서 디렉토리화 할지는 정하면 될 것이다. 이와 같은 레이아웃은 코드에서 몇 가지 수정을 해줘야 한다.


settings.py 환경별로 분리하기

레이아웃에 settings/를 보면 환경별로 구분해 놓은 것을 볼 수 있다. base를 제외한 모든 settings은 아래 코드처럼 base를 import 하도록 변경해야 한다.

# settings/dev.py
from .base import *

그리고 환경별로 settings를 재 정의해주면 된다. 위 상태로 runserver 커맨드를 실행하면 “ModuleNotFoundError” 에러가 발생한다. 서버를 실행할 때settings 파일을 참조하는데 해당 모듈을 찾을 수 없다는 에러임으로 이 부분도 수정해줘야 한다.

# manage.py
...
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'coninggu.settings.dev')
...

위와 같이 코드를 수정하면 runserver 시 기본 settings 으로 dev를 참조한다.다른 환경으로 runserver를 하려면 settings 옵션을 추가하면 된다.

(boot) $ ./manage.py runserver --settings=coninggu.settings.prod # prod 환경으로 runserver

마찬가지로 wsgi.py도 수정하자.

# coninggu/wsgi.py
...
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'coninggu.settings.dev')
...

settings 값 변경

templates이 앱 디렉토리 밖에 위치해 있음으로 이를 장고에게 알려줘야 한다. 또한 settings.py가 settings/ 디렉토리 하위에 위치함으로 BASE_DIR를 변경해줘야 한다. 추가적으로 static과 media에 대해서도 장고한테 알려주자.

# settings/base.py
...
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # 변경
...
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ['templates'], # templates 추가
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
...
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static') # 추가
]

MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # 추가

MEDIA_URL = '/media/' # 추가
...

자 이제 새로운 레이아웃에 대한 설정을 맞췄다. 다시 runserver하여 “Hello World!!!”가 출력되는지 확인하자.


django-debug-toolbar

디버그 툴바는 개발시에 꼭 필요한 도구이다. 요청과 응답에 대한 다양한 디버그 정보를 보여주는데 가장 유용한 정보는 쿼리로 인해 발생하는 병목 현상을 확인하고 디버깅하여 문제를 해결할 수 있게 도와준다. 장고 프로젝트를 시작하기에 앞서 항상 이 디버그 툴바 도구는 우선적으로 설치하길 바란다.

(boot) $ pip install django-debug-toolbar
# coninggu/urls.py
from django.conf import settings
from django.contrib import admin
from django.urls import path, include

from coningguapp.views.homepage import HomepageView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', HomepageView.as_view(), name='homepage'),
]

if settings.DEBUG:
    import debug_toolbar

    urlpatterns = [path('__debug__/', include(debug_toolbar.urls)), ] + urlpatterns
# coninggu/urls.py
from django.conf import settings
from django.contrib import admin
from django.urls import path, include

from coningguapp.views.homepage import HomepageView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', HomepageView.as_view(), name='homepage'),
]

if settings.DEBUG:
    import debug_toolbar

    urlpatterns = [path('__debug__/', include(debug_toolbar.urls)), ] + urlpatterns

다시 http://127.0.0.1:8000로 접근해서 브라우저 오른쪽 상단에 DjDT 버튼이 활성화 되어 있는지 확인해보자. 코드를 간단히 설명하면 디버그툴바는 개발시에만 사용할 도구이기에 dev.py에 추가하였고, 마찬가지로 urls.py도 settings.DEBUG 값으로 개발 환경에서만 활성화 되도록 추가하였다.