[Django] westagram (2) - account 앱 만들기

Magit·2020년 4월 12일
1

westagram

목록 보기
2/4
post-custom-banner

그 다음부터 무엇을 해야할지 막막했는데 세션에서 멘토분이 하신 말씀에 어느정도 감을 잡을 수 있었다.
구현을 하는 경우 동작 순서의 정반대부터 하면 된다고 하셨다.
즉, 구현을 할 경우 모델 작성 -> 뷰 작성 -> 경로 작성 하는 순서로 하면 될것이다.

먼저 앱을 만들어야한다.
장고 프로젝트의 앱은 작은 구성 단위이다. 자신만의 모델, 템플릿, 뷰를 포함하고 있으며, 프로젝트는 여러 개의 앱들로 구성된다.

나는 계정과 관계된 것(회원가입 및 로그인)을 처리하는 account 앱과 포스팅된 글에서 코멘트 관련해서 처리하는 comment 앱 두 가지로 나눠서 만들것이다.

먼저 account 앱을 생성해보자.

앱 생성하기

위에서 말한대로 계정에서 회원가입 및 로그인을 처리하는 account 앱을 만들것이다.

django-admin startapp account

그러면 겉 껍데기westagram_project 디렉토리 밑에 account 디렉토리가 생겨난다. 위에서도 설명했듯이 모델 작성 -> 뷰 작성 -> 경로 작성 순서로 진행할것이다.

까먹지말자! 앱 등록하기!

그전에 새로운 앱을 만들었으니 settings.pyaccount 앱을 등록해줘야한다. 등록해줘야 장고가 인식을 해준다.

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를 받을 것이다. CharFieldmax_length 로 최대값을 설정했다. CharField는 데이터베이스의 SQL문에서 VARCHAR(n) 을 의미한다. 즉, 문자열 타입이다.

DateTimeField는 년월일을 갖고오는데, auto_now_add = True 를 넣어주면 처음 생성된 시점을 자동으로 기록해준다. auto_now = True 를 넣어주면 필드 업데이트마다 시점을 기록해준다.

Meta 클래스 부분은 이 테이블이 생성되었을 때, 테이블 명을 accounts로 만들어지게 만들어준다. 'db세계에서 이름을 accounts로 지정하겠다.' 라는 의미이다. 만약 Meta 클래스를 만들어놓지 않으면 account_Account (앱이름_클래스명) 같은 좋지 못한 이름으로 생성된다.


여기까지하면 기본적인 테이블 모양을 잡은것이다.


makemigrations? migate? showmigrations? sqlmigrate?

모델을 새로 생성 및 작성, 수정을 한 이후에 필수적으로 해야할 사항이 있다.
모델은 결국 DB라는 상자에 담겨질 틀 하나를 만든것이다. 그런데 그 틀이 제대로 만들어져서 적용이 될지, 지금 장고(파이썬)으로 작성된걸 DB세계에서는 어떻게 해석해서 적용시킬지를 알아야한다.

makemigrations

makemigrations 명령어는 모델의 생성/변경사항을 장고에서 인지해서 DB에 적용시킬 SQL 문장으로 장고가 알아듣도록 파이썬 코드로 자동으로 생성하는 명령어이다. 즉, models.py 에서 적용한 변경사항이나 추가, 삭제 사항들을 감지하고 마이그레이션 파일(초안)을 생성하는 역할을 한다. 이렇게 생성된 파일들로 인해 장고는 모델의 변경사항을 저장할 수 있다. 내가 직접 작성한 model.py의 class들을 SQL command로 만들어서 파일로 만든것이다.

  • 모델 필드와 관련된 어떠한 변경이라도 발생하면 마이그레이션 파일 생성
    • 실제로 DB 스키마에 가해지는 변화가 없더라도 수행하자
  • 마이그레이션 파일은 모델의 변경내역을 누적하는 역할을 한다.
    • 적용된 마이그레이션 파일은 삭제하면 안된다.
    • 마이그레이션 파일이 너무 많아지면, squashmigrations 명령으로 다수의 마이그레이션 파일을 통합할 수 있다.

makemigrations 뒤에 마이그레이션을 할 앱 이름을 지정해서 적어주는게 좋다. 만약 앱 모델 간에 참조 관계가 있는 경우 순서가 중요할 때가 있기 때문이다.

migrate

makemigrations로 생성된 코드를 장고가 SQL 문장으로 변환해서 DB에 적용하는 명령어이다. 모델이 생성/변경되어서 생긴 마이그레이션 파일을 DB에 반영하는 명령어인 것이다. 즉, 마이그레이션 파일에 작성된 SQL 문법을 사용해서 데이터베이스에 테이블을 만든다.

python manage.py migrate account 0001
# 마이그레이트 할 앱 이름과 마이그레이션 번호를 지정했다.
# 지정하지 않으면 전체 프로젝트 대상으로 마이그레이트한다.

showmigrations

현재 적용된 마이그레이션 파일의 상태를 확인할 수 있다. [X] 로 체크가 되있다면 migrate 된것이고, 아닌것은 아직 적용이 안된것이다.

sqlmigrate

해당 명령어로 생성된 마이그레이션 파일이 어떤식으로 SQL 문장으로 바뀌고, 실행되는지 확인할 수 있다.

Work Flow

모델을 작성하고서 migrationsmigrate를 해줘야한다. 하지만 무작정하기보다는 순서에 맞춰서, 그리고 때때로 showmigrationssqlmigrate를 중간중간 섞어가면서 하는걸 추천한다.

  1. 마이그레이션 파일 생성 (migrations)
  2. 지정 마이그레이션의 SQL 내역 출력 및 확인하기 (sqlmigrate)
  3. 지정 데이터베이스에 마이그레이션 적용 (migrate)
  4. 마이그레이션 적용 현황 출력 및 확인 (showmigratios)

무조건 1-2-3-4 순서로 할 필요는 없다. showmigrations와 sqlmigrate는 중간중간 확인할 겸 순서를 바꿔서 해도 괜찮다. 그러나 일단 migrate를 하기전에 sqlmigrate로 미리 확인하는 습관은 중요하다.
실제로 데이터베이스에 변화를 가한다는건 굉장히 중요한 일이기에 미리 확인해보는 것이다.



## 뷰 작성하기 이제 뷰를 만들기 위해서 `account/views.py` 을 건들여보자. 일단 존재하는 코드들을 전부 지운 후 필요한 녀석들로 채워보자.
import json

from django.views import View
from django.http import HttpResponse, JsonResponse

from .models import Account
  • http를 통해서 받은 요청 정보인 json이 파이썬이 읽을 수 있는 형태로 변환해주는 모듈인 json을 import 해왔다.
  • 장고의 내장 프레임워크에 내장된 클래스를 상속받기 위해서 django.views의 View도 import해왔다. 그리고 json 형태로 응답 해줄 수 있는 JsonResponse 모듈과 Http 요청에 대한 응답이 담겨진 HttpResponse를 import해왔다.
  • 또한 뷰에서 처리할 정보가 쌓이는 기준이 될 Account 클래스를 import했다.
    (위에 세 줄은 변하지 않을것이고, 순서도 이대로 유지하자.)

회원가입을 담당할 SignUp 클래스를 만든다.
일단 배운대로 Post 요청이 들어올 때 회원가입 시키게 만들것이다. 왜냐하면 Post는 데이터를 입력, 수정할 때 post 요청이 들어오기 때문이다.

요청(request)을 통해 들어온 json 데이터를 json.loads가 파이썬이 읽을 수 있게 디코딩해준다. 이 데이터에서 우리가 필요한 데이터를 뽑아, 위에서 만든 Account 모델의 DB에 저장한다.



여기서 잠시 필수 지식을 알아보고 가자!

Model Manager

  • 데이터베이스 질의 인터페이스를 제공한다. 디폴트 Manager로 모델클래스.objects 가 제공된다.

Model Manager를 통해서 해당 모델 클래스의 DB 데이터를 추가, 조회, 수정, 삭제(CRUD)가 가능하다.
ModelClass.objects.all() : 특정 모델의 전체 데이터 조회
ModelClass.objects.all().order_by('-id')[:10] : 특정 모델의 최근 10개 데이터 조회
ModelClass.objects.create(title="New Title") : 특정 모델의 새 Row 저장

QuerySet

  • SQL을 생성해주는 인터페이스
  • queryset을 통해서 별도로 SQL을 작성할 필요 없이 DB로 부터 데이터를 갖고오고 추가, 수정, 삭제가 가능해진다.
  • Model Manager를 통해 해당 Model에 대한 QuerySet을 획득한다.
    • Post.objects.all() : “SELECT * FROM post…” 와 같은 SQL문 생성
    • Post.objects.create() : “INSERT INTO post VALUES(…)” 와 같은 SQL문 생성
  • Chaining을 지원한다.
  • 지정 조건(get, first, last)
    • 지정 조건으로 DB로부터 데이터 Fetch
    • queryset.get
      • 해당 조건에 해당되는 데이터가 1개임을 기대
  • DB 데이터 추가 (Create)
    • 추가시에 필수필드 (필스 정의 시에, blank=True, null=True 혹은 디폴트값이 지정되지 않은 필드)를 모두 지정해야함. IntegrityError 발생
    • shell에서 모델명?? 을 통해 해당 모델의 상세 필드옵션을 확인할 수 있음
    • 방법1. 각 Model Instance의 save 함수를 통해 저장
    model_instance = Post(author=User.objects.all()[0], title='title', text='content')
    model_instance.save()
    • 방법2. 각 Model Manager의 create 함수를 통해 저장
    new_post = Post.objects.create(author=User.objects.get(id=1), title='title', text='content')
    • .save(), .create() 실행시 DB에 INSERT SQL이 전달된다.

위에서 알아본 대략적인 개념을 적용해서 뷰를 만들어보자.

# 회원가입 부분

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')),
]
  • 원래는 path가 import 되있었지만, include를 추가했다.
    • include는 앞에 인자로 있는 경로로 들어오게되면, 그때부터 경로는 include안에 있는 곳에서 관리하겠다는 것이다.
    • 기본 url은 마지막에 "/"를 자동으로 붙이기때문에 또 적으면 안된다.
      • path('/account', include('account.urls')) 라고 적으면 마지막에 //account 이라는 주소로 가버린다는 의미

이제 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 앱을 만들어보자.

profile
이제 막 배우기 시작한 개발자입니다.
post-custom-banner

0개의 댓글