PyMongo에서 사용자 정의 Python 객체를 BSON으로 인코딩하는 방법
PyMongo를 사용하여 사용자 정의 Python 객체를 BSON 형식으로 MongoDB에 저장하려면 SONManipulator를 직접 작성해야 합니다. 공식 문서에서는 이를 다음과 같이 설명합니다.
"SONManipulator 인스턴스를 사용하면 PyMongo가 자동으로 적용할 변환 규칙을 지정할 수 있습니다."
즉, 데이터베이스에 문서를 저장하거나 조회할 때마다 특정 로직이 자동으로 실행되도록 만들 수 있습니다. 아래 예제는 사용자 정의 객체(Custom)를 인코딩하고 디코딩하는 Transform 클래스입니다.
from pymongo.son_manipulator import SONManipulator
class Transform(SONManipulator):
def transform_incoming(self, son, collection):
# DB에 저장되기 전에 호출됨
for (key, value) in son.items():
if isinstance(value, Custom):
son[key] = encode_custom(value)
elif isinstance(value, dict): # 하위 문서도 재귀적으로 처리
son[key] = self.transform_incoming(value, collection)
return son
def transform_outgoing(self, son, collection):
# DB에서 조회될 때 호출됨
for (key, value) in son.items():
if isinstance(value, dict):
if "_type" in value and value["_type"] == "custom":
son[key] = decode_custom(value)
else: # 마찬가지로 하위 문서도 재귀적으로 처리
son[key] = self.transform_outgoing(value, collection)
return son
코드의 핵심 동작은 다음과 같습니다.
- transform_incoming: 문서가 MongoDB에 저장되기 전에 호출되며,
Custom타입의 값을encode_custom()함수로 변환합니다. - transform_outgoing: 문서를 조회할 때 호출되며,
_type필드가"custom"인 딕셔너리를 원래의 Python 객체로 복원(decode_custom())합니다. - 두 메서드 모두 중첩된 딕셔너리(하위 문서)까지 재귀적으로 순회하여 모든 계층의 값이 올바르게 변환되도록 합니다.
조작기 등록하기
작성한 조작기를 PyMongo 데이터베이스 객체에 다음과 같이 등록합니다.
db.add_son_manipulator(Transform())
이제 해당 데이터베이스 객체를 통해 저장하고 조회하는 모든 문서에 자동으로 변환이 적용됩니다.
참고 사항
numpy 배열처럼 _type 식별 필드 없이 조용히 Python 배열로 자동 변환만 하면 되는 경우라면, 굳이 _type 필드를 추가할 필요는 없습니다.
또한 한 가지 주의할 점이 있습니다. SONManipulator는 구버전 PyMongo에서 제공되던 기능으로, 최신 버전(PyMongo 4.0 이상)에서는 제거되었습니다. 최신 환경에서는 TypeRegistry와 TypeCodec을 활용한 사용자 정의 타입 코덱 방식을 사용하는 것이 권장되므로, 프로젝트의 PyMongo 버전을 먼저 확인한 후 적절한 방식을 선택하시기 바랍니다.