python decorator 작성 순서
데코레이터 작성을 순차적으로 해보자.
📜 제목으로 보기
- 데코레이터를 작성하는 순서
- 기본 데코레이터 만들기
- 01 기능을 입힐 base함수부터 작성한다. 이왕이면 print가 아니라 return하는 결과물이 있는 함수를 작성하여, 반환값에 대한 장식도 가능하게 한다.
- 02 base함수를 (호출부없는)함수객체를 인자로 받는 클로져 함수를 사용-생성한다. 여기까지는 지역상태(메서드내부 인자 등)를 기억하는 함수객체를 return한 뒤, 외부에서 실제 인자를 받는 클로져가 된다.
- 03 클로져 상태에서, base함수를 호출한 결과값을 장식해야한다면? 반환은 값이 되므로 함수객체를 반환하지 않게 되어, 클로져도 아니고 데코레이터도 아니게 된다. 그냥 값이 반환된 상태라서 외부에서 ()호출없이 끝난다.
- 04 base결과물 장식 후, 클로져 상태(함수객체 반환)를 유지하기 위한 inner메서드 wrapper로 감싼 뒤, return wrapper함수객체 -> 외부에서 호출될 함수로서, 함부러 파라미터를 내부context라고 파라미터로 만들면 안된다. -> base함수의 인자에 맞춰야한다.
- 05 완성된 데코는, base함수에 @달아서 사용해주기
- 06 다중데코레이터는, 외부에서 한번더 감싸서 만들고, 달 때는 아래->위 순서대로 단다.
- 함수객체 name에 base함수가 찍히도록 디버깅되는 데코레이터로 바꾸기 by wrapper메서드에 @functools.wraps(func) 데코 달아주기
- base함수호출시 필요한 특정 인자를 외부에서 조달하려면, 외부에 return되서 ()가 붙는 함수객체 wrapper에 *args, **kwargs로 파라미터를 정의하여 어떤 인자든 들어오게 한다.
- 기본 데코레이터 만들기
- 역순으로 jwt 인증 decorator 작성
- 01 Route마다 매번 [token 인증 -> 실패시 예외]의 검증하는 로직을 route에 반복 기술하는 대신 데코레이터로 만들기
- 02 token인증(실패시 예외)을 데코레이터 만드는 방법(바깥에서부터)
- 001 decorator는 ()호출 전의 base_func객체를 인자로 받아서 내부에서 호출하고, return은 func객체를 호출하고 실제 deco하는 inner method wrapper를 호출하지 않은체 반환하므로 [input:func(base_func) -> output func(wrapper)]이다.
- 002 inner method wrapper메서드는 [결과값을 deco당하거나 or 호출전후로 작업을 하고 싶은 base_func()]의 decorating 과정에서 -> 외부 인자가요구하는 순간, wrapper method도 인자를 *args, **kwargs로 받는다. inner method인 wrapper는 decorator 맨 마지막에 호출하지 않은 체 반환되는 closure다
- 003 GET route에서 token인증을 위한 decode + 예외처리 했던 로직을 inner method warpper(여기선 decorated) 내부로 가져와서, [base_func (여기서는 route함수) 호출전 (1)TOKEN인증(==실패시 예외) + (2) TOKEN REFRESH 작업]을 항상 진행하게 해준다.
- 004 필요한 것들을 import하고, 수정해나간다
- 005 base_func인 function(route함수)는 wrapper(decorated)에서 [token인증 및 예외처리] 끝내고, 자신만 호출되면 되나? 인증안되면 예외발생하고, 인증되면 route 자신의 역할만 하면 끝??
- 006 route함수가 decorator로 token인증을 거치는 경우, 인증성공시의 refresh된 token을 route함수 인자로 받아야하며, decorator작성시, base_func(route_func)은 *args**kargs 앞에 가장 첫 인자로 next_token을 추가해야한다
- 007 inner method wrapper메서드에 @functools.wraps( base_func )을 달아주면, 디버깅시 함수name이 같이 찍힌다
- 008 auth_jwt init에 올리기
- 03 token 인증필요 route(GET)에서 jwt 인증 decorator사용하기
데코레이터를 작성하는 순서
기본 데코레이터 만들기
01 기능을 입힐 base함수부터 작성한다. 이왕이면 print가 아니라 return하는 결과물이 있는 함수를 작성하여, 반환값에 대한 장식도 가능하게 한다.
02 base함수를 (호출부없는)함수객체를 인자로 받는 클로져 함수를 사용-생성한다. 여기까지는 지역상태(메서드내부 인자 등)를 기억하는 함수객체를 return한 뒤, 외부에서 실제 인자를 받는 클로져가 된다.
- 데코레이터는 장식할 함수를 일급객체로서 간주하여, ()호출부 없이 함수객체만 인자로 받으며 -> 장식 후 -> 다시 함수객체로 반환하는 클로져 형태를 띈다.
- 대문자로 변경하는 장식을 할 것이므로, 데코레이터 메서드 명을 uppercase로 만든다.
03 클로져 상태에서, base함수를 호출한 결과값을 장식해야한다면? 반환은 값이 되므로 함수객체를 반환하지 않게 되어, 클로져도 아니고 데코레이터도 아니게 된다. 그냥 값이 반환된 상태라서 외부에서 ()호출없이 끝난다.
04 base결과물 장식 후, 클로져 상태(함수객체 반환)를 유지하기 위한 inner메서드 wrapper로 감싼 뒤, return wrapper함수객체 -> 외부에서 호출될 함수로서, 함부러 파라미터를 내부context라고 파라미터로 만들면 안된다. -> base함수의 인자에 맞춰야한다.
-
wrapper는
inner메서드
이므로, context객체들을 그대로 사용하여 paramter로 뺄 필요가 없다.(외부호출시 사용될 메서드 객체기 때문에 파라미터를 함부러줘선 안된다.)
05 완성된 데코는, base함수에 @달아서 사용해주기
06 다중데코레이터는, 외부에서 한번더 감싸서 만들고, 달 때는 아래->위 순서대로 단다.
함수객체 name에 base함수가 찍히도록 디버깅되는 데코레이터로 바꾸기 by wrapper메서드에 @functools.wraps(func) 데코 달아주기
- 데코레이터를 입힌 base함수를,
함수객체.__name__
을 찍어보면, 중간의 wrapper로 나온다. -
데코레이터의 wrapper함수에
@functools.wraps(func)
데코레이터를 달아줘야, base함수가 찍힌다. -
만약, 2개의 데코레이터를 중첩적용하고 있고, 첫번째 데코만 처리해줬다면? -> wrapper로 나온다.
-
2번째 데코레이터도 달아줘야한다.
-
*args, **kwargs
로 파라미터를 정의하여 어떤 인자든 들어오게 한다.
base함수호출시 필요한 특정 인자를 외부에서 조달하려면, 외부에 return되서 ()가 붙는 함수객체 wrapper에 -
데코레이터 함수 자체에 인자가 아니다(func만 받는다.)
-
마지막엔
return wrapper
로 wrapper의 함수객체가 반환되므로 wrapper에*args
나\**kw args
를 받도록 하면,어떤 인자든 다 들어올 수 있다
- 들어온 인자를 그대로 사용하려면
*args
를 base에서 그대로 사용한다. - 만약
콤마로 연결된 인자들
이 들어왔는데,반복문 등에 사용
된다면*를 빼서 패킹
된 상태로 돌린다.
- 들어온 인자를 그대로 사용하려면
-
현재 base함수를 파라미터를 통해 문자열 인자 받도록 변경한다.
- 그래도 바로 사용못한다. 왜냐면, 데코레이터에 감싸진 이상
wrapper
가 반환되기 때문에 - wrapper는 인자를 안받는데, 왜 받고 있냐 물어본다.
- 그래도 바로 사용못한다. 왜냐면, 데코레이터에 감싸진 이상
-
외부에서 호출되는 wrapper함수에
base함수호출시 필요한 인자
를 주되*args, *\*kwargs
로 주어 어떤 인자든 받을 수 있게 한다.-
args
: 튜플로 패킹된다. -
*args
: 받은 인자 그대로를 언패킹해서 사용한다.- 파라미터속
*args
: 콤마 = 튜플로 넘어온 인자를 언패킹한 상태로 가지고 있다. 언제든지*
를 떼서 컬렉션형태로 사용할 수 있다.
- 파라미터속
-
역순으로 jwt 인증 decorator 작성
01 Route마다 매번 [token 인증 -> 실패시 예외]의 검증하는 로직을 route에 반복 기술하는 대신 데코레이터로 만들기
-
기본 데코레이터 만들기 01 기능을 입힐 base함수부터 작성한다. 이왕이면 print가 아니라 return하는 결과물이 있는 함수를 작성하여, 반환값에 대한 장식도 가능하게 한다. 02 base함수를 (호출부없는)함수객체를 인자로 받는 클로져 함수를 사용-생성한다. 여기까지는 지역상태(메서드내부 인자 등)를 기억하는 함수객체를 return한 뒤, 외부에서 실제 인자를 받는 클로져가 된다. 03 클로져 상태에서, base함수를 호출한 결과값을 장식해야한다면? 반환은 값이 되므로 함수객체를 반환하지 않게 되어, 클로져도 아니고 데코레이터도 아니게 된다. 그냥 값이 반환된 상태라서 외부에서 ()호출없이 끝난다. 04 base결과물 장식 후, 클로져 상태(함수객체 반환)를 유지하기 위한 inner메서드 wrapper로 감싼 뒤, return wrapper함수객체 -> 외부에서 호출될 함수로서, 함부러 파라미터를 내부context라고 파라미터로 만들면 안된다. -> base함수의 인자에 맞춰야한다. 05 완성된 데코는, base함수에 @달아서 사용해주기 06 다중데코레이터는, 외부에서 한번더 감싸서 만들고, 달 때는 아래->위 순서대로 단다. 함수객체 name에 base함수가 찍히도록 디버깅되는 데코레이터로 바꾸기 by wrapper메서드에 @functools.wraps(func) 데코 달아주기 base함수호출시 필요한 특정 인자를 외부에서 조달하려면, 외부에 return되서 ()가 붙는 함수객체 wrapper에 *args, **kwargs로 파라미터를 정의하여 어떤 인자든 들어오게 한다.
001 src > auth_jwt > token_verifier.py 생성
02 token인증(실패시 예외)을 데코레이터 만드는 방법(바깥에서부터)
- route의 메서드가, 데코레이터의 decorator -> warpper -> base_func 중 basefunc을 차지하게 됨.
001 decorator는 ()호출 전의 base_func객체를 인자로 받아서 내부에서 호출하고, return은 func객체를 호출하고 실제 deco하는 inner method wrapper를 호출하지 않은체 반환하므로 [input:func(base_func) -> output func(wrapper)]이다.
-
참고
def token_verify(function: callable) -> callable:
*args, **kwargs
로 받는다. inner method인 wrapper는 decorator 맨 마지막에 호출하지 않은 체 반환되는 closure다
002 inner method wrapper메서드는 [결과값을 deco당하거나 or 호출전후로 작업을 하고 싶은 base_func()]의 decorating 과정에서 -> 외부 인자가요구하는 순간, wrapper method도 인자를 -
참고
def token_verify(function: callable) -> callable:
def decorated(*args, **kwargs):
## pre decoration
# result = function()
## post decoration
# post_decorated_result = result + @
# return decorated_result
return decorated
003 GET route에서 token인증을 위한 decode + 예외처리 했던 로직을 inner method warpper(여기선 decorated) 내부로 가져와서, [base_func (여기서는 route함수) 호출전 (1)TOKEN인증(==실패시 예외) + (2) TOKEN REFRESH 작업]을 항상 진행하게 해준다.
-
get route
-
이동
004 필요한 것들을 import하고, 수정해나간다
- auth_jwt > token_verifier.py에
- flaks관련 모듈이 import된다
- jwt가 import된다
- 싱글톤 token_creator도 import된다
- 인증 및 예외발생 route에는 없던 refresh를 해주기 위해
import jwt
from flask import request, jsonify
def token_verify(function: callable) -> callable:
def decorated(*args, **kwargs):
raw_token = request.headers.get("Authorization")
uid = request.headers.get("uid")
# if not raw_token:
if not raw_token or not uid:
return jsonify({
"error": "Bad Request"
}), 400
try:
token = raw_token.split()[1]
token_information = jwt.decode(token, key='1234', algorithms='HS256')
token_uid = token_information["uid"]
except jwt.InvalidSignatureError:
return jsonify({
"error": "Invalid Token"
}), 498
except jwt.ExpiredSignatureError:
return jsonify({
"error": "Token expried"
}), 401
except KeyError as e:
return jsonify({
"error": "Invalid Token2"
}), 401
if int(token_uid) != int(uid):
return jsonify({
"error": "User not permission"
}), 400
return decorated
005 base_func인 function(route함수)는 wrapper(decorated)에서 [token인증 및 예외처리] 끝내고, 자신만 호출되면 되나? 인증안되면 예외발생하고, 인증되면 route 자신의 역할만 하면 끝??
-
시나리오
-
route함수가, 인증 및 예외처리만 할 경우 -> deco내wrapper내 token처리 끝나고 base_func()호출시 특별한 인자 없이
base_func(\*args, \**kwargs)
로 return하고 끝낸다 -
route함수가, 인증 및 예외처리하고
REFRESH한 token이 새로 발급
되어서,이것을 인자로 받아갈 때
-
006 route함수가 decorator로 token인증을 거치는 경우, 인증성공시의 refresh된 token을 route함수 인자로 받아야하며, decorator작성시, base_func(route_func)은 *args**kargs 앞에 가장 첫 인자로 next_token을 추가해야한다
import jwt
from flask import request, jsonify
from .token_handler import token_creator
def token_verify(function: callable) -> callable:
def decorated(*args, **kwargs):
raw_token = request.headers.get("Authorization")
uid = request.headers.get("uid")
# if not raw_token:
if not raw_token or not uid:
return jsonify({
"error": "Bad Request"
}), 400
try:
token = raw_token.split()[1]
token_information = jwt.decode(token, key='1234', algorithms='HS256')
token_uid = token_information["uid"]
except jwt.InvalidSignatureError:
return jsonify({
"error": "Invalid Token"
}), 498
except jwt.ExpiredSignatureError:
return jsonify({
"error": "Token expried"
}), 401
except KeyError as e:
return jsonify({
"error": "Invalid Token2"
}), 401
if int(token_uid) != int(uid):
return jsonify({
"error": "User not permission"
}), 400
next_token = token_creator.refresh(token)
# route function
# return function(*args, **kwargs)
return function(next_token, *args, **kwargs)
return decorated
007 inner method wrapper메서드에 @functools.wraps( base_func )을 달아주면, 디버깅시 함수name이 같이 찍힌다
from functools import wraps
#...
def token_verify(function: callable) -> callable:
@wraps(function)
def decorated(*args, **kwargs):
008 auth_jwt init에 올리기
from .token_handler import token_creator
from .token_verifier import token_verify
03 token 인증필요 route(GET)에서 jwt 인증 decorator사용하기
001 jwt 인증 데코레이터는 base_func이 인자로 next_token을 받는 것을 가정했으니 -> route함수는 token을 인자로 받아야한다 -> return에도 받은 next_token을 반환해보자.
- api_route.py
#...
from src.auth_jwt import token_creator, token_verifier
#...
@api_routes_bp.route("/secret", methods=["GET"])
@token_verifier
def secret_route(token):
return jsonify({
'data': token
}), 200