# TIL 웹개발 - Django쟝고에서 Decorator 데코레이터 사용하기

Django에서 데코레이터는 웹 애플리케이션의 특정 기능을 쉽게 구현할 수 있도록 도와준다. 주로 인증, 권한 관리, 보안, 캐싱 등의 기능을 위해 사용된다. 

- @login_required : 로그인된 사용자만 접근 가능

- @permisison_required : 특정 권한이 있는 사용자만 접근 가능

- @crsf_exempt : CSRF 검사를 비활성화

- @require_http_mothods : 지정된 HTTP 메소드만 허용

- @require_POST: POST 요청만 허용

- @cache_page: 지정된 시간동안 캐싱하여 출력 성능 향상

- @transaction.atomic: 하나의 트랜잭션으로 모든 DB작업 처리

위 데코레이터들은 쟝고에서 주로 사용되는 것들인데 이제 하나씩 알아보자.

### @login_required

사용자가 로그인하지 않은 상태에서 이 데코레이터가 사용된 뷰에 접근하면 로그인 페이지로 다이렉트를 하는 기능 제공한다.

```javascript
from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    # 로그인한 사용자만 이 뷰에 접근 가능
    return render(request, 'my_template.html')
```

이렇게 사용하면 my_view에 접근하기 위해 로그인이 필요하고 로그인하지 않은 경우 로그인 페이지로 이동한다. (물론 로그인 페이지가 있는 경우)
이때 두 가지 방법으로 로그인 페이지를 login_required와 연결할 수 있다.

1. **직접 login_url 설정**

@login_reqired 데코레이터 함수를 보면 다음과 같이 작성되어 있다.

```javascript
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_url = None으로 되어있는데 이 부분을 로그인 페이지로 설정하면 데코레이터가 정상적으로 작동한다.

2. **accounts / login.html 설정**

쟝고의 기본 설정에 따라 아래 그림처럼 페이지를 구성하면 @login_required를 사용할 수 있다.

![Image](https://upload.cafenono.com/image/slashpagePost/20240817/195202_e1vjE3tb7UGi7ctSI4?q=80&s=1280x180&t=outside&f=webp)

로그인 페이지를 accounts 폴더에 위치시키면 쟝고가 알아서 해당 페이지를 리다이렉트해준다.

로그인 후에는 사용하던 페이지의 url을 next 파라미터에 저장해 다시 리다이렉트 해주니 굉장히 편리한 기능이다.

### @permisison_required

특정 권한이 있는 사용자만 해당 뷰에 접근할 수 있게 제한한다. 권한이 없는 사용자가 접근할 경우 403 Forbidden 응답을 받는다.

```javascript
from django.contrib.auth.decorators import permission_required

@permission_required('app_label.permission_codename')
def my_view(request):
    # 특정 권한이 있는 사용자만 접근 가능
    return render(request, 'my_template.html')
```

### @csrf_exempt

특정 뷰에서 CSRF 검사를 하지 않아도 작동하도록 설정한다. http POST를 사용하면 csrf_token을 포함하지 않아도 된다.

```javascript
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def my_view(request):
    if request.method == "POST":
        # POST 요청 처리
        pass
    return HttpResponse('CSRF 보호가 비활성화된 뷰입니다.')
```

http POST 요청에는 기본적으로 보안을 위해 CSRF 검사를 활성화하는데 아래와 같은 경우 사용한다.

- 서드파티 서비스와의 통합

- API 엔드포인트

- 클라이언트에서 직접 호출하는 뷰

- 서버 간 통신

- 서버가 CSRF를 관리하는 방식이 다른 경우

### @require_http_methods

특정 http 메소드(GET, POST, PUT, DELETE 등)만 허용하도록 제한한다.

```javascript
from django.views.decorators.http import require_http_methods

@require_http_methods(["GET", "POST"])
def my_view(request):
    # GET 또는 POST 요청만 허용
    return render(request, 'my_template.html')
```

데코레이터를 작성하고 리스트 안에 허용할 요청방식을 적어주면 된다.

- @require_POST

- @require_GET

처럼 하나만 사용할 경우 위처럼 적어서 사용할 수 있다.

### @cache_page

특정 뷰의 출력을 캐싱하여 성능을 향상시킨다. 설정된 시간 동안 동일한 요청에 대해 데이터베이스 조회나 처리 없이 빠르게 동일한 응답을 반환한다.

```javascript
from django.views.decorators.cache import cache_page

@cache_page(60 * 15)  # 15분 동안 캐싱
def my_view(request):
    # 이 뷰의 결과가 15분 동안 캐시됨
    return render(request, 'my_template.html')
```

웹 페이지가 자주 변경되지 않고 동일한 콘텐츠를 여러 번 제공하는 경우(블로그, 포슽, 뉴스 기사 등) @cache_page를 사용해 서버 부하를 감소하고 응답 속도를 향상시킨다. 

### @transaction.atomic

뷰에서 실행되는 모든 데이터베이스 작업이 하나의 트랜잭션으로 처리되도록 보장한다. 

```javascript
from django.db import transaction

@transaction.atomic
def my_view(request):
    # 이 뷰의 모든 데이터베이스 작업이 하나의 트랜잭션으로 처리됨
    pass
```

복잡한 데이터베이스 작업을 하나의 트랜잭션으로 묶어서 중간에 오류가 발생했을 때 모든 작업을 원상복구할 수 있다. (예시: 주문 처리 과정에서 여러 데이터베이스 테이블을 업데이트 해야할 때)

모든 데이터베이스 작업이 성공적으로 완료되거나 실패할 때 모든 변경 사항이 롤백되므로 데이터 무결성을 유지한다. 그리고 단일 트랜잭션 처리로 코드의 복잡성을 줄이고 오류를 방지할 수 있다.

For the site tree, see the [root Markdown](https://slashpage.com/kyugntae-ai.md).
