파이썬의 비밀스러운 삶: 피클 병
Source: Dev.to
“Cannot Pickle”가 발생하는 이유: 파이썬 직렬화의 한계
데이터베이스 연결, 파일 핸들, 네트워크 소켓 등과 같이 특정 객체를 복사하거나 직렬화하려 할 때 파이썬은 “cannot pickle …” 와 같은 TypeError를 발생시킵니다. 이 오류는 pickle 모듈이 오직 파이썬 메모리 안에 완전히 존재하는 데이터만 직렬화할 수 있다는 근본적인 제한에서 비롯됩니다.
피클링이란?
피클링은 파이썬에서 직렬화(serialization) 를 일컫는 용어로, 메모리 상의 객체를 파일에 쓰거나 네트워크를 통해 전송하거나 깊은 복사를 위해 평평한 바이트 스트림으로 변환하는 과정입니다.
import pickle
data = {"matches": 10, "active": True}
pickled_data = pickle.dumps(data)
print(pickled_data)
pickle.dumps()는 원본 데이터를 나타내는 바이트 객체(예:b'\x80\x04\x95\x11...')를 반환합니다.pickle.loads()는 그 과정을 역전시켜, 원본과 동등한 새로운 객체를 재구성합니다.
pickle 모듈은 각 객체의 타입, 속성, 그리고 원시 데이터를 기록합니다. 언피클링 시 파이썬은 기록된 지시를 따라 객체를 복원합니다.
일부 객체를 피클링할 수 없는 이유
운영 체제가 관리하는 리소스에 의존하는 객체—예를 들어:
- 파일 디스크립터(열린 파일)
- 네트워크 소켓(활성 연결)
- 데이터베이스 연결
은 해당 OS가 리소스를 보유하고 있는 동안에만 의미 있는 상태를 가집니다. 원시 디스크립터(예: 소켓 번호)를 직렬화한다 해도, 나중에 OS가 그 번호를 다른 프로세스에 재할당할 수 있기 때문에 무용지물이 됩니다. 따라서 pickle은 이러한 객체를 직렬화하지 않으며 TypeError를 발생시킵니다.
피클 vs. 다른 포맷
| Feature | Pickle | JSON |
|---|---|---|
| 언어 특이성 | 파이썬 전용 | 언어에 구애받지 않음 |
| 지원 타입 | 거의 모든 파이썬 객체(사용자 정의 클래스 포함) | 기본 타입: 문자열, 숫자, 불리언, 리스트, 딕셔너리 |
| 인간 가독성 | 없음(바이너리) | 있음(텍스트) |
| 보안 | 언피클링 시 임의 코드 실행 가능 | 안전(코드 실행 없음) |
자바스크립트, Go 등 다른 언어로 작성된 프로그램과 데이터를 교환해야 한다면 JSON(또는 MessagePack 같은 상호 운용 포맷)이 보통 더 좋은 선택입니다.
보안 고려 사항
신뢰할 수 없는 소스로부터 절대 언피클링하지 마세요.
언피클링 과정에서 파이썬은 바이트 스트림에 저장된 지시를 실행합니다. 악의적인 페이로드가 자동으로 실행되는 코드를 포함할 수 있어, 임의 코드 실행으로 이어질 수 있습니다.
직접 만든 데이터나 검증된 안전한 출처에서 받은 데이터에만 pickle.loads()를 사용하십시오.
고급 팁: 사용자 정의 직렬화
특수한 처리가 필요한 클래스는 __reduce__(또는 __reduce_ex__) 메서드를 구현합니다. 이 메서드는 pickle에게 객체를 어떻게 직렬화하고 재구성할지 정확히 알려줍니다.
class MyClass:
def __init__(self, value):
self.value = value
def __reduce__(self):
# 객체를 재생성하기 위한 호출 가능한 객체와 인자를 반환
return (self.__class__, (self.value,))
요약
- 피클링 = 파이썬 전용 바이트 스트림으로의 직렬화.
- 언피클링 = 파이썬 객체로의 역직렬화.
- 경계: 파일, 소켓, DB 연결 등 OS에 바인딩된 리소스는 피클링할 수 없음.
- 대안: 언어 간 데이터 교환을 위해 JSON 사용.
- 보안 규칙: 신뢰할 수 있는 데이터만 언피클링.
- 커스터마이징:
__reduce__를 정의해 직렬화 과정을 세밀하게 제어.