flask 책에서 jsonify를 사용해서 그대로 따라 썼는데, 왜 쓰는지에 대해 이해하기 위해 찾아보았다. 내가 이해한 바로 결론을 내면, jsonify가 편하긴 하지만 지원되지 않는 형식(한글 등)을 주고 받으려면 별도의 인코더가 필요하다!
참고 문서:
- flask API
- programmerssought
- soooprmx
사용자가 json data를 내보내도록 제공하는 flask의 함수. jsonify()는 json response를 보내기 위해 이미 content-type header가 'application/json'로 되어 있는
flask.Response() 객체를 리턴한다.
문제 (참고 링크)
jsonify는 json.dump를 사용하기 때문에 아스키 이스케이프 인코딩을 적용한다. Flask처럼 웹서버로 쓰이는 환경에서 이런식으로 데이터를 내려보내면 프론트엔드에서는 다시 이 코드포인트들을 문자열로 변환해야 한다는 문제점이 생긴다.
플라스크 소스 코드의 일부분을 참고해보자. 위에서 잠깐 언급했듯 jsonify도 함수 내부에서도 json form으로 serialize하는 과정에서 json.dumps를 쓴 것을 알 수 있다. 다만 dump하기 전에 받은 값들을 모두 dictionary로 만들었다.
def jsonify(*args, **kwargs):
if __debug__:
_assert_have_json()
return current_app.response_class(json.dumps(dict(*args, **kwargs),
indent=None if request.is_xhr else 2), mimetype='application/json')
python이 가지고 있는 json library의 json.dumps() method는 수동으로 MIME type header를 추가해주어야 하는 encoded string을 리턴한다.
하지만 flask가 알아서 판단해 response를 자동으로 보내주도록 사용하기 때문에 직접적으로 사용할 수 있다. 다만 reponse header fields는 디폴트(text/html; charset=utf-80)로 처리된다.
아래에 있는 Flask API 설명에 의하면, payload serializer가 있고, 디폴트가 compact JSON이라서 바로 가지는 것 같다.
serializer = <flask.json.tag.TaggedJSONSerializer object>
A python serializer for the payload.
The default is a compact JSON
derived serializer with support for some extra Python types such as datetime objects or tuples.
# jsonify
return jsonify({'token' : token}), 200
# json.dumps
return json.dumps({'token' : token}), 200
# dictionary
return {'token' : token}, 200
jsonify : 참고한 글에서는 dictionary type만 된다고 적혀 있는데, 테스트를 해보니 이제 리스트도 받을 수 있다. 아마 업데이트가 된 것 같다.
json.dumps : jsonify 보다 더 다양한 type을 받을 수 있다고 나와있다.
이건 내가 python shell에서 찾다가 본 사소한 차이점인데 ㅋㅋ 당연한 말이겠지만 jsonify는 flask 앱 내에서만 실행이 가능해서 앱 세팅이 안 되어 있는 shell에서는 사용이 안 된다.
반면 json.dumps는 shell에서 바로 return을 받아볼 수 있다.
view function은 string, dict, tuple, reponse instange(jsonify 같은?) 애들만 return 할 수 있다. 글 하단에 공식 문서의 response type을 첨부해두었다. 우선 리스트를 리턴하려고 했을 때 뜨는 에러는 다음과 같다.
TypeError: The view function did not return a valid response. The return type must be a string, dict, tuple,
Response instance, or WSGI callable, but it was a list.
만약 json.dumps, jsonify로 리스트를 보내게 되면 정상적으로 들어온다. 위에서 말했듯 content-type만 다르다.
...
return json.dumps( [ token ] )
return jsonify( [ token ] )
# 둘 다 리스트를 리턴할 수 있다.
아래 링크의 make_response에서 확인했다.
https://flask.palletsprojects.com/en/1.1.x/api/
make_response(rv)
Convert the return value from a view function to an instance of response_class.
Parameters rv –
the return value from the view function. The view function must return a response. Returning None, or the view ending without returning, is not allowed. The following types are allowed for view_rv:
str (unicode in Python 2)
A response object is created with the string encoded to UTF-8 as the body.
bytes (str in Python 2)
A response object is created with the bytes as the body.
dict
A dictionary that will be jsonify’d before being returned.
tuple
Either (body, status, headers), (body, status), or (body, headers), where body is any of the other types allowed here, status is a string or an integer, and headers is a dictionary or a list of (key, value) tuples. If body is a response_class instance, status overwrites the exiting value and headers are extended.
response_class
The object is returned unchanged.
other Response class
The object is coerced to response_class.
callable()
The function is called as a WSGI application. The result is used to create a response object.
멋져...