Django framework에서 기본적으로 사용되는 design pattern(링크참조)이며 이 design pattern에서 유명한 것이 M(model).V(view).C(controller) 패턴이다. 하지만 django에선 V와 C 부분이 각각 Template, View(django view)로 치환되어 사용되는데 그에 따른 철학적인 이유는 django 공식문서에 잘 나와 있다.(django docs 참조)
위 이미지와 같이 클라이언트에 대해서 request를 URLConf를 통해 view가 받게 되고 view에서 해당 request에 대한 처리를 진행하면서 데이터가 필요하다면 그걸 model을 기반으로 C(Create), R(Read), U(Update), D(Delete)작업을 진행하고 결과를 받아온다. Model은 DB에 ORM기법을 사용하여 DB테이블을 class의 형태로맵핑해서 DB테이블을 생성한다. 이렇게 구성된 데이터를 기반으로 view에서 business logic을 수행하며 그 결과 값을 template에 rendering하여 Client에게 응답을 하게끔 되어 있다.
이와 같은 일련의 과정에 대한 Design pattern을 MVT 패턴이라고 부른다.
model에선 앞서 언급했던 것 처럼 DB(테이블)을 생성하기 위한 데이터에 관한 정의가 class기반으로 이뤄진다. 따라서 하나의 class는 하나의 DB 테이블을 의미한다.
from django.db import models
class Users(models.Model):
name = models.CharField(max_length=50)
email = models.CharField(max_length=50)
password = models.CharField(max_length=300)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
django 상에서 이런 식으로 models.py
를 정의하면 위와 같이 정의내릴 수 있는데, django에선 아래와 같은 명령어를 통해 실제 DB테이블이 어떤 쿼리로 실행 되었는지 확인할 수 있다.
$ python manage.py sqlmigrate simple_end 0001
BEGIN;
--
-- Create model Users
--
CREATE TABLE "simple_end_users" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(50) NOT NULL, "email" varchar(50) NOT NULL, "password" varchar(300) NOT NULL, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL);
COMMIT;
DB테이블의 이름은 <appname><class_name>형태로 정의 되어 있으며 PK가 자동으로 들어가는 것을 확인할 수 있다. model에 정의된 여러 column 속성들은 실제 선언된 Field 생성자에 맞게 쿼리가 정의된 것을 확인할 수 있다.
이렇게 ORM을 통해 모델링을 하게되면 어떤 종류의 DB가 와도 django와 호환되는 DB라면 ORM이 자동으로 적당한 SQL문을 선언해 주기 때문에 모델 구현에 대한 코드의 재사용도 가능하다.
view는 클라이언트로 부터 요청을 받아 DB접속 등 해당 app의 맞는 logic을 처리하고 데이터를 HTML 템플릿처리를 한 후에 response를 하는 부분이다. 다시 말해 함수나 클래스 방식으로 구현하여 클라이언트의 요청을 받는 것이다.
import json
from django.views import View
from django.http import JsonResponse
from .models import Users
class MainView(View):
def post(self, request):
data = json.loads(request.body)
Users(
name = data['name'],
email = data['email'],
password = data['password']
).save()
return JsonResponse({'message':'SUCCESS'}, status=200)
def get(self, request):
return JsonResponse({'Hello':'World'}, status=200)
위의 예제는 클래스 방식으로 view를 작성한 것이다. 템플릿을 반환하는 view가 아닌 클라이언트에 json의 형태로 주고 받는 형태의 view이다. 이전에는 풀스텍으로 view에서 직접 템플릿을 불러오고 데이터를 입히는 방식으로 구현해 나갔지만 요즘은 back-end기능만 전적으로 담당해서 데이터를 주고 받는 부분만 구현이 되는 편이다.
웹 클라이언트로부터 request를 받게되면 가장 먼저 URLConf를 통해 url패턴을 분석한다. django에선 복잡한 URL까지 쉽게 처리 할 수 있을 정도로 URLConf가 잘 정의 되어 있다.
# Generic view django tutorial polls app
# Django에서 정의된 generic view를 활용하게 되면 코드 사용량을 줄일 수 있다
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:pk>/results', views.ResultsView.as_view(), name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
위 코드에서 \<int:question_id> 부분의 path를 보면 해당 url은 views.vote라는 메소드에서 처리 됨을 알 수 있고 \<int:question_id>와 같은 패턴들은 보통 \<type:name>의 방식으로 사용할 수 있다. 패턴과 매칭이 되지 않는 형식의 argument가 들어갈 경우 제대로 된 url로 접속할 수 없다.
\<type:name> 이 부분은 Path converter로 정의될 수 있는데 해당 부분에 사용되는 타입은 다음과 같다.
1) settings.py
파일의 root_urlconf 항목을 읽어 최상위 URLconf(urls.py
)의 위치를 확인
2) URLconf를 로딩하여 urlpatterns 변수에 지정 되어있는 url 리스트를 검사
3) 위에서 부터 순서대로 URL 리스트의 내용을 검사하면서 URL 패턴이 매치되면 검사 종료
4) 매치된 URL의 뷰를 호출. 여기서 뷰 함수 또는 클래스 메소드가 된다. 호출 시 HttpRequest 객체와 매칭할 때 추출된 단어들을 view에 인자로 넘겨준다
5) URL리스트 끝까지 검사했는데 매칭이 안되면 error 호출
django가 view에서 logic을 수행한 이후 html에 데이터를 붙여 반환하게 되는데 여기서 html에 해당하는 부분이 template이다. 기존 html 문법 말고도 django template 문법이 적용되어 있으며 주의해야 할 점은 해당 template파일을 적당한 디렉토리에 위치 시켜야 하는 것이고 그 경로는 settings.py
에서 정해줄 수 있다. 지금 개발에선 이 부분이 FE쪽 개발로 대체되고 있는 편이다.
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'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',
],
},
},
]