ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • DRF Renderer에 따라 다르게 동작되는 서버 Response
    💻 프로그래밍/Django 2022. 7. 10. 19:29

    으악 500에러 안돼~

    회사 신규 서비스를 개발하던 중 슬랙 알림으로 500 에러가 올라왔습니다!;; (500 에러 보면 경기 일으킴)

    에러 메시지를 보니까 토큰 리프래시 하는 api에서 오류가 났더라고요!

     

    method를 확인해보니 GET으로 요청하고 있었습니다. 토큰 리프래시 api는 POST로만 요청이 가능했고, 해당 메서드가 없는 경우 405를 리턴하는 걸로 알고 있었습니다.

     

    postman으로 테스트를 했을 때도 500 에러가 아닌 405를 리턴해주고 있었습니다!

    뭔가 이상함을 느꼈고 에러 메시지를 유심히 보다가 UserAgent를 보았습니다.

     

    Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0

     

    음... ios, android에서 보내는 UA는 아닌 것 같아서 찾아보니 크롬 브라우저의 UA 였습니다! (유레카!)

    바로 크롬에서 토큰 리프레시 api를 호출하니 500 에러가 난 걸 확인할 수 있었습니다!

     

    응? 클라이언트에 따라 다르게 동작을 하네??

     

    왜 그런지 원인을 찾아보았습니다! 

    💡 원인


    (좌)크롬 브라우저, (우)포스트맨

    혹시 브라우저에서 DRF api url을 호출했을 경우 (좌)의 이미지를 보신 적 있을까요?! 혹은 포스트맨으로 api를 호출했을 때 (우) 이미지 같이 보신 적은요?!

     

    바로 여기에 정답이 있었습니다. 일단은 우리가 위 이미지를 보았을 때 알 수 있는 사실은 어디서 호출하느냐에 따라 reponse의 랜더링이 달라진다는 것입니다! (오 그럼 동작도 다를 것 같..다?! 그래서 !!)

    공식문서 Rederers 설명

    DRF 공식문서를 찾아보니 renderer에 대한 설명과 default renderer에 대해서 나와있었습니다.  기본적으로 settings에 아무런 설정이 없다면 JSONRederer, BrowableAPIRenderer를 기본으로 사용하고 있습니다.

     

    두 랜더러 클래스 설명

     

    공식문서를 보면 JSONRederer, BrowableAPIRenderer가 어떤 형태로 랜더링 되는지 확인할 수 있습니다.

     

    DRF에서는 Accept header미디어 타입(Media type)을 참고하여 어떤 reponse를 보낼지 결정한다고 합니다! 그렇기 때문에 브라우저와 포스트맨의 요청에서 reponse의 형태가 달랐던 것입니다.

     

    먼저 브라우저에서 어떤 Accept가 있는지 확인을 해보겠습니다! 개발자 도구 네트워크 탭에서 request header를 보겠습니다

     

    브라우저 요청헤더

    오... 헤더의 Accept를 보니까 위와 같이 생겼네요. 오 일단 JSONRenderer에서 사용하는 application/json 타입은 없네요! 그렇기 때문에 BrowableAPIRenderer를 통해 랜더링 된 response가 전달이 되었네요!!

     

    여기까지 request header의 Accept에 따라 세팅된 rederer class를 체킹 해서 적절히 랜더링 된 response를 전달해준다는 것을 알았습니다. 그럼 실제 코드상으로 위 동작들이 어떻게 진행되는지 디버깅을 해보겠습니다!

     

     

    🐛 디버깅


    제가 궁금했던 건 어디서 renderer를 어떻게 선택해주는지가 궁금했습니다!

    select_renderer 함수에서 default renderer중에 선택

    디버깅을 찍어 보았을 때 select_renderer 함수에서 media_type을 통해 어떤 renderer를 선택해야 할지 체크하고 있었습니다. 

    (좌) BrowsableAPIRenderer (우) JSONRenderer

    for media_type_set in order_by_precedence(accepts):

    위 로직에서 accepts에 있는 media_type들의 순서를 odering 뒤 for loop를 돌리고 loop안에서는  rederer가 rendering 할 수 있는 media_type인지 체크합니다.

     

    그냥 실제로 동작이 어떻게 일어나는지 한번 보고 싶어서 디버깅을 해봤습니다! ㅋㅋ

     

    다만, 브라우저에서 호출해도 postman으로 호출했을 때처럼 동일하게 출력되게 하려면..??

     

    REST_FRAMEWORK = {
        'DEFAULT_RENDERER_CLASSES': [
            'rest_framework.renderers.JSONRenderer',
            # 'rest_framework.renderers.BrowsableAPIRenderer',
        ]
    }
    cs

    settings에서 BrowsableAPIRenderer를 주석처리하거나 삭제하면 됩니다!

     

     

    참고
    [HTTP] Content-Type vs Accept 헤더

     

    [HTTP] Content-Type vs. Accept 헤더

    HTTP 헤더 중 Content-Type 헤더와 Accept 헤더의 용도와 차이점

    velog.io

     

    댓글

운동하는 개발자 JAY-JI