ํฌ์ŠคํŠธ

JSON ์ง๋ ฌํ™”

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)

์ด ๊ธฐ์‚ฌ๋Š” ์ €์ž‘๊ถŒ์ž์˜ CC BY 4.0 ๋ผ์ด์„ผ์Šค๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.