[REAL Python – Flask] – “Flask HTTP API(4) – marshmallow 로 Python에서 직렬화 / 역직렬화 처리하기”
[REAL Python – Flask] – “Flask HTTP API(4) – marshmallow 로 Python에서 직렬화 / 역직렬화 처리하기”
marshmallow 사용해 보기 – 직렬화
먼저, 프로젝트를 하나 생성해 주겠습니다. Flask와 같은 것들은 설치하지 않아도 됩니다. 단지, marshmallow-example 이라는 디렉토리에 serialization.py 라는 파일을 하나 만들고, python -m venv venv 을 터미널에 입력하여 새로운 가상환경을 만들고 활성화시켜 주세요. 아래와 같은 구조가 되어야 합니다.
이후, 위와 같이 serialization.py 파일을 하나 만들어 주겠습니다.
pip list 를 터미널에 입력하면, 단지 위의 두 줄만 있어야 합니다.
이후,
pip install -U marshmallow
를 입력하여 marshmallow 를 설치해 줍니다.
다시 pip list 를 입력하면, 위와 같이 패키지 목록에 marshmallow, packaging, pyparsing 이 추가된 것을 확인할 수 있습니다.
그리고, 만들어준 serialization.py 에 아래와 같은 코드를 입력하겠습니다.
“가오리” 클래스를 정의했습니다. 해당 클래스는 학명, 이름, 길이 등의 인스턴스 변수를 가지고 있습니다. 생성자에 위와 같은 인자들을 입력한다면, 하나의 가오리 객체가 생성될 겁니다. 그럴 일은 없겠지만, 제가 미미라는 이름의 매가오리 한 마리를 키운다고 가정해 보겠습니다. 그리고 그것을 위에서 만든 가오리 클래스를 이용하여 구현해 봅시다.
제가 키우는 미미라는 가오리는 Myliboatis tobijei라는 학명을 가지고 있으며, “미미” 라는 이름을 가지고, 1.8m의 길이, 1.2m의 너비, 45kg의 무게, 3살, 암컷이라는 정보를 가지고 있습니다. __dict__
는 파이썬의 매직 메소드로서, 해당 객체가 가지고 있는 정보들을 확인할 수 있게 해 줍니다.
아무튼, 저는 미미라는 가오리를 너무 애지중지 키우던 도중, 제 친한 친구 민수에게 미미에 대해 소개해주고 싶어졌습니다. 그런데, 평소 본인이 상남자라는 사실을 전파해 온 본인은 미미라는 가오리의 성별이 암컷이라는 사실을 숨기고 싶었습니다( 현실의 본인의 사상과는 무관한 그냥 예시임..). 또, 과학에 문외한인 민수를 위해서 학명과 같은 정보는 보내고 싶지 않았습니다. 단지, 간단한 정보인 “이름”, “길이”,”너비”,”체중” 의 정보만 보내주고 싶었습니다. 저는 미미에 대한 정보를 친구 민수에게 json
으로 보내줄 겁니다.
그러한 경우, 아래와 같이 보내고 싶은 정보를 꾸릴 수 있습니다.
딕셔너리에서 값을 얻어온 다음, 그것을 다시 딕셔너리의 값으로 만들어 넘기면 됩니다. 딕셔너리는 파이썬 내장 모듈인 json을 사용하여 파이썬의 자료구조인 딕셔너리를 json 으로 바꿀 수 있습니다, 아무튼 위와 같은 딕셔너리를 얻는다면, 우리는 친구 민수에게 보낼 데이터를 성공적으로 얻게 되는 겁니다.
미미라는 가오리를 키우던 저는 미미가 외로울까 “뽀삐” 라는 이름을 붙인 대왕쥐가오리 한 마리를 더 데려왔습니다.
또 자랑병이 도진 저는 제 친구 민수에게 뽀삐에 대해서 자랑하고 싶어졌습니다. 그렇다면 위에서 했던 작업 그대로, 아래와 같은 코드를 작성하여 민수에게 보내고자 하는 데이터를 얻을 수 있겠죠?
수집욕에 빠진 제가 앞으로 백 마리, 천 마리의 가오리를 데려오고 데려올 때마다 친구 민수에게 데이터를 보낸다고 생각해 보세요. 이는 굉장히 끔찍한 코드 작성의 노동일 겁니다. 손가락을 걱정하던 저는, Marshmallow 라는 파이썬 라이브러리를 찾아내게 됩니다. 아래까지가 현재까지 작성된 코드입니다.
class Stingray:
def __init__(self, scientific_name, name, length, width, weight, age, gender, ):
self.scientific_name = scientific_name # 학명
self.name = name # 이름
self.length = length # 길이
self.width = width # 너비
self.weight = weight # 체중
self.age = age # 나이
self.gender = gender # 성별
내가_키우는_가오리_미미 = Stingray(
"Myliobatis tobijei",
"미미",
"1.8m",
"1.2m",
"45kg",
"3",
"female"
)
민수에게_보낼_미미의_정보 = {"name" : 내가_키우는_가오리_미미.__dict__['name'],
"length" : 내가_키우는_가오리_미미.__dict__['length'],
"width" : 내가_키우는_가오리_미미.__dict__['width'],
"weight" : 내가_키우는_가오리_미미.__dict__['weight']
}
print("민수야, 미미에 대해서 소개할게!")
print(민수에게_보낼_미미의_정보)
내가_키우는_또다른_가오리_뽀삐 = Stingray(
"Mobula birostris",
"뽀삐",
"8.9m",
"9.5m",
"3000kg",
"17",
"male"
)
민수에게_보낼_뽀삐의_정보 = {"name" : 내가_키우는_또다른_가오리_뽀삐.__dict__['name'],
"length" : 내가_키우는_또다른_가오리_뽀삐.__dict__['length'],
"width" : 내가_키우는_또다른_가오리_뽀삐.__dict__['width'],
"weight" : 내가_키우는_또다른_가오리_뽀삐.__dict__['weight']
}
print("민수야, 뽀삐에 대해서 소개할게!")
print(민수에게_보낼_뽀삐의_정보)
# 이 줄부터 작성하시면 됩니다!
marshmallow 에서는 아래와 같이 스키마 클래스를 상속받아, StringraySchema 클래스를 정의할 수 있습니다.
그리고, StringraySchema 클래스의 객체를 하나 만들어 주겠습니다.
가오리 정보 변환기.dump(내가 키우는 가오리 미미) 와 같은 코드를 넣었더니,
미미와 뽀삐에 대한 원하는 정보만 딱딱 변환이 되는 것을 확인할 수 있네요. 저는 손가락을 지킬 수 있게 되었습니다.
예시가 사뭇 비현실적이지만, 지금까지 우리가 작업한 내용은 “가오리 클래스 인스턴스” 를 “딕셔너리 형태” 로 바꾼 것이었습니다. 그리고 아래의 코드를 추가한다면 “딕셔너리” 를 “json” 으로 바꿀 수도 있습니다.
제 친구 민수에게 제가 키우는 가오리에 대해서 설명해주고 싶은데, 문제는 “말로 그것을 어떻게 설명할래?” 였습니다. 그리하여, 저는 가오리 클래스를 작성한 다음 가오리 객체를 만들고, 그것을 친구에게 설명해줄 수 있는 포맷으로 직렬화해 주었습니다. 이러한 방식을 사용하여 데이터를 친구 민수에게 전송해 준다면, 민수가 사용하는 프로그래밍 환경이 무엇이던간에 미미와 뽀삐에 대해서 알 수 있을 겁니다. marshmallow 는 이처럼, 파이썬의 클래스를 다른 포맷으로 직렬화하는 좋은 방법을 제시해 줍니다. 스키마를 작성해서, “어떤 클래스 객체에 대한 이러한 것들만 직렬화할래!” 라고 했고, 실제로 Stingray 객체를 파이썬의 딕셔너리로 바꾸어 주었습니다.
marshmallow 사용해 보기 – 역직렬화
이번에는, 파이썬의 딕셔너리를 파이썬의 클래스로 변환하는 작업을 해 보겠습니다. 이것은 위의 반대 과정입니다. Stringray 클래스의 객체->json
였다면, 이번에는 json-> Stringray 클래스의 객체
로 바꾸어 볼 것입니다!
일단, serialization.py 에서 아래 내용을 주석 처리합니다.
위와 같은 이름을 가진 파일을 만들고,
from marshmallow import Schema, fields
from serialization import Stingray
import json
'''
어떤 데이터가 json 으로 들어왔고,
json.dumps(어떤 데이터) 를 통해서 <class dict> 가 되었다고 가정
'''
JSON_가오리_데이터 = {"scientific_name":"some_stringray",
"length": "1.8m",
"weight": "45kg",
"name": "common_stingray",
"width": "1.2m",
"gender":"female",
"age": 3 }
# Schema 정의
class StingraySchema(Schema):
scientific_name = fields.String()
length = fields.String()
weight = fields.String()
name = fields.String()
width = fields.String()
gender = fields.String()
age = fields.Integer()
가오리정보_변환기 = StingraySchema()
data = 가오리정보_변환기.load(JSON_가오리_데이터)
Stringray_객체 = Stingray(**data)
print(Stringray_객체)
위의 코드를 살펴보겠습니다.
관점을 약간 틀어서, 우리가 서버이고 클라이언트가 어떠한 데이터를 json 으로 보내주었다고 가정하겠습니다.
그러면, 서버에서는 들어온 json 데이터를 파이썬의 객체로 다룰 수 있도록 할 것입니다.
위의 상황이라면, 예컨대 플라스크에서 request.get_json() 과 같은 데이터로 클라이언트가 보낸 데이터를 파이썬의 dict로 바꾸었다고 가정합니다.
이러한 경우 들어온 해당 데이터를, 아래와 같이 serialization.py 에서 정의했던 Stingray 클래스의 객체로 바꿀 수 있습니다.
파이썬 클래스의 객체를 json 으로, json 을 다시 파이썬 클래스의 객체로 바꾸는 작업을 해 보았습니다. 앞서 Sqlalchemy 와 같은 파이썬 ORM 을 사용할 때에 모델과 같은 코드들을 파이썬의 클래스로 작업했던 것을 기억하시는지요? 이제 위의 marshmallow 를 sqlalchemy 와 연동하여 조금 더 쉽게 사용할 수 있는 별도의 패키지를 설치하여, 하나의 프로젝트를 완성시켜 보겠습니다. :-)