
sign-in-decorator
westargram mission(modeling, sign-up, sign-in, authentication, authorization)중 일단락을 짓는 sign-in decoratior 생성하기
해당 서비스를 사용하는 사용자가 회원가입을 거쳐 한 번 로그인을 하게되고, http의 stateless의 성질 때문에 정보가 기록되지 않는데, 해당 사용자에게 서버는 jwt token을 발급하여 한 번 방문했던 사용자로부터 특정 request가 발생할 때, 다시 로그인 하지 않아도 되도록 서버가 정보를 기억하고, 이때 view가 사용하는 것이 jwt sign-in decorator이다.
예를 들어, 회원가입 된 사용자가 게시물을 등록한다고 가정하였을 때, view는 다음과 같다.
class PostView(View):
@sign_in_decorator
def post(self, request):
try:
user = request.user
data = json.loads(request.body)
describe = data["describe"]
image = data["image"]
validation_url(image)
Post.objects.create(
user_id = user.id,
image = image,
describe = describe
)
return JsonResponse({'MESSAGE':'CREATED'}, status = 201)
except ValidationError as e:
return JsonResponse({"MESSAGE":e.message}, status = 400)
except KeyError:
return JsonResponse({"MESSAGE":"KEY_ERROR"}, status = 400)
-------------------------------------------------------------------------------------
#decorator
def sign_in_decorator(func):
def access_token(self, request, *args, **kwargs):
try:
access_token = request.headers.get("Authorization", None) # 1
payload = jwt.decode(access_token, SECRET_KEY, algorithms = ALGORITHM) # 2
user = User.objects.get(id = payload['id']) # 3
request.user = user # 4
return func(self, request, *args, **kwargs)
except jwt.exceptions.DecodeError:
return JsonResponse({'MESSAGE':'INVALID_TOKEN'}, status = 400)
except User.DoesNotExist:
return JsonResponse({'MESSAGE':'INVALID_USER'}, status = 401)
위 code상에서 decorator를 보면,
#1 : 먼저 frontend에서 보낸 http request내 headers의 "Authorization"key의 value인 token을 access_token이라는 변수에 할당
#2 : 위 변수에 저장된 token을 decoding해서 payload라는 변수에 할당
print(payload)
{'id': 72}
#3 : payload라는 변수에 저장된 dictionary 형태의 고객정보에서 'id'값(72)으로 User table에서 해당 user를 객체 형태로 불러와서 user라는 변수에 저장
print(user)
User object (72)
#4 : #3에서 저장한 객체를 request.user라는 변수에 할당하는데, 이 때 token을 decoding하여 얻은 고객 정보(payload)를 request에 담아 view 함수를 호출할 때 보내어 변수로 사용한다.
func(self, request, *args, **kwargs)
이 때, "request"라는 변수 또는 인자가 중복되어 반복적으로 사용 되고 있다.
1) 이 request들은 client가 api server로 보낸 request와 모두 동일한 request인지
2) request.user는 client가 보낸 request와 관련이 있는 변수인지 단지, request하고, 우리 service에 가입된 회원임이 인증된 회원이라는 것을 직관적으로 전달하고자 만든 변수인지
3) 각 request는 또 실제로 어떠한 데이터 타입으로 변수를 보내는지 혼동이 되어 각각 print를 해보았다.
request와 request.body
print(request)
<WSGIRequest: POST '/posting/posting'>
print(request.body) #client가 body에 담아 요청한 내용
b'{"image": "https://gaussian37.github.io/assets/img/python/django/django.png", "describe": "django"}'
request
print(request)
<WSGIRequest: POST '/posting/posting'>
user)하고 다시 변수에 할당한 request.user print(request.user)
User object (72)
결과적으로 decorator에서 user의 정보를 request.user에 한 번 더 할당하여 func(self, request, *args, **kwargs)에 담아 보내는데 view에서는 이렇게 전달받은 request에서
1)request.body로 body에 담긴 client의 실제 request를 꺼내서 실행하고,
2)request.user로 실제로 request한 user의 id를 꺼내어 명령을 실행한다.
request.user=user 로 변수에 할당하는 순간, python의 문법으로 request['user'] = User object (72)가 되고(python의 dictionary를 만들어주는 문법) request dictionary에 'user': <User: User object (72)> 의 형태로 새로운 dictionary가 생성되어 view 함수로 전달된다.
print(request.__dict__)
{'request' : {asdfwefsdjkfhweiufhjksldfjoiwejf...} .... 'headers' :{........}, 'user' : <User: User object (72)>}
#How?:
request['user'] = User object (72) #request.user = user
{'user' : User object (72)}


왜 request.__dict__에 body는 보이지 않는지 도통 알수가 없어서 request.__dir__()을 출력해보면,

body는 request의 dic에서는 숨겨져서 보이지 않지만, dir에서는 확인이 가능한 것을 볼 수 있다!!! (.....왜지..... 알고싶다요.....) dir이 dic의 상위 개념이라고 봤을 때, body는 dic에서는 제외된다는 것은 알겠다. 근데 왜? 더 중요해서? 덜 중요해서? 후...