TIL54. Django : Westargram 회원가입&로그인 API

ID짱재·2021년 10월 25일
0

Django

목록 보기
37/43
post-thumbnail

📌 이 포스팅에서는 Django를 이용하여 Westargram API(회원가입, 로그인)를 만드는 과정을 정리하였습니다.



🌈 Westargram 회원가입 & 로그인 API

🔥 프로젝트 초기 세팅

🔥 회원 정보를 위한 Model

🔥 회원가입 API 구현

🔥 로그인 API 구현



1. 프로젝트 초기 세팅

🤔 시작은 언제나 clone 🚀

✔️ conda를 통해 가상환경을 activate 시킨 후, 작업할 github repository를 로컬로 내려 받는다.

✔️ .을 찍으면 현재 디렉토리에 바로 repository의 내용들을 내려 받기 때문에 내려받은 내용을 담을 디렉토리를 별도로 생성해주었다면, 그 경로 내에서 안에서 점(.)을 추가해서 내려 받아도 된다.

$ git clone [github repository 주소] .

✔️ repository 이름으로 디렉토리를 생성 후 그 안에 내용물이 담겨지길 원한다면 점(.)을 찍지 않아야 한다.

$ git clone [github repository 주소]

🤔 Django 초기 세팅 시작

✔️ API 구축에 필요한 몇몇 프로그램을 다운 받는다.

$ pip install django 👈 Django 설치

$ pip install mysqlclient 👈 mysql 접속을 위한 client 설치

$ pip install django-cors-headers 👈 CORS 설치

✔️ Mysql에 접속하여 DB를 생성하고 Django Project를 생성한다.

mysql > create database [DB이름] character set utf8mb4 collate utf8mb4_general_ci;

✔️ 프로젝트 디렉토리를 이미 생성했다면 점(.)을 붙여 현재 경로에 바로 파일 및 디렉토리가 위치하도록 설치한다.

$ django-admin startproject [프로젝트명] .

✔️ .gitignore 파일을 생성해서 추적이 불필요한 파일 및 디렉토리를 등록한다.

✔️ settgins.py 설정을 수행하고, my_settgins.py에 SECRET_KEY와 DATABASES 정보를 은폐시킨다.

🤔 협업을 위한 requirements.txt 생성

✔️ 다른 사람과 함게 작업을 한다면, 이 프로젝트 구동을 위해 다운 받은 외부 라이브러리, 프로그램 등에 대해 버전과 함께 기록해두어야 한다.
✔️ 뿐만아니라, 추후 AWS에 배포할 때도 이 과정을 통해 Cloud PC에 필요한 파일을 설치할 수 잇다.

$ pip freeze > requirements.txt

✔️ 이렇게하면, 현재 가상환경에 설치된 모든 프로그램이 requirements.txt에 담기게된다. 실제로 설치한 것은 3개인데 여러 내용이 존재하는 것은 자동으로 설치되는 프로그램도 함께 목록에 나타나기 때문이다.

✔️ 별도로 설치할 필요가 없는 내용들은 requirements.txt에서 지워주도록 하자. 현재 세팅 상태 기준으로는 아래 내용들만 적혀 있어도 충분하다.

# requirements.txt
Django==3.2.8
django-cors-headers==3.10.0
mysqlclient==2.0.3


2. 회원 정보를 위한 Model

🤔 가입에 필요한 정보

✔️ 회원정보를 위한 앱을 생성한 뒤, models.py에 작업을 진행한다. 앱이름은 보통 복수로 생성하고 앱등록을 해준다.

$ python manage.py startapp users

INSTALLED_APPS = [
    # 'django.contrib.admin',
    # 'django.contrib.auth',
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "corsheaders",
    "users", # 👈 app 등록
]

✔️ Accounts 테이블은 사용자명, 이메일, 비밀번호, 핸드폰번호, 생년월일, 생성일, 수정일로 필드를 구성했다.

✔️ 추후 이메일로 로그인을 할 계획이기 때문에 이메일은 DB에 중복으로 존재하면 안된다. 이럴 때에는 unique 옵션 사용한다. default가 False기 때문에 True로 변경해준다.

✔️ 또한 회원정보의 생성과 변경에 대한 자동 기록을 위해 DateTimeField에 옵션을 이용한다.

from django.db import models
class Accounts(models.Model):
    username = models.CharField(max_length=50)
    email = models.EmailField(unique=<True)  # 👈 unique는 default가 False다.
    password = models.CharField(max_length=100)  # 👈 암호화하기 대비해 100자로 넉넉히 주었다.
    phone_number = models.CharField(max_length=30)
    date_of_birth = models.DateField()
    created_at = models.DateTimeField(auto_now_add=True) # 👈 생성될 때만 자동 저장
    modified_at = models.DateTimeField(auto_now=True) # 👈 업데이트할 때마다 자동 변경
    class Meta:
        db_table = "accounts"

✔️ 마이그레이션을 진행하고 runserver를 통해 문제가 없는지 확인한다.

$ python manage.py makemigrations && python manage.py migrate 👈 한번에 명령도 가능하다.



3. 회원가입 API 구현

🤔 회원가입 로직

✔️ 이메일이나 패스워드가 전달되지 않을 경우, {"message": "KEY_ERROR"}, status=400 반환

✔️ 이메일에는 @와 .이 필수로 포함되어야 하고, 해당 조건이 만족되지 않은 경우 {"message": "INVALID_EMAIL"}, status=400) 반환

✔️ 비밀번호는 8자리 이상, 문자, 숫자, 특수문자의 복합이어야 합니다. 해당 조건이 만족되지 않은 경우, {"message": "PASSWORD_MISMATCH"}, status=400) 반환

✔️ 회원가입시 서로 다른 사람이 같은 이메일을 사용하지 않으므로 DB에 등록된 데이터와 중복되는 경우, {"message": "USER_ALREADY_EXISTS"}, status=409) 반환

🤔 데이터 validate를 위한 정규표현식

✔️ views.py에 복잡도를 줄이기 위해 정규표현식 처리 로직을 "validate.py"로 분리하였다.

✔️ 정규표현식은 https://regexr.com/ 에서 test를 해볼 수 있다.

✔️ email과 password에 대한 검증 함수를 만들고, 이 함수는 Boolean값을 반환토록 하였다.

# validate.py
import re # 👈 python에서 정규표현식을 사용할 때, re 라이브러리를 이용
def validate_email(email):
    email_pattern = re.compile("^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$")
    return bool(email_pattern.match(email))
def validate_password(password):
    password_pattern = re.compile("^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@!%*#?&]{8,}$")
    return bool(password_pattern.match(password))

🤔 회원가입 로직

✔️ 회원가입 로직은 간단하다. 값이 전달되지 않았을 때는 KEY_ERROR을 발생시키고, 값이 들어왔다면, email과 password를 정규표현식에 돌려서 처리한다.

✔️ 정규표현식에서 True를 반환하면 문제가 없지만, False를 반환하면 INVALID 에러를 반환한다.

✔️ 회원가입 시, 비밀번호 일치 여부가 판단하다고 생각되서 re_password값으로 전달받아 password와 일치하는지 검증했다.

✔️ 마지막으로 이미 존재하는 email인지 DB에서 해당 객체의 존재여부를 exists를 사용하여 확인한다. 이미 존재한다면 DB단에서 충돌이 발생한 것이기 때문에 409 상태코드로 에러를 알린다.

✔️ 이 과정에서 모두 통과되었다면 Accounts 테이블에 정보를 객체를 저장한다. 이 때는 생성이 성공되었단 의미에서 201 상태 코드를 반환한다.

import json
from django.http import JsonResponse
from django.views import View
from .validate import validate_email, validate_password
from .models import Accounts
# 🚀 SignUp controller
class SignUpView(View):
    def post(self, request):
        data = json.loads(request.body)
        try:
            username = data["username"]
            email = data["email"]
            password = data["password"]
            re_password = data["re_password"]
            phone_number = data["phone_number"]
            date_of_birth = data["date_of_birth"]
            if not validate_email(email):
                return JsonResponse({"message": "INVALID_EMAIL"}, status=400)
            if not validate_password(password):
                return JsonResponse({"message": "INVALID_PASSWORD"}, status=400)
            if password != re_password:
                return JsonResponse({"message": "PASSWORD_MISMATCH"}, status=400)
            if Accounts.objects.filter(email=email).exists():
                return JsonResponse({"message": "USER_ALREADY_EXISTS"}, status=409)
            Accounts.objects.create(
                username=username,
                email=email,
                password=password,
                phone_number=phone_number,
                date_of_birth=date_of_birth,
            )
            return JsonResponse({"message": "SUCCESS"}, status=201)
        except:
            return JsonResponse({"message": "KEY_ERROR"}, status=400)
# 🚀 Log-In controller         
class SignInView(View):
    def post(self, request):            

🤔 url 경로 설정

✔️ 우선 메인 urls.py에서 include통해 users.urls를 참조하도록 조치해두어야 한다.

from django.urls import path, include
urlpatterns = [
    path("", include("users.urls")),
]

✔️ 해당 로직을 작동시키기 위해 경로와 로직을 아래와 같이 매핑한다.

from django.urls import path
from .views import SignUpView, SignInView
urlpatterns = [
    path("signup", SignUpView.as_view()),
    path("signin", SignInView.as_view()),
]

✔️ POST 메시지를 날려보면, 정상적으로 등록이 되는 것을 볼 수 있다. 비밀번호에 역슬래시를 포함한 이유는 zsh에서 특수 문자를 보낼 때 에러가 발생해 찾아밨더니 저렇게 해야하는 것 같다. 실제 전송된 데이터에는 역슬래시가 포함되어 있지 않다.

$ http -v POST 127.0.0.1:8000/signup username="장재원" email="jewon119@gmail.com" password="1q2w3e4r!" re_password="1q2w3e4r!" phone_number="010-1234-5678" date_of_birth="1999-11-11"



4. 로그인 API 구현

🤔 login 로직 구현

✔️ login 로직은 회원가입보다 간편하다. 값이 전달되었다면 DB와 데이터와 대조시키면 끝이다.

✔️ 우선 POST 메시지에 실려온 email값과 password값이 일치하는 객체가 있는지 확인하여 그렇지 않는다면 {"message": "INVALID_USER"}, status=401)을 반환한다.

✔️ 두 개의 검증과정을 통과했다면 성공 메시지 {"message": "SUCCESS"}, status=200)를 반환한다.

import json
from django.http import JsonResponse
from django.views import View
from .validate import validate_email, validate_password
from .models import Accounts
# 🚀 SignUp controller
class SignUpView(View):
    def post(self, request):
        .....
        .....
# 🚀 Log-In controller         
class SignInView(View):
    def post(self, request):
        data = json.loads(request.body)
        try:
            email = data["email"]
            password = data["password"]
            if not Account.objects.filter(email=email, password=password).exists():
                return JsonResponse({"message": "INVALID_USER"}, status=401)
            return JsonResponse({"message": "SUCCESS"}, status=200)
        except KeyError:
            return JsonResponse({"message": "KEY_ERROR"}, status=400)

✔️ POST 메시지를 날려보면, 정상적으로 등록이 되는 것을 볼 수 있다. 비밀번호에 역슬래시를 포함한 이유는 zsh에서 특수 문자를 보낼 때 에러가 발생해 찾아밨더니 저렇게 해야하는 것 같다. 실제 전송된 데이터에는 역슬래시가 포함되어 있지 않다.

$ http -v POST 127.0.0.1:8000/signin email="jewon119@gmail.com" password="1q2w3e4r!"

profile
Keep Going, Keep Coding!

0개의 댓글