본문 바로가기

문돌이 존버/Django 스터디

장고(Django), 적절한 HTTP 상태코드 및 URL Reverse 활용하기

반응형
본 글은 Holix의 "리액트와 함께 장고 시작하기 Complete" 강의를 듣고 작성한 일지입니다.

HTTP 상태코드

웹 서버는 요청이 들어왔을 때 적절한 상태코드로 응답을 해야 합니다. 각 HttpResponse 클래스마다 고유한 status_code가 할당되며 HTTP 상태코드는 REST API를 만들 때 유용합니다.

대표적인 상태코드는 다음과 같습니다(더 자세한 내용은 제 블로그의 다른 글 "HTTP 상태코드 및 일반 헤더"를 참고하시기 바랍니다).

200번대: 성공
- 200: 서버가 요청을 잘 처리함
- 201: 서버가 요청을 접수하고 새로운 리소스를 작성함

300번대: 요청을 마치기 위해 추가 조치 필요
- 301: 요청한 페이지가 새 위치로 영구적으로 이동
- 302: 페이지가 현재 다른 위치에서 요청에 응답하고 있지만, 클라이언트는 향후 원래 위치를 계속 사용해야 함

400번대: 클라이언트측 오류
- 400: 잘못된 요청
- 401: 권한 없음
- 403(Forbidden): 필요한 권한을 가지고 있지 않아 요청 거부
- 404: 서버에서 요청한 리소스를 찾을 수 없음
- 405: 허용되지 않는 방법(예: POST 방식만 지원하는 뷰에 GET 요청을 할 경우)

500번대: 서버측 오류
- 500: 서버 내부 오류 발생

302 응답하는 예를 들면 HttpResponseRedirect 보단 아래 방법을 추천한다고 합니다.

from django.shortcuts import redirect

def view(request):
    ...
    return redirect('shop:item_list')
    
# 가능하나 추천하지 않는 방법
from django.http import HttpResponseRedirect
def view(request):
    return HttpResponseRedirect('/shop/') # 아래 방법도 가능
    # url = resolve_url('shop:item_list')
    # return HttpResponseRedirect(url)
redirect 함수는 내부적으로 resolve_url을 사용합니다. 파라미터로 지정된 문자열이 url_reverse에 실패할 경우 그 문자열을 그대로 URL로 사용하여 redirect를 시도합니다.
from django.http import Http404
from django.shortcuts import get_object_or_404
from shop.models import Item

def view(request):
   try:
       item = Item.objects.get(pk=100)
   except Item.DoesNotExist:
       raise Http404

# 추천하는 방법
def view(request):
    item = get_object_or_404(Item, pk=100)

URL Dispatcher

URL Dispatcher란 urls.py 변경만으로 "각 뷰에 대한 URL"이 변경되는 유연한 URL 시스템입니다. 개발자가 일일이 URL을 계산하지 않아도 되며, URL이 변경되더라도 URL Reverse가 변경된 URL을 추적합니다.

URL Reverse를 수행하는 4가지 함수에는 아래가 있습니다.

url 탬플릿태그 ex) {% url ~~ %}
- 내부적으로 reverse 함수를 사용
- {% url 'blog:post_detail' 100 %}
  {% url 'blog:post_detail' pk=100 %}

reverse 함수
- 매칭 URL이 없으면 NoReverseMatch 예외 발생
- reverse('blog:post_detail', args=[100])
  reverse('blog:post_detail', kwargs={'pk': 100})

resolve_url 함수
- 매핑 URL이 없으면 "인자 문자열"을 그대로 리턴
- 내부적으로 reverse 함수 사용
- resolve_url('blog:post_detail', 100)
  resolve_url('blog:post_detail', pk=100)
  resolve_url('/blog/100/')

redirect 함수
- 매칭 URL이 없으면 "인자 문자열"을 그대로 URL로 사용
- 내부적으로 resolve_url 함수 사용
- redirect('blog:post_detail', 100)
  redirect('blog:post_detail', pk=100)
  redirect('/blog/100/')

다시 말해 urls.py 에서 url 탬플릿을 바꾸더라도 위의 URL Reverse를 사용한다면 개발자가 직접 바꿀 필요 없이 장고가 알아서 처리해주는 것이죠. 아래는 get_absolute_url 함수와 reverse 함수를 함께 사용한 버전입니다.

# 앱/urls.py
urlpatterns = [
    path('', views.post_list, name='post_list'),
    path('<int:pk>/', views.post_detail, name='post_detail'),
    ...
]
# models.py
class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    title = models.CharField(max_length=100)
    content = models.TextField()
    ...

    def __str__(self):
        return self.content
    
    def get_absolute_url(self):
        return reverse('blog1:post_detail', args=[self.pk])
<!-- post_list.html -->
<td>
    <a href="{{ post.get_absolute_url }}">
    {{ post.content }}
    </a>
</td>

URL Reverse를 활용하는 또 다른 경우는 CreateView 및 UpdateView를 사용할 때입니다. 포스팅을 추가하거나 기존의 포스팅을 수정한 이후에 자동으로 다른 페이지로 이동되는 경험이 많죠. 이렇게 현재 작업 완료 이후에 보여줄 페이지를 success_url이라고 하며 이를 제공하지 않을 경우 해당 model instance의 get_absolute_url 주소로 이동이 가능한지 체크하고, 가능할 경우 이동합니다.

글을 생성하거나 수정한 이후 Detail 화면으로 이동하는 것은 자연스러운 시나리오입니다. 특정 모델에 대한 Detail 뷰를 작성할 경우 URLConf 설정을 하자마자 필히 get_absolute_url 설정을 한다면 코드가 보다 간결해질 것입니다.

728x90
반응형