[django]새 프로젝트 시작- 회원가입/ 로그인 앱(app) 생성)

hyeyul·2020년 5월 7일
0

Django

목록 보기
4/8

가상환경 설치

우선 프로젝트 생성전에 먼저 새로운 가상환경을 세팅해야한다. 각각의 프로젝트가 사용하는 파이썬, 장고 등의 프레임워크 버전이 다를 수 있기 때문에 충돌이 생기지 않도록 새 프로젝트를 생성할 때마다 새로 가상환경을 세팅하는게 좋다.

가상환경을 생성하는 명령어는 다음과 같다.

conda create -n <name> <python version>
  
conda create -n project4 python==3.8

실제로 생성된 가상환경들을 보려면,

condan env list

이렇게 입력하면 된다.

장고설치 및 프로젝트 생성

pip install django

이렇게 장고를 설치하고 나면, 새 프로젝트를 생성한다.

django-admin startproject insta_ex   # 프로젝트명: insta_ex

insta_ex
├── init.py
├── pycache
│   ├── init.cpython-38.pyc
│   ├── settings.cpython-38.pyc
│   ├── urls.cpython-38.pyc
│   └── wsgi.cpython-38.pyc
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py

프로젝트를 생성한 후 트리 명령어를 통해 해당 디렉토리를 확인해보면, 이렇게 새로운 디렉토리와 파일들이 생성된 것을 볼 수 있다.

그리고 기존에 만들어진 admin 부분을 사용하지 않을 예정이고, auth나 csrf 등 권한, 보안적인 부분도 배재하고 진행한다. 그래서 다음의 요소를 각각 주석하였다.

INSTALLED_APPS = [
  # 'django.contrib.admin',
  # 'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    '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',
]

앱(app)생성

이제 회원가입과 로그인을 관리하는 account앱을 만들어 보자.

django-admin startapp account

생성한 앱은 settings.py에 등록을 해줘야 django가 인식을 하기 시작한다. settings.py 에 들어가 INSTALLED_APPSaccount를 추가한다. 이 단계를 잊지말고 꼭 해줘야 오류가 안난다.

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'account', # account 앱 등록
]

@ account/urls.py

from django.urls import path
from .views import SignUpView, SignInView
  
   urlpatterns = [
       path('/sign-up',SignUpView.as_view()),
       path('/sign-in',SignInView.as_view()),
   ]

모델 작성

account 안에 models.py를 열고 다음과 같이 작성한다.

@account/models.py

from django.db import models
  
class Account(models.Model):
      email = models.CharField(max_length =200)
      password = models.CharField(max_length = 100)
      created_at = models.DateTimeField(auto_now_add =True)
      updated_at = models.DateTimeField(auto_now=True)
 
class Meta:
      db_table ="accounts"

모델로 사용할 Account 클래스를 만든다.(참고로 클래스명은 맨 앞은 대문자이면서, 단수로 만든다.) 커스터마이징 하지 않는 이상 모든 클래스는 django에 포함된 models.Model을 상속받는다.

모델 내부에는 향후 DB(데이터베이스)에 쌓아줄 데이터의 기준을 만들어준다. 회원가입 및 로그인 과정에서는 이메일과 패스워드만 받을 예정이므로, 각각의 필드를 만들어준다. 그리고, 기본적으로 시점에 대한 기록을 남기기 위해 처음 계정이 생성된 시점과 업데이트 되는 시점을 필드로 만든다.

그리고 각 필드에는 특정 설정값을 인자로 줄 수 있다. CharField의 경우에는 max_length로 최대값을 설정해 줄 수 있다. 해당 필드가 비어있어도 괜찮다면 blank=true 값을 추가할 수 있지만, 이메일과 패스워드는 필수이므로 추가하지 않는다.

DateTimeField는 날짜와 시간을 가져오는 필드인데, auto_now_add = True를 넣어주면 처음 생성(추가)된 시점을 자동으로 기록해주고, auto_now = True를 넣어주면 필드가 업데이트 될 때마다 그 시점을 기록해준다.

마지막으로 내부에 Meta 클래스를 추가해 db_table을 선언해준다. 이 필드값은 DB에 account 데이터가 어떤 이름으로 테이블에 기록될지 정의해줍니다. 설정하지 않으면 'account_Account' 같은 이상한 값으로 기록되므로 원하는 값으로 바꿔줘야 한다.

모델을 다 작성해줬으면 migrationmigrate를 해줘야한다. 장고는 migrations 테이블을 두어 마이그레이션 적용 여부를 추적하고, migrate 할 때 문제가 있는지 미리 확인해준다. (migrate 과정은 model 작성, 수정시에만 적용된다.)

먼저 마이그레이션을 해보자.
makemigrations 뒤에 마이그레이션을 할 앱을 지정해주는게 좋은데, 만약 앱 모델 간에 참조 관계가 있는 경우 순서가 중요할 때가 있기 때문이라고 한다.

python manage.py makemigrations <앱 이름>  # 명령어 구성, 앱 이름 생략 시 프로젝트 전체에서 마이그레이션 진행
python manage.py makemigrations account   # 실제 명령어 작성

뷰 작성

뷰는 작성된 모델을 바탕으로, 들어오는 데이터를 어떻게 처리할지에 대한 논리를 맡고 있다.
첫 부분에는 우리가 사용할 모듈들을 불러와야 하는데, 먼저 우리는 http를 통해 받은 요청 정보(json)를 파이썬이 읽을 수 있는 형태로 변환해줄 수 있는 모듈인 json을 임포트 해오고, json 형태로 응답(response) 해줄 수 있는 JsonResponse 모듈과 Http를 통해 받은 요청에 응답할 수 있는 HttpRespose 모듈을 불러온다. 그리고 뷰에서 처리할 정보가 쌓이는 기준이 되는 Account class를 모델에서 불러온다.

import json
from django.views import View
from django.http import HttpResponse, JsonResponse
from .models import Account

참고로 json.loads는 json 형태의 데이터를 파이썬이 읽을 수 있는 형태로 디코딩 해주는 역할을 한다.
json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]')
['foo', {'bar': ['baz', None, 1.0, 2]}]

회원가입 뷰 (SignUpView)

이제 회원가입을 담당하는 SignUpView를 만들어 보자.
회원가입 클래스에서는 POST요청이 들어오면 회원가입을 처리해주는 로직으로 만들어야 한다.

class SignUpView(View):
    def post(self,request):
        data = json.loads(request.body)
        try:
            if Account.objects.filter(email = data['email']).exists():
                 return HttpResponse(status=400)
            Account.objects.create(
               email = data['email'],
               password = data['password']
               )
            return JsonResponse({'message': '회원가입 성공'},status=200)
       except keyError:
         return JsonResponse({"message":"INVALID_KEYS"},status=400)

data는 json.loads를 통해 요청 받은 데이터를 파이썬이 읽을 수 있는 형태로 디코딩이 된다.
이 데이터에서 우리가 필요한 데이터를 뽑아 Account모델의 DB에 저장한다. 그러나 회원가입을 할 때 이미 가입한 이메일일 수 있으므로, filter함수를 통해 DB에 저장된 이메일 중 이 이메일이 있는지 확인 작업을 거치게 코드를 작성했다.
이미 등록된 이메일이면 status=400 코드를 리턴하게 해주었다. 그렇지 않다면, 데이터를 저장하고 message와 함께 status=200코드를 리턴해준다.
또한, 사용자가 email 또는 password를 입력하지 않았을때 keyError가 발생하도록 키에러 처리 구문도 넣어주었다.

< Http 상태코드 (status) >

  • 200(성공): 서버가 요청을 제대로 처리했다는 뜻이다. 이는 주로 서버가 요청한 페이지를 제공했다는 의미로 쓰인다.
  • 400(잘못된 요청): 서버가 요청의 구문을 인식하지 못했다.
  • 401(권한 없음): 이 요청은 인증이 필요하다. 서버는 로그인이 필요한 페이지에 대해 이 요청을 제공할 수 있다. 상태 코드 이름이 권한 없음(Unauthorized)으로 되어 있지만 실제 뜻은 인증 안됨(Unauthenticated)에 더 가깝다.(인증실패)
  • 500(내부 서버 오류): 서버에 오류가 발생하여 요청을 수행할 수 없다.

로그인 뷰(SignInView)

이제 로그인 뷰를 만들어 보자.
우선 로그인 뷰는 사용자가 입력한 데이터를 전송해야하므로 POST함수를 써야한다.
그리고 로그인 뷰는 사용자가 입력한 이메일과 패스워드가 DB에 있는 값과 일치하는 지를 먼저 확인해야 한다.
그런 후 일치하면 패스워드를 확인하는 순으로 코드를 작성했다.
비밀번호가 틀리면 인증실패 코드401를 리턴해주고 이메일 자체가 등록되어 있지 않다면 잘못된 요청의 코드400을 리턴했다.

class SignInView(View):
    def post(self, request):
        data = json.loads(request.body)

        if Account.objects.filter(email = data['email']).exists() :
            user = Account.objects.get(email = data['email'])
            if user.password == data['password'] :
                return JsonResponse({'message':f'{user.email}님 로그인 되셨습니다.'}, status=200)
            else :
                return JsonResponse({'message':'비밀번호가 틀렸어요'},status = 401)

        return JsonResponse({'message':'미등록 이메일 입니다.'},status=400)

경로(URL) 작성

이제 경로를 작성해보자!
startproject 인 insta_ex에서 settings.py에 들어가면 ROOT_URLCONF라는 부분에서 어디서 최초 경로를 관리하는 지 알 수 있다.

ROOT_URLCONF = 'insta_ex.urls'

urls.py에 들어가서, import에 include를 추가해준다.

from django.urls import path,include

 urlpatterns = [
 
    path('account',include('account.urls'))

include는 인자로 받아지는 경로로 들어오면 그때부터는 그 곳에서 관리한다는 뜻이다.
즉, 기본 url뒤에 /account 를 치고 들어오면 그 뒤의 url은 account.urls에서 처리를 하겠다는 의미이다. 또한, 기본 url은 마지막에 "/"을 자동으로 붙여주기 때문에 또 적으면 "//"이렇게 되므로 절대 붙이면 안된다.

acccount.urls

자, 이제 최상위 경로 urls를 작성했으니 그 다음 account의 urls.py에 들어가서 작성해보자.

 from django.urls import path
 from .views import SignUpView, SignInView
  
  urlpatterns = [
      path('/sign-up',SignUpView.as_view()),
      path('/sign-in',SignInView.as_view()),
  ]

클래스 기반 뷰

뷰 클래스는 내장 함수를 반환하는 as_view() 클래스 메서드를 제공한다.
django.views.generic.View 에서 아래와 같은 매커니즘이 구현되고, 모든 클래스 기반 뷰는 이 클래스를 직간접적으로 상속받아 이용한다.

  • as_view()에서 클래스의 인스턴스를 생성
  • 인스턴스의 dispatch() 메서드 호출
  • dispatch() 메서드에서 HTTP GET/POST 메서드를 알아냄
  • 인스턴스 내 해당 메서드로 중개를 한다
  • 메서드가 정의되어 있지 않으면 HttpResponseNotAllowed 예외 발생

방금 만든 우리의 뷰를 가지고 대입해보면, http://127.0.0.1:8000/account/sign-up email=misun@gggg password=1234을 통해 요청이 들어오면, as_view()SignUpView의 인스턴스를 생성하고, dispatch() 메서드를 통해 들어온 요청 메서드가 뷰 안에서 정의한 POST인지 확인한다. 이 경우 POST가 있기 때문에 정상적으로 정의된 SignUpView의 논리에 따라 응답이 처리되서 반환된다.

자, 이제 회원가입, 로그인앱이 완성되었다. 잘 작동하는지 확인하기 위해 httpie를 활용해보자

먼저 서버를 시작하고, 다른 쉘을 켜서 http -v http:/127.0.0.1:8000/account/sign-up email=misun@naver.com password = 1234
이렇게 입력해보자.

POST /account/sign-up HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 52
Content-Type: application/json
Host: 127.0.0.1:8000
User-Agent: HTTPie/2.1.0

{
    "email": "misun@naver.com.com",
    "password": "1234"
}

HTTP/1.1 200 OK
Content-Length: 52
Content-Type: application/json
Date: Thu, 07 May 2020 08:17:22 GMT
Server: WSGIServer/0.2 CPython/3.8.0
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "message": "회원가입 성공"
}

이렇게 나오면 성공이다.!

0개의 댓글