westagram 회원가입 로그인 해보기

.·2020년 5월 15일
2

Coding

목록 보기
16/33

1. 가상환경 만들기

conda create -n westagram python=3.7

westagram 은 터미널 상에 표시되는 내가 설정한 가상환경의 이름이다. 뒤에 파이썬 버전을 명시해주지 않으면 기본 버전 2.x 대로 설치되어 질 것이므로 유의한다.

2. 가상환경 activate 하기

conda activate westagram

이렇게 해주면 가상환경이 활성화 상태가 된다. 이 가상환경에 대해서는 영상이나 자료를 봤지만은 음 한번 다시 보자.

2-1. 가상 환경이란?

  • 자신이 원하는 (아마도 Python) 환경 구축을 위해 필요한 모듈만을 모아놓은 것
  • 각각의 가상환경은 독립적이어서 서로 다른 가상환경에 설치된 모듈들의 영향을 받지 않는다.

2-2. 언제 가상환경을 사용하나요?

  • 서로 다른 가상환경에서 같은 모듈을 사용한다고 하더라도 다른 버전을 필요로 하는 경우가 있는 경우 사용함.
  • 어떤 프로그램을 실행하기 위한 최소한의 환경을 구축할 때
  • 파이썬에서는 한 라이브러리에 대해 하나의 버전만 설치가 가능합니다.
    여러개의 프로젝트를 진행하게 되면 이는 문제가 됩니다. 작업을 바꿀때마다 다른 버전의 라이브러리를 설치해야합니다.
  • 이를 방지하기 위한 격리된 독립적인 가상환경을 제공합니다.
    일반적으로 프로젝트마다 다른 하나의 가상환경을 생성한 후 작업을 시작하게 됩니다.
  • 가상환경의 대표적인 모듈은 3가지가 있습니다.
    venv : Python 3.3 버전 이후 부터 기본모듈에 포함됨
    virtualenv : Python 2 버전부터 사용해오던 가상환경 라이브러리, Python 3에서도 사용가능
    conda : Anaconda Python을 설치했을 시 사용할 수있는 모듈
    pyenv : pyenv의 경우 Python Version Manger임과 동시에 가상환경 기능을 플러그인 형태로 제공

3. 가상환경에 django 설치하기

pip install django

pip is the package installer for Python. You can use pip to install packages from the Python Package Index and other indexes.
pip 을 터미널에 입력하면 용법과 커맨드 등이 나오므로 참고하자.

4. 프로젝트 만들기

django-admin startproject westagram_project

뒤에 프로젝트 명을 적고 보통 상위 루트에서 만든다.

5. 계정 app 만들기

프로젝트를 만들고 나면 ls 로 확인 시에 westagram_project 라는 폴더가 생긴 것을 확인할 수 있다.
그러면 그 프로젝트 폴더로 들어가서 앱을 만들건데 그 앱은 바로 회원가입을 하고 로그인을 하게 될 앱이다.
그럼 앱 이름을 account 로 해주고 만들자.

django-admin startapp account
westagram_project
├── account
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── manage.py
└── westagram_project
    ├── __init__.py
    ├── asgi.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

tree 최상위의 westagram_project 는 겉 껍데기 디렉토리 아래에 account 디렉토리가 생겨난다.

6. account app 등록하기

방금 만든 app 을 settings.py 에 있는 INSTALLED_APPS 에 등록해준다.

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

아 근데 여기서 나는 'account.apps.AccountConfig', 이렇게 등록했었다.
왜냐하면 account 폴더 내의 apps.py 에 들어가보면 저렇게 되어 있어서 그대로 입력했다. 물론 동작하는데는 문제가 없지만 굳이 앱 이름만 써도 되는 것을 저렇게 길게 쓸 필요가 있었을까.

7. model 작성하기

갑자기 장고 orm 에 대해 알아보자면 db를 추상화해서 코드로 조작할 수 있게 하는 기능이라 하며 데이터 추가, 수정, 검색, 삭제가 가능하다고 한다.

좀 더 자세한 설명은 아래와 같다.

장고는 ORM이라는 것을 제공하고 있습니다. ORM? 오알엠이 뭔가요? 라고 물으실것 같아 준비했습니다.

Object Relational Mapping, 객체-관계 매핑
객체와 관계형 데이터베이스의 데이터를 자동으로 매핑(연결)해주는 것을 말한다.
객체 지향 프로그래밍은 클래스를 사용하고, 관계형 데이터베이스는 테이블을 사용한다.
객체 모델과 관계형 모델 간에 불일치가 존재한다.
ORM을 통해 객체 간의 관계를 바탕으로 SQL을 자동으로 생성하여 불일치를 해결한다.
데이터베이스 데이터 <—매핑—> Object 필드
객체를 통해 간접적으로 데이터베이스 데이터를 다룬다.

이와 같이 ORM을 사용하면서 원하는 모델을 작성하고 다루는 곳이 바로 models.py 파일 입니다.

쉽게 말하면 DATABASE를 잘 몰라도 models.py 를 통해 DB를 관리할 수 있다는 사실만 알고 계시면 됩니다.

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'

7-1. class Account(models.Model):

커스터마이징의 경우를 빼고는 (장고의 기능을 가져와쓰지 않고 만드는 과정을 거치는..) 모든 모델 클래스는 models 에 들어 있는 클래스 Model 을 상속받는다. Model 을 상속받아 다 써먹겠다는 의미.

7-2. FieldType

필드는 Field 클래스의 인스턴스이다. Django 는 필드를 통해 다음과 같은 사항을 정의한다.

테이블의 열 저장할 데이터의 유형 (문자열, 정수 등)
폼 필드를 렌더링할 때 사용할 기본 HTML 위젯
관리자 페이지와 자동생성 되는 폼들에 적용될 최소 요구사항
Django 는 기본적으로 아래와 같은 필드 타입들을 제공한다.

관계 정의 필드들도 있지만 그건 나중에 정리해보기로 한다.

7-3. DateTimeField (auto_now_add=True, auto_now_add)

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

7-4. class Meta:

class Meta:
        db_table = 'accounts'

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

# 바른 예시

# sqlite3 db.sqlite3

sqlite> .tables
accounts             django_migrations
django_content_type  django_session

8. makemigrations 와 migrate

  • makemigrations : models.py에서 적용한 변경사항이나 추가된 혹은 삭제된 사항들을 감지하여 초안 마이그레이션 파일로 생성
  • migrate : 적용되지 않은 migrations들을 (설정값들을) 적용시키는 역할
python manage.py makemigrations

python manage.py migrate account 0001
# 같은 동일 경로에서 진행하므로 migrate 만 진행해도 된다 한다는데 이부분은 자세히 알아봐야 겠다.

즉, makimigrations는 장고에서 제공하는 모델의 변경사항들을 감지하고 기록하는 역할을 하며 migrate는 그러한 기록된 파일들과 설정값들을 읽어서 그 변경사항을 db에 저장하는 역할을 한다.

  1. 모델 작성
  2. makemigrations
  3. migrate

여기에 showmigrations 와 sqlmigrate 로 중간중간 위 순서에 섞어서 사용하며 확인한다는 것 같다.

9. 쿼리셋 QuerySet 이란?

QuerySet

간단하게 말하면 Database에서 전달받은 객체들의 모음(list)이다. DB(SQL)에서는 row에 해당한다.
Python으로 작성한 코드가 SQL로 mapping되어 QuerySet이라는 자료 형태로 값이 넘어온다.

객체별 접근방식

DB에는 column과 row에 데이터가 저장된다. Django에서 column에 해당하는 부분은 모델의 각 클래스안에 지정해준 속성들이며, row에 해당하는 부분은 각 속성에 부여되어 있는 값들이다. 즉 dictionary 가 저장되는 것이다.

따라서, QuerySet안에 있는 객체에 접근할 때에는 value에 접근하는지, dictionary의 요소에 접근하는지 등에 따라서 접근 방식이 다르다.

<클래스이름>.objects.values() :

.values()로 dictionary의 key와 value에 접근이 가능하다.
QuerySet()은 리스트이고, 객체는 dictionary 이므로 
<variable name>[index]['key'] 의 형식으로 value값에 접근이 가능하다

<클래스이름>.objects.get(id=1) :
get()은 dictionary의 요소 하나를 반환한다.
해당 조건의 요소가 존재하지 않을때는 DoesNotExist, 여러개 존재할때는 
MultipleObjectsReturned 에러가 발생한다.
하나의 객체이기에 반환되기 때문에, dot notation으로 접근이 가능하다. 
<variable name>.name

<클래스이름>.objects.filter() or <클래스이름>.objects.all()

filter()는 주어진 parameter에 따라 query의 결과를 필터하며, 결과는 리스트로 반환한다.
all()은 QuerySet안의 모든 객체를 리스트 형태로 return한다.
따라서, <variable name>[index]로 접근이 가능하다.

>>> b = Account.objects.filter(name__startswith="K")
>>> <QuerySet [<Account: Account object (1)>, <Account: Account object (2)>]>
>>> b[0]
<Account: Account object (1)>

>>> c = Account.objects.all()
<QuerySet [<Account: Account object (8)>, <Account: Account object (9)>, <Account: Account object (10)>]>
>>> c[0]
<Account: Account object (8)>

10. View 만들기 - SignUpView (회원가입)

import json

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

from .models import Account

뷰는 작성된 모델을 바탕으로, 들어오는 데이터를 어떻게 처리할지에 대한 논리를 맡고 있다.

JSON은 데이터 타입으로 JavaScript Object Notation의 줄임말로,
인터넷에서 자료를 주고 받을때 널리 쓰이는 데이터 타입으로 자리 잡은 형식이다.

django 에서 View 클래스는 직접 생성하지 않고 장고 프레임워크에 내장된 클래스를 상속받아 쓴다.
http를 통해 받은 요청 정보(json)를 파이썬이 읽을 수 있는 형태로 변환해줄 수 있는 모듈인 json 을 임포트 한다.

json 형태로 응답(response) 해줄 수 있는 JsonResponse 모듈을 불러온다. 그리고 뷰에서 처리할 정보가 쌓이는 기준이 되는 Account class를 모델에서 불러온다.

JsonResponse는 서버의 요청에 대한 응답을 Json으로 응답하기 위해 사용한다.

10-1. 회원가입을 담당할 SignUp 클래스 만들기

보통 get, post 요청이 들어오는데, get은 순전히 데이터만을 요청할 때, post는 어떠한 데이터를 입력, 수정을 할 때 라고 생각하면 된다. 예를 들어, 로그인도 로그인 완료라는 데이터를 요청해서 get일 것 같지만, 일단 데이터를 입력해서 데이터가 맞는지 확인 받기 때문에 post 메서드를 사용한다.

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)

10-2. 로그인을 담당할 SignIn 클래스 만들기

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)

11. url 경로 작성

westagram 의 경로를 최초 경로를 관리하는 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 코드를 작성해보자.

from django.urls import path
from .views import SignInView, SignUpView

urlpatterns = [
    path('/signup', SignUpView.as_view()),
    path('/signin', SignInView.as_view()),
]

들어온 요청 데이터를 처리하는 곳이 views 이다. 그리고 그 안에 만들어뒀던 SignUpView, SignInView를 통해서 데이터를 처리한다.

view 클래스는 내장 함수를 반환하는 as_view() 를 메서드로 제공한다. (모든 클래스 기반 뷰는 이 클래스를 직간접적으로 상속받는다.)

as_view() 는 클래스의 인스턴스를 생성하고, 이 인스턴스의 dispatch() 메서드를 호출한다. dispatch() 메서드에서는 view가 받은 요청을 검사해서 HTTP의 메소드(GET, POST)를 알아낸다. 인스턴스 내에 해당 이름을 갖는 메서드로 요청을 중계한다. 해당 메서드가 정의되지 않으면, HttpResponseNotAllowd 예외를 발생시킨다.

localhost:8000/account/signup email password 로 요청이 들어오면 as_view()가 SignUpView, SignInView 로 인스턴스를 생성하고, dispatch() 메서드를 통해서 들어온 요청 메서드가 뷰 안에서 정의한 POST인지 확인한다. 이 경우 POST가 있으면 정상적으로 SignUpView의 논리에 따라 응답이 처리되서 반환된다.

profile
.

0개의 댓글