개발기록장

[Django] 인스타그램 클론 코딩(2) - 로그인 기능 구현 본문

TIL/Django

[Django] 인스타그램 클론 코딩(2) - 로그인 기능 구현

yangahh 2021. 2. 2. 17:20

 

[이전 글]

[Django] 인스타그램 클론 코딩(1) - 회원가입 기능 구현

 

[Django] 인스타그램 클론 코딩(1) - 회원가입 기능 구현

[다음 글] devvvyang.tistory.com/41 [Django] 인스타그램 클론 코딩(2) - 로그인 기능 구현 인스타그램은 전화번호, 사용자 이름, 이메일 중 하나와 패스워드로 로그인을 할 수 있다. 이를 views.py에 LoginView..

devvvyang.tistory.com

[다음 글]

[Django] 인스타그램 클론 코딩(4) - Authorizarion Decorator 만들고 활용하기

 

[Django] 인스타그램 클론 코딩(3) - Authorization Decorator 만들고 활용

[이전 글] devvvyang.tistory.com/41 [Django] 인스타그램 클론 코딩(2) - 로그인 기능 구현 인스타그램은 전화번호, 사용자 이름, 이메일 중 하나와 패스워드로 로그인을 할 수 있다. 이를 views.py에 LoginView..

devvvyang.tistory.com

 

 

 

인스타그램은 전화번호, 사용자 이름, 이메일 중 하나와 패스워드로 로그인을 할 수 있다. 

이를 views.py에 LoginView에 작성하였다.

views.py

- LoginView 클래스에서 로그인 기능 처리

import jwt
import json
import bcrypt

from django.views           import View
from django.http            import JsonResponse
from django.db.models       import Q

from .models                import User
from my_settings            import SECRET


class LoginView(View):
    def post(self, request):
        data     = json.loads(request.body)
        email    = data.get('email', None)
        name     = data.get('name', None)
        phone    = data.get('phone', None)
        password = data.get('password', None)
        
        # key error check
        if not (password and (email or name or phone)):
            return JsonResponse({'message': 'KEY_ERROR'}, status=400)        
            
        # valid user check  
        if User.objects.filter(Q(email=email) | Q(name=name) | Q(phone=phone)).exists():
            user = User.objects.get(Q(email=email) | Q(name=name) | Q(phone=phone))

            # password check
            if bcrypt.checkpw(password.encode('utf-8'), user.password.encode('utf-8')):
                
                # JSON Web Token
                token = jwt.encode({'user_id': user.id}, SECRET['secret'], algorithm='HS256')
                return JsonResponse({'message': 'SUCCESS', 'access_token': token}, status=200) 
            
            return JsonResponse({'message': 'INVALID_PASSWORD'}, status=401)
        
        return JsonResponse({'message': 'INVALID_USER'}, status=401)


 

  • http request에서 이메일(email), 사용자 이름(name), 전화번호(phone) 중 하나와 비밀번호(password)를 입력받으면 로그인이 되도록 구현하였다.

  • 로그인 시 이메일이나 사용자 이름, 전화번호 중 하나만 일치하면 해당하는 user를 get하고 비밀번호 일치 여부를 체크하도록 구현했다.

  • password는 회원가입 시 암호화 된 상태로 저장되게 구현했기 때문에(bcrypt.haspw() 사용), 비밀번호가 일치하는지 체크하는 과정에서 bcrypt.checkpw() 메소드를 이용하였다.

  • 로그인에 성공을 하면 해당 user의 인가를 위해 JSON Web Token을 발행하고, respone에 전달한다.

 

 

발생할 수 있는 에러와 처리 방법

1. key error

(생략..)

class LoginView(View):
    def post(self, request):
        data     = json.loads(request.body)
        email    = data.get('email', None)    # request body에 'email'이라는 키 값이 없으면 None으로 처리
        name     = data.get('name', None)     # request body에 'name'이라는 키 값이 없으면 None으로 처리
        phone    = data.get('phone', None)    # request body에 'phone'이라는 키 값이 없으면 None으로 처리
        password = data.get('password', None) # request body에 'password'이라는 키 값이 없으면 None으로 처리
        
        # key error check
        if not (password and (email or name or phone)):
            return JsonResponse({'message': 'KEY_ERROR'}, status=400)   

        (생략..)
  • 인스타그램 로그인 시  email, name, phone 중 하나의 값만 입력해야 하기 때문에, 입력하지 않은 값은 default로 None값을 세팅하도록 구현하였다.

  • http request body에서 views.py에 명시된 키값들을 전달받지 못하면 key error가 발생하는데, try-except로 처리할 수도 있지만

  • 여기서는 들어오지 않은 값들이 None인 것을 이용하여 email, name, phone, password 이 4개의 값이 모두 None이 될 때 key error를 return하여 에러 처리를 구현하였다.

 

2. 계정 정보 또는 비밀번호가 일치하지 않을 때

위에서 말했듯, email 또는 name 또는 phone 값 중 하나가 DB에 저장된 user의 정보와 일치하는게 있다면, 해당 user 정보를 가져온다. (Django의 Q 메소드를 사용해서 간편하게 로직을 구현할 수 있었다)

(생략..)

class LoginView(View):
    def post(self, request):
        
        (생략..)
        
        # valid user check  
        if User.objects.filter(Q(email=email) | Q(name=name) | Q(phone=phone)).exists():
            user = User.objects.get(Q(email=email) | Q(name=name) | Q(phone=phone))

            # password check
            if bcrypt.checkpw(password.encode('utf-8'), user.password.encode('utf-8')):
                
                # JSON Web Token
                token = jwt.encode({'user_id': user.id}, SECRET['secret'], algorithm='HS256')
                return JsonResponse({'message': 'SUCCESS', 'access_token': token}, status=200) 
            
            return JsonResponse({'message': 'INVALID_PASSWORD'}, status=401)
        
        return JsonResponse({'message': 'INVALID_USER'}, status=401)
        
        (생략..)

 

  • 기존 user와 일치하는 user가 없다면, 유효하지 않은 계정이므로 401 code로 에러를 반환하도록 구현하였다.

  • 일치하는 user가 있다면 password가 같은지 체크하고 비밀번호가 틀릴 경우, 마찬가지로 401 code와 에러를 반환한다.

  • 사실 로그인 시 계정(여기서는 이메일, 사용자 이름, 전화번호 중 하나)이 틀렸거나 비밀번호가 틀렸을 때, 두 경우를 나눠서 에러 메세지를 반환하지 않는다. 해킹 위험이 증가하기 때문이다. (현업에서는 return도 HttpResponse로 401 코드만 준다)
    하지만 여기서는 front-end와의 원활한 작업을 위해 각 경우에 대해 에러처리를 따로 해줬다.