그 다음부터 무엇을 해야할지 막막했는데 세션에서 멘토분이 하신 말씀에 어느정도 감을 잡을 수 있었다.
구현을 하는 경우 동작 순서의 정반대부터 하면 된다고 하셨다.
즉, 구현을 할 경우 모델 작성 -> 뷰 작성 -> 경로 작성
하는 순서로 하면 될것이다.
먼저 앱을 만들어야한다.
장고 프로젝트의 앱은 작은 구성 단위이다. 자신만의 모델, 템플릿, 뷰를 포함하고 있으며, 프로젝트는 여러 개의 앱들로 구성된다.
나는 계정과 관계된 것(회원가입 및 로그인)을 처리하는 account
앱과 포스팅된 글에서 코멘트 관련해서 처리하는 comment
앱 두 가지로 나눠서 만들것이다.
먼저 account
앱을 생성해보자.
위에서 말한대로 계정에서 회원가입 및 로그인을 처리하는 account
앱을 만들것이다.
django-admin startapp account
그러면 겉 껍데기westagram_project
디렉토리 밑에 account
디렉토리가 생겨난다. 위에서도 설명했듯이 모델 작성 -> 뷰 작성 -> 경로 작성
순서로 진행할것이다.
그전에 새로운 앱을 만들었으니 settings.py
에 account
앱을 등록해줘야한다. 등록해줘야 장고가 인식을 해준다.
INSTALLED_APPS = [
# 'django.contrib.admin',
# 'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'account',
]
이제 모델을 작성해보자.
# models.py
from django.db import models
class Account(models.Model):
email = models.CharField(max_length=200)
password = models.CharField(max_length=400)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'accounts'
Account
라는 클래스를 만들자. 모든 모델 클래스는 models.Model
을 상속받는다. (커스터마이징을 할 경우가 아니면)
models.Model을 상속받는 이유?
models.Model이 가지고 있는 메서드나 속성값을 사용하기 위해서다. ORM관련 기능을 갖고 있는데, ORM이란 DB를 추상화해서 코드로 조작할 수 있게 하는 기능이다. 데이터를 추가, 수정, 검색, 삭제가 가능해진다.
MDN 내용 중 일부 발췌
모델들은 보통 어플리케이션의 models.py 파일에서 정의됩니다. 이들은 django.db.models.Model의 서브 클래스로 구현되며 필드, 메소드 그리고 메타데이터를 포함할 수 있습니다.
모델 내부에 DB에 쌓아줄 데이터의 틀을 만드는 것이다. 변수들은 데이터베이스의 필드명이 된다. 회원가입 및 로그인에서 email과 password를 받을 것이다. CharField
의 max_length
로 최대값을 설정했다. CharField
는 데이터베이스의 SQL문에서 VARCHAR(n)
을 의미한다. 즉, 문자열 타입이다.
DateTimeField
는 년월일을 갖고오는데, auto_now_add = True
를 넣어주면 처음 생성된 시점을 자동으로 기록해준다. auto_now = True
를 넣어주면 필드 업데이트마다 시점을 기록해준다.
Meta
클래스 부분은 이 테이블이 생성되었을 때, 테이블 명을 accounts
로 만들어지게 만들어준다. 'db세계에서 이름을 accounts
로 지정하겠다.' 라는 의미이다. 만약 Meta
클래스를 만들어놓지 않으면 account_Account
(앱이름_클래스명) 같은 좋지 못한 이름으로 생성된다.
여기까지하면 기본적인 테이블 모양을 잡은것이다.
모델을 새로 생성 및 작성, 수정을 한 이후에 필수적으로 해야할 사항이 있다.
모델은 결국 DB라는 상자에 담겨질 틀 하나를 만든것이다. 그런데 그 틀이 제대로 만들어져서 적용이 될지, 지금 장고(파이썬)으로 작성된걸 DB세계에서는 어떻게 해석해서 적용시킬지를 알아야한다.
makemigrations
명령어는 모델의 생성/변경사항을 장고에서 인지해서 DB에 적용시킬 SQL 문장으로 장고가 알아듣도록 파이썬 코드로 자동으로 생성하는 명령어이다. 즉, models.py 에서 적용한 변경사항이나 추가, 삭제 사항들을 감지하고 마이그레이션 파일(초안)을 생성하는 역할을 한다. 이렇게 생성된 파일들로 인해 장고는 모델의 변경사항을 저장할 수 있다. 내가 직접 작성한 model.py의 class들을 SQL command로 만들어서 파일로 만든것이다.
makemigrations
뒤에 마이그레이션을 할 앱 이름을 지정해서 적어주는게 좋다. 만약 앱 모델 간에 참조 관계가 있는 경우 순서가 중요할 때가 있기 때문이다.
makemigrations
로 생성된 코드를 장고가 SQL 문장으로 변환해서 DB에 적용하는 명령어이다. 모델이 생성/변경되어서 생긴 마이그레이션 파일을 DB에 반영하는 명령어인 것이다. 즉, 마이그레이션 파일에 작성된 SQL 문법을 사용해서 데이터베이스에 테이블을 만든다.
python manage.py migrate account 0001
# 마이그레이트 할 앱 이름과 마이그레이션 번호를 지정했다.
# 지정하지 않으면 전체 프로젝트 대상으로 마이그레이트한다.
현재 적용된 마이그레이션 파일의 상태를 확인할 수 있다. [X] 로 체크가 되있다면 migrate 된것이고, 아닌것은 아직 적용이 안된것이다.
해당 명령어로 생성된 마이그레이션 파일이 어떤식으로 SQL 문장으로 바뀌고, 실행되는지 확인할 수 있다.
모델을 작성하고서 migrations와 migrate를 해줘야한다. 하지만 무작정하기보다는 순서에 맞춰서, 그리고 때때로 showmigrations와 sqlmigrate를 중간중간 섞어가면서 하는걸 추천한다.
무조건 1-2-3-4 순서로 할 필요는 없다. showmigrations와 sqlmigrate는 중간중간 확인할 겸 순서를 바꿔서 해도 괜찮다. 그러나 일단 migrate를 하기전에 sqlmigrate로 미리 확인하는 습관은 중요하다.
실제로 데이터베이스에 변화를 가한다는건 굉장히 중요한 일이기에 미리 확인해보는 것이다.
import json
from django.views import View
from django.http import HttpResponse, JsonResponse
from .models import Account
json
을 import 해왔다.회원가입을 담당할 SignUp
클래스를 만든다.
일단 배운대로 Post
요청이 들어올 때 회원가입 시키게 만들것이다. 왜냐하면 Post
는 데이터를 입력, 수정할 때 post
요청이 들어오기 때문이다.
요청(request)을 통해 들어온 json 데이터를 json.loads
가 파이썬이 읽을 수 있게 디코딩해준다. 이 데이터에서 우리가 필요한 데이터를 뽑아, 위에서 만든 Account 모델의 DB에 저장한다.
Model Manager를 통해서 해당 모델 클래스의 DB 데이터를 추가, 조회, 수정, 삭제(CRUD)가 가능하다.
ModelClass.objects.all() : 특정 모델의 전체 데이터 조회
ModelClass.objects.all().order_by('-id')[:10] : 특정 모델의 최근 10개 데이터 조회
ModelClass.objects.create(title="New Title") : 특정 모델의 새 Row 저장
model_instance = Post(author=User.objects.all()[0], title='title', text='content')
model_instance.save()
new_post = Post.objects.create(author=User.objects.get(id=1), title='title', text='content')
위에서 알아본 대략적인 개념을 적용해서 뷰를 만들어보자.
# 회원가입 부분
class SignUpView(View):
def post(self, request):
signup_data = json.loads(request.body) # JSON 데이터를 python으로 파싱해서 data에 저장
try:
if Account.objects.filter(email=signup_data['email']).exists():
return HttpResponse(status=409)
Account.objects.create(
email=signup_data['email'],
password=signup_data['password'],
)
return HttpResponse(status=200)
except KeyError:
return JsonResponse({"message": "INVALID_KEYS"}, status=400)
# 로그인 부분
class SignInView(View):
def post(self, request):
signin_data = json.loads(request.body)
try:
if Account.objects.filter(email=signin_data['email']).exist():
user = Account.objects.get(email=signin_data['email'])
if user.password == signin_data['password']:
return HttpResponse(status=200)
except KeyError:
return JsonResponse({"message": "INVALID_KEYS"}, status=400)
이 프로젝트의 경로 설정은 settings.py에서 볼 수 있다.
ROOT_URLCONF = 'westagram_project.urls'
이 코드로부터 최초 경로를 관리하는 걸로 볼 수 있다.
해당 경로의 파일로 들어가보자.
# westagram_project/urls.py
from django.urls import path, include
urlpatterns = [
path('account', include('account.urls')),
]
include
를 추가했다.include
는 앞에 인자로 있는 경로로 들어오게되면, 그때부터 경로는 include
안에 있는 곳에서 관리하겠다는 것이다.이제 include로 넘어온 account 앱의 urls.py 코드를 작성해보자.
이곳에서는 다른 곳으로 url이 넘어가지 않기에 include는 필요는 없다. 들어온 요청 데이터를 처리하는 곳이 views
이다. 그리고 그 안에 만들어뒀던 SignUpView, SignInView를 통해서 데이터를 처리한다.
view 클래스는 내장 함수를 반환하는 as_view() 를 메서드로 제공한다. (모든 클래스 기반 뷰는 이 클래스를 직간접적으로 상속받는다.)
as_view()
는 클래스의 인스턴스를 생성하고, 이 인스턴스의 dispatch() 메서드를 호출한다. dispatch() 메서드에서는 view가 받은 요청을 검사해서 HTTP의 메소드(GET, POST)를 알아낸다. 인스턴스 내에 해당 이름을 갖는 메서드로 요청을 중계한다. 해당 메서드가 정의되지 않으면, HttpResponseNotAllowd 예외를 발생시킨다.
localhost:8000/signup email password 로 요청이 들어오면 as_view()가 SignUpView, SignInView 로 인스턴스를 생성하고, dispatch() 메서드를 통해서 들어온 요청 메서드가 뷰 안에서 정의한 POST인지 확인한다. 이 경우 POST가 있으면 정상적으로 SignUpView의 논리에 따라 응답이 처리되서 반환된다.
# account/urls.py
from django.urls import path
from .views import SignInView, SignUpView
urlpatterns = [
path('/signup', SignUpView.as_view()),
path('/signin', SignInView.as_view()),
]
처음 설명대로 account 앱의 모델 작성 -> 뷰 작성 -> 경로 작성을 완료했다.
이제 comment 앱을 만들어보자.