데코레이터는 특정 함수가 작동할 때에 함수 로직의 코드 수정 없이, 추가적인 로직을 주입하기 위해 사용한다.
데코레이터에 대한 이해는 아래 링크를 통해서 해보시고,
django에서 내장된 login_required
데코레이터만 살펴 보겠다.
def login_required( function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None ): """ Decorator for views that checks that the user is logged in, redirecting to the log-in page if necessary. """ actual_decorator = user_passes_test( lambda u: u.is_authenticated, login_url=login_url, redirect_field_name=redirect_field_name, ) if function: return actual_decorator(function) return actual_decorator
login_required
함수에는 wrapper
함수도 없고, actual_decorator
를 반환해줄 뿐이기에,actual_decorator
정의부에 대한 것은 user_passes_test
함수를 정확히 살펴봐야 하는 것이다.
따라서 핵심 함수는 user_passes_test
함수이므로, 함수 호출 시에 넘어가는 인자에 대해 하나씩 살펴보겠다.
첫번째 인자 lambda u: u.is_authenticated
lambda
또한 설명 범위를 넘어가니, 간단히 해당 함수의 역할에 대해 설명만 하겠다.user
가 인증되어있는지를 여부를 리턴해주는 익명함수이다.
실제로 아래 user_passes_test
함수의 test_func
인자로 lambda
함수가 넘어가며, 해당 함수를 실행하고 test_pass 여부에 따라 페이지를 보여줄지, login 페이지로 redirect할지 결정하는 코드가 L35 , L47에 위치하고 있다.
코드가 두가지로 나뉘는 이유는 비동기여부에 따라 분기 처리 되어있기 때문이다.
두번째 인자 login_url
은 L13 에서 resolve_url
함수를 통해서 resolved_login_url
을 가져오기 위해 전달하는 것이다.
마지막 인자 redirect_field_name
의 경우 로그인 페이지 이동 시에 로그인 성공 후 어느 페이지로 이동할 지 querystring에 선언하는 것이 일반적인데 해당 필드 이름을 설정하는 인자이다.
만약 설정하지 않으면 아래 L2에서 확인할 수 있듯이, REDIRECT_FIELD_NAME
상수 값인 next
가 들어간다
def user_passes_test( test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME ): """ Decorator for views that checks that the user passes the given test, redirecting to the log-in page if necessary. The test should be a callable that takes the user object and returns True if the user passes. """ def decorator(view_func): def _redirect_to_login(request): path = request.build_absolute_uri() resolved_login_url = resolve_url(login_url or settings.LOGIN_URL) # If the login url is the same scheme and net location then just # use the path as the "next" url. login_scheme, login_netloc = urlparse(resolved_login_url)[:2] current_scheme, current_netloc = urlparse(path)[:2] if (not login_scheme or login_scheme == current_scheme) and ( not login_netloc or login_netloc == current_netloc ): path = request.get_full_path() from django.contrib.auth.views import redirect_to_login return redirect_to_login(path, resolved_login_url, redirect_field_name) if asyncio.iscoroutinefunction(view_func): async def _view_wrapper(request, *args, **kwargs): auser = await request.auser() if asyncio.iscoroutinefunction(test_func): test_pass = await test_func(auser) else: test_pass = await sync_to_async(test_func)(auser) if test_pass: return await view_func(request, *args, **kwargs) return _redirect_to_login(request) else: def _view_wrapper(request, *args, **kwargs): if asyncio.iscoroutinefunction(test_func): test_pass = async_to_sync(test_func)(request.user) else: test_pass = test_func(request.user) if test_pass: return view_func(request, *args, **kwargs) return _redirect_to_login(request) # Attributes used by LoginRequiredMiddleware. _view_wrapper.login_url = login_url _view_wrapper.redirect_field_name = redirect_field_name return wraps(view_func)(_view_wrapper) return decorator
데코레이터를 아무런 생각없이 사용을 꽤 오랬동안 사용해왔었는데,
이렇게 디테일하게 코드를 이해하는 시간이 없으면 아무런 생각없이 코드를 작성하게 되기 쉬운 것 같다.
좋은 코드를 분석 함으로서, 더 나은 개발자로 거듭날 수 있다고 생각한다.
'IT > Django' 카테고리의 다른 글
vscode에서 dockerized 된 django 디버깅 방법 (1) | 2024.01.09 |
---|---|
Django core serialize with FK. (0) | 2024.01.08 |
Django에서 Celery가 필요한 이유? (0) | 2024.01.04 |
django request.user가 anonymous로만 나오는 이유? (0) | 2023.06.28 |
제대로된 해결 방법 - received a naive datetime while time zone support is active. (0) | 2023.06.22 |