회고록 | 위스타그램 코스를 마치며

이도운·2022년 1월 21일
0

회고록

목록 보기
1/2
post-thumbnail

위스타그램 코스 통신 전에

위스타그램 코스 통신 전에 나는 Django에서 JSON을 이용해서 통신하는 방법을 배웠다. 부트캠프에 들어오기 전에 Django를 배웠지만 Django에서 지원하는 템플릿 기능을 이용했다.

위스타그램 코스란

위스타그램 코스는 백엔드와 프론트엔드가 최초로 서로 통신하는 코스다. 그동안 백엔드는 백엔드 과제를 수행하고 프론트엔드는 프론트엔드 과제를 수행하고 끝냈다면, 이번 코스는 백엔드와 프론트엔드가 서로 통신하는 것이 최종 목표다. 통신은 JSON으로 이루어진다.

통신을 위한 모델

# models.py

from django.db import models

class User(models.Model):
    name       = models.CharField(max_length=25)
    email      = models.EmailField(unique=True, max_length=50)
    password   = models.CharField(max_length=200)
    phone      = models.CharField(max_length=25)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "users"

class Follow(models.Model):
    follow    = models.ForeignKey(User, models.DO_NOTHING, related_name='follow_list')
    following = models.ForeignKey(User, models.DO_NOTHING, related_name='following_list')

    class Meta:
        db_table = 'follows'

실제로 통신에 쓰인 코드는 상단에 User 클래스다. 아래 Follow 클래스는 추가적인 학습을 위해 작성한 코드다. 모델은 코드로 보면 실제로 간소하다. 다만 모델을 작성하기 전에 모델링 과정에서 시간이 많이 쓰일 뿐이다. 물론 이 코스에서 복잡한 모델 관계성은 없었다.

통신을 위한 뷰

# views.py

import json

from django.core.exceptions import ValidationError
from django.http            import JsonResponse
from django.views           import View

import bcrypt
import jwt

from users.models import User, Follow
from users.validators import validate_email, validate_password, validate_email_duplicate

from my_settings import JWT_SECRET_KEY, ALGORITHM

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

        try:
            name     = data['name']
            email    = data['email']
            password = data['password']
            phone    = data['phone']

            validate_email(email)
            validate_password(password)
            validate_email_duplicate(email)

            hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
            hashed_password = hashed_password.decode('utf-8')

            User.objects.create(
                name     = name,
                email    = email,
                password = hashed_password,
                phone    = phone,
            )

            return JsonResponse({"message": "SUCCESS"}, status=201)
        except KeyError:
            return JsonResponse({"message" : "KEY_ERROR"}, status=400)
        except ValidationError as e:
            return JsonResponse({"message" : e.message}, status=400)

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

        try:
            email    = data['email']
            password = data['password']
            user     = User.objects.get(email=email)
        except KeyError:
            return JsonResponse({"message": "KEY_ERROR"}, status=400)
        except User.DoesNotExist:
            return JsonResponse({"message": "INVALID_USER (email)"}, status=401)

        if not bcrypt.checkpw(password.encode('utf-8'), user.password.encode('utf-8')):
            return JsonResponse({"message": "INVALID_USER (password)"}, status=401)

        access_token = jwt.encode({'id': user.id}, JWT_SECRET_KEY, algorithm=ALGORITHM)

        return JsonResponse({"message": "SUCCESS", "JWT": access_token}, status=200)

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

        try:
            payload   = request.headers.get('Authorization')
            _, token  = payload.split(" ")
            payload   = jwt.decode(token, JWT_SECRET_KEY, ALGORITHM)
            follow    = User.objects.get(id=payload['id'])
            following = User.objects.get(id=data['following_id'])

            follow_obj, follow_bool = Follow.objects.get_or_create(follow=follow, following=following)

            if not follow_bool:
                follow_obj.delete()
                return JsonResponse({"message": "DELETE SUCCESS"}, status=200)
        except jwt.exceptions.DecodeError:
            return JsonResponse({'message': 'INVALID_TOKEN'}, status=400)
        except User.DoesNotExist:
            return JsonResponse({'message': 'INVALID_USER'}, status=400)
        except KeyError:
            return JsonResponse({"message": "KEY_ERROR"}, status=400)

        return JsonResponse({"message": "CREATE SUCCESS"}, status=200)

실제로 쓰인 코드는 LoginView 클래스다. *.object.get() 메소드를 통해서 객체를 얻었고 이에 대한 예외 처리는 except User.DoesNotExist를 통해서 해결했다. 이번 코스는 매우 간단한 과정이라서 get() 메소드 하나로 해결했지만 앞으로 진행되는 프로젝트에 있어서는 get() filter() 이외 메소드를 사용할 수 있도록 노력해야 한다. 코드의 간결성도 그렇고 데이터베이스 최적화를 위해서라도 그렇게 해야 할 것이다.

email passwordbody 통해서 전달 받는다. 전달받은 passwordbcrypt를 이용해서 암호화한다. 이후로 JWTJSWON에 담아서 다시 프론트엔드에 전달하는 과정을 거친다. 해당 JWT는 세션 유지를 위해서 필요하다. 물론 해당 코스에서 사용할 일은 없었다. 다만 앞으로 진행될 프로젝트에 있어서는 필수가 될 것이다.

프론트엔드와 통신

프론트엔드와 통신은 성공적이었다. 처음에 method 관련 이슈로 에러가 떴었는데 내가 로그인을 post가 아니라 get으로 받고 있었다. getbody를 갖지 않는다는 사실을 까먹었던 것일까. 우리 팀은 통신을 통해 200 혹은 400이 뜨는 것을 넘어서 200이 떴을 때 프론트엔드에서 화면 전환이 되는 과정을 테스트 과정에서 구현했다. 구현하는 방법은 크게 어렵지 않았다.

내가 백엔드에서 JSON을 통해서 보내준 message를 확인한 후 message 내용에 따라서 화면을 전환하는 코드를 작성하면 되는 일이었다. 물론 난 백엔드였기 때문에 구현하는 과정은 프론트엔드 동기분께서 하셨다.

성공적인 결과와 프로젝트 진행 전에

우려했던 프론트엔드와 통신 성공은 고무적인 결과다. 이제 본격적인 프로젝트가 진행된다. 단순히 로그인 회원가입을 넘어서 일반적인 커머스 사이트 API를 구현해야 할 것이다. 그리고 해당 API는 모두 프론트엔드와 원활히 통신이 되야 할 것이다. 프로젝트 시작을 기다리면서 구현에 대한 걱정과 어떤 것을 구현하고 성공적인 결과를 보일 수 있을지 설레기도 한다. 멘토님 말씀처럼 여러 개의 API를 구현하기 보다도 보다 구체적인 API를 구현하는 것에 노력해야 할 것이다.

profile
⌨️ 백엔드개발자 (컴퓨터공학과 졸업)

0개의 댓글