JSON ์ง๋ ฌํ
๐ก Serialization(์ง๋ ฌํ)
๋ชจ๋ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์ ํต์ ์์ ๋ฐ์ดํฐ๋ ํํ ๋ฌธ์์ด ๋๋ ๋ฐ์ดํธ๋ก
ํํ ๋์ด์ผ ํ๋ค.
์ฆ, ๋ฐ์ดํฐ๋ฅผ ๋ฌธ์์ด ๋๋ ๋ฐ์ดํธ๋ก ๋ณํํด์ฃผ๋ ์์ ์ ์ง๋ ฌํ๋ผ๊ณ ํ๋ค.
์ก์ ์์ ๊ฒฝ์ฐ์๋ ๊ฐ์ฒด๋ฅผ ๋ฌธ์์ด๋ก ๋ณํํ์ฌ, ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ณ (์ง๋ ฌํ)
์์ง์์ ์ ์ฅ์์๋ ์์ ํ ๋ฌธ์์ด์ ๋ค์ ๊ฐ์ฒด๋ก ๋ณํํ์ฌ(๋น์ง๋ ฌํ) ๋ฐ์ดํฐ๋ฅผ
ํ์ฉํด์ผ ํ๋ค. ๋ํ ๊ฐ ์ธ์ด์์ ๋ชจ๋ ์ง์ํ๋ ์ง๋ ฌํ ํฌ๋งท(JSON, XML ๋ฑ)๋
์๊ณ , ํน์ ์ธ์ด์์๋ง ์ง์ํ๋ ์ง๋ ฌํ ํฌ๋ฉง(python-pickle)์ด ์๋ค.
๐ก JSON VS Pickle
JSON
๏ผ. ๋ค๋ฅธ ์ธ์ด/ํ๋ซํผ๊ณผ ํต์ ํ ๋ ์ฃผ๋ก ์ฌ์ฉ.
๏ผ. ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ json ์ ๊ณต
๏ผ. Pickle์ ๋นํด ์ง๋ ฌํ๋ฅผ ์ง์ํ๋ ๋ฐ์ดํฐํ์ ์ ์๊ฐ ์ ์ง๋ง, ์ปค์คํ ๊ฐ๋ฅ
๏ผ. ์ฐ์์ฑ ๋ฐ์ดํฐ์ ์ฃผ๋ก ํ์ฉ
1
2
3
4
5
6
7
8
9
10
11
12
import json
data = [{'message':'django'}]
# ์ง๋ ฌํ - ๋ฌธ์์ด ํ์
json_string = json.dumps(data)
print(json_string)
# ์ถ๋ ฅ: [{'message':'django'}]
# ๋น์ง๋ ฌํ
print(json.loads(json_string))
# ์ถ๋ ฅ: [{'message':'django'}]
Pickle
๏ผ. ํ์ด์ฌ ์ ์ฉ ํฌ๋งท์ผ๋ก์ ํ์ด์ฌ ์์คํ ๋ผ๋ฆฌ๋ง ํต์ ํ ๋ ์ฌ์ฉ ๊ฐ๋ฅ
๏ผ. ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ pickle ์ ๊ณต โ json ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ์ฌํ ์ฌ์ฉ ๋ฐฉ๋ฒ
๏ผ. ํ์ด์ฌ ๋ฒ์ ํน์ฑ์ ํ๋ ๊ฒฝ์ฐ๊ฐ ์์.
๏ผ. ์์์ฑ ๋ฐ์ดํฐ์ ์ฃผ๋ก ํ์ฉ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import pickle
data = [{'message':'django'}]
# ์ง๋ ฌํ - ๋ฐ์ดํธ ํ์
pickle_string = pickle.dumps(data)
print(pickle_string)
"""
b'\x80\x04\x95\x1a\x00\x00\x00\x00\x00\x00\x00]\x94}
\x94\x8c\x07message\x94\x8c\x06django\x94sa.'
"""
# ๋น์ง๋ ฌํ
print(pickle.loads(pickle_string))
# ์ถ๋ ฅ: [{'message': 'django'}]
ํ์ง๋ง JSON/Pickle ๋ชจ๋ ํ์ด์ฌ ๊ธฐ๋ณธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
๋ฐ๋ผ์ ์ฅ๊ณ ํ์ (Model/Queryset ๋ฑ)์ ๋ํด์๋ ์ง๋ ฅํ Rule์ด ์๋ค.
๋ง์ฝ์ Queryset์ ์ง๋ ฌํ๋ฅผ ์๋ํ ๊ฒฝ์ฐ์๋ ์๋์ ๊ฐ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
TypeError: Object of type QuerySet is not JSON serializable
๐ก DjagnoJSONEncoder
์์ ๊ฐ์ด ์๋ฌ๊ฐ ์๊ธด๊ฒฝ์ฐ DjagnoJSONEncoder๋ฅผ ์๋ํด์ ๋ช ๊ฐ์ง
Rule์ ์ถ๊ฐ๋ก ๋ถ์ฌํ ์ ์๋ค.
1
2
3
4
5
6
import json
from django.core.serializers.json import DjangoJSONEncoder
from django.contrib.auth import get_user_model
qs = get_user_model().objects.all()
json_string = json.dumps(qs, cls=DjangoJSONEncoder)
์์ ๊ฐ์ด cls ์ธ์๋ฅผ ์ฌ์ฉํ์ฌ DjagnoJSONEncoder๋ฅผ ํ์ฉํ ์ ์๋ค.
ํ์ง๋ง ์ฌ๊ธฐ์๋ ์ญ์๋ TypeError: Object of type QuerySet is not JSON serializable
์๋ฌ๊ฐ ๋ฐ์ํ๋ค. ๊ทธ๋ฌ๋ฉด ์ด๋ป๊ฒ ์ ์๋ฌ๋ฅผ ํด๊ฒฐํ ์ ์์๊น??
List-Comprehension์ ์ฌ์ฉํ์ฌ โ์ง์ ๋ณํโ ํด์ฃผ๋ฉด ๋๋ค.
1
2
3
4
5
data = [
{'id': post.id, 'title': post.title, 'content': post.content} for post in Post.objects.all()
]
json.dumps(data, ensure_ascii=False)
์ฌ๊ธฐ์ ensure_ascii ํ๋ผ๋ฏธํฐ์ default๊ฐ์ True์ด๋ค.
์๋์ ๊ฐ์ด True์ผ ๊ฒฝ์ฐ์๋ ์ ๋์ฝ๋๋ฅผ ๋ฐํํด์ฃผ๊ณ ,
False์ผ ๊ฒฝ์ฐ ๋ฌธ์์ด์ ๊ทธ๋๋ ๋ฐํํด์ค๋ค.
1
2
3
4
5
6
7
import json
print(json.dumps('์์ธ'))
# "\uc11c\uc6b8"
print(json.dumps('์์ธ', ensure_ascii=False))
# "์์ธ"
๐ก ์ง์ Rule ์ง์
ํ์ง๋ง ๋งค๋ฒ ์ง์ ๋ณํํ๊ฒ ๋๋ฉด ์๊ฐ๋ ์ค๋๊ฑธ๋ฆฌ๊ณ ๋นํจ์จ์ ์ด๋ค.
๋ฐ๋ผ์ โ์ง์ ๋ณํ Ruleโ์ ์ง์ ํ๋ ๋ฐฉ๋ฒ์ด ์ต์ ์ด๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models.query import Queryset
#์ปค์คํฐ JSON Encoder๋ฅผ ์ ์
class MyJSONEncoder(DjangoJSONEncoder):
def default(self,obj):
if isinstance(obj, Queryset):
return tuple(obj)
elif isinstance(obj, Post):
return {'id': obj.id, 'title': obj.title, 'content': obj.content}
elif hasattr(obj, 'as_dict'):
return obj.as_dict()
# ์์ ๋ก์ง ๋ชจ๋ ์คํจ์
return super().default(obj)
data = Post.objects.all()
# ์ง๋ ฌํ ํ ๋, ์ง๋ ฌํ๋ฅผ ์ํํด์ค JSON Encoder๋ฅผ ์ง์ ํด์ค๋๋ค.
json.dumps(data, cls = MyJSONEncoder, ensure_ascii=False)
๐ก DRF - JSONRender
rest_framwork/utils/encoders.py์ JSONEncoder๋ฅผ ํตํ ์ง๋ ฌํ.
Model ํ์ ์ ์ฌ๊ธฐ์ ์ง์ํ์ง ์๊ณ , ModelSerializer์์ ์ง์ํด์ค๋ค.
-
์ฅ๊ณ ์ DjangoJSONEncoder๋ฅผ ์์๋ฐ์ง ์๊ณ , json.JSONEncoder ์์์ ํตํด ๊ตฌํ
-
datetime.datetime/date/time/timedelta
-
decimal.Decimal, uuid.UUID, six.binary_type
-
__getitem__์์ฑ์ ์ง์ํ ๊ฒฝ์ฐ dict(obj) -
__iter__์์ฑ์ ์ง์ํ ๊ฒฝ์ฐ tuple๋ณํ -
QuerySet ํ์ ์ผ ๊ฒฝ์ฐ, tuple ๋ณํ
-
.tolist์์ฑ์ ์ง์ํ ๊ฒฝ์ฐ, obj.tolist() ๋ฐํ
๐ก DRF - JSONRenderer
JSONRenderer๋ json.dumps๋ฅผ ๋ํํ ํด๋์ค์ด๋ค.
JSONRenderer๋ ๋ณด๋ค ํธ๋ฆฌํ ์ง๋ ฌํ๋ฅผ ์ง์ํด์ฃผ๊ณ , UTF-8 ์ธ์ฝ๋ฉ๋ ์ถ๊ฐ์ ์ผ๋ก ์ํํด์ค๋ค.
ํ์ง๋ง ์ฌ๊ธฐ์์๋ Model์ ๋ํ ๋ณํ Rule์ ์๋ค.
1
2
3
4
5
6
7
from rest_framework.renderers import JSONRenderer
data = Post.objects.all()
serializer = PostSerializer(data, many=True)
JSONRenderer().render(serializer.data)
๐ก Model Serializer๋ฅผ ํตํ JSON์ง๋ ฌํ
Serializer/ModelSerializer ํด๋์ค๋ ํ ์ธ์ด์ ์๋น์ค ์์ญ์ ๋ด๋นํ๋ค๊ณ
๋ณผ ์ ์๋ค. ์ค์ ๋ฐ์ดํฐ ์์ฑ, ์์ , ์กฐํ ๋ก์ง์ ๊ตฌํํ ์ ์๋ค.
๋ํ Form/ModelForm๊ณผ ์ ์ฌํ ์ธก๋ฉด์ด ์์ง๋ง ๊ธฐ๋ฅ์ด ์กฐ๊ธ ๋ ๋ง๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# serializers.py
from rest_framework.serializers import ModelSerializer
from .models import Post
class PostSerializer(ModelSerializer):
class Meta:
model = Post
fields = '__all__'
# forms.py
from django.forms import ModelForm
from .models import Post
class PostForm(ModelFrom):
class Meta:
model = Post
fields = '__all__'
ModelForm๊ณผ ์ ์ฌํ ModelSerializer
๋ฐ์ดํฐ ํ๊ฐ๋ง ๋๊ธธ ๊ฒฝ์ฐ๋ ์๋์ ๊ฐ์ด. 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from rest_framework.serializers import ModelSerializer
from .models import Post
class PostSerializer(ModelSerializer):
class Meta:
model = Post
fields = '__all__'
# Post ํ์
post = Post.objects.first()
serializer = PostSerializer(post)
# ReturnDict ํ์
serializer.data
๋ฐ์ดํฐ ๋๊ฐ ์ด์ ๋๊ธธ ๊ฒฝ์ฐ์๋ many=True๋ฅผ ๊ผญ ์ง์ ํด์ค์ผํ๋ค.
์ด ๊ฒฝ์ฐ์ ๋ฐํ ๊ฐ์ list์ OrdererDict์ ์กฐํฉ์ด๋ค.
ReturnDict ํ์
์ค์ serializer.data์ ๋ฐ์ดํฐ ํ์ ์ด๋ค.
OrderedDict๋ฅผ ์์ ๋ฐ์์ผ๋ฉฐ, serializer๋ฅผ ํค์๋ ์ธ์๋ก ๋ฐ๋๋ค.
1
2
3
4
5
class ReturnDict(OrderedDict):
def __init__(self, *args, **kwargs):
self.serializer = kwargs.pop('serializer')
super().__init__(*arg, **kwargs)
ModelSerializer์ QuerySet ๋ณํ ์ง์
Model์ ๊ฐ์ฒด์ ๋ํด์๋ many=False(default)๋ฅผ ์ง์ ํด์ค์ผ ํ๋ค.
๋ฐ๋ฉด์, QuerySet ๊ฐ์ฒด์ ๋ํด์๋ many=True๋ฅผ ์ง์ ํด์ค์ผ ํ๋ค.
๋ง์ฝ many์ ์ง์ ์ด ์ณ์ง ์์ ๊ฒฝ์ฐ TypeError, AttributeError๊ฐ ๋ฐ์ํ๋ค.
1
2
3
4
5
6
7
8
9
# ํ์ด์ฌ ๊ธฐ๋ณธ JSON ๋ณํ ์ฌ์ฉ ํ์ฉ
# ๋ฌธ์์ด ํ์
import json
json_str_string = json.dumps(serializer.data, ensure_ascii=False)
# DRF์์ ์ง์ํ๋ JSON ๋ณํ ํ์ฉ โ ๋ณํ Rule์ด ์ถ๊ฐ๋ Encoder
# ๋ฐ์ดํธ ํ์
from rest_framework.renderers import JSONRenderer
json_utf8_string = JSONRenderer().render(serializer.data)


