MongoDB에서 각 문서는 고유한 식별자(_id 필드)를 가지며, 기본값으로 BSON 라이브러리의 ObjectId 클래스가 사용됩니다. 이 글에서는 ObjectId의 구조, 특징, 그리고 파이썬에서의 실제 활용법을 설명합니다.
ObjectId의 역할과 장점
- 전역 고유성 보장: MongoDB 클러스터 내에서 모든 문서가 유일한 ID를 갖도록 설계되었습니다.
- 내장 타임스탬프: 문서 생성 시간을 ID 자체에 포함하므로 별도 필드를 저장하지 않아도 됩니다.
- 정렬 가능성: 생성 시간 순서대로 정렬되며, 인덱싱과 범위 쿼리에 유리합니다.
ObjectId의 내부 구조
12바이트(96비트)로 구성되며 각 부분의 의미는 다음과 같습니다:
+------------+---------------------+------------+
| 4바이트 | 5바이트 | 3바이트 |
+------------+---------------------+------------+
| 타임스탬프 | 머신 식별자+프로세스| 랜덤 카운터|
+------------+---------------------+------------+
- 처음 4바이트: UNIX 에포크(초) 기준 문서 생성 시각
- 다음 5바이트: 호스트 ID와 프로세스 ID 조합으로 충돌 방지
- 마지막 3바이트: 동일 초 내 고유성을 보장하는 증가 카운터
파이썬에서 ObjectId 다루기
1. 새 ObjectId 생성 및 기본 확인
from bson import ObjectId
new_id = ObjectId()
print(repr(new_id)) # ObjectId('...')
print(len(new_id.binary)) # 12 (바이트 크기 확인)
print(type(new_id)) # <class 'bson.objectid.ObjectId'>
2. 문자열과 ObjectId 변환
API로부터 24자리 헥스 문자열을 받았을 때 변환하는 패턴입니다:
from bson import ObjectId
hex_id = "64eddb89ad14e44f5c6c9a1e"
oid = ObjectId(hex_id)
print(oid) # 64eddb89ad14e44f5c6c9a1e
# 역변환 (ObjectId → str)
back_to_str = str(oid)
assert back_to_str == hex_id
3. MongoDB 쿼리에서 ObjectId 사용
PyMongo 또는 MongoEngine에서 _id로 문서를 찾을 때:
from bson import ObjectId
from pymongo import MongoClient
client = MongoClient()
db = client['my_database']
collection = db['users']
target = "64eddb89ad14e44f5c6c9a1e"
doc = collection.find_one({"_id": ObjectId(target)})
if doc:
print(doc['name'])
주의: _id 필드가 ObjectId 타입이라면 쿼리에도 동일 타입을 전달해야 합니다. 문자열로 전달하면 일치하지 않아 결과가 나오지 않습니다.
4. ObjectId에서 타임스탬프 추출
from bson import ObjectId
from datetime import datetime, timezone
oid = ObjectId("64eddb89ad14e44f5c6c9a1e")
utc_time = oid.generation_time
print(utc_time) # UTC 기준 datetime 객체
print(utc_time.replace(tzinfo=None)) # 타임존 정보 제거 가능
# 지역 시간으로 변환
local = utc_time.astimezone()
print(local.strftime("%Y-%m-%d %H:%M:%S"))
5. ObjectId 비교 연산
older = ObjectId("64eddb89ad14e44f5c6c9a1e")
newer = ObjectId("64eddb89ad14e44f5c6c9a1f")
if older < newer:
print("older는 newer보다 먼저 생성됨") # 참
# 리스트 정렬에도 활용 가능
ids = [newer, older, ObjectId()]
sorted_ids = sorted(ids)
고려사항 및 팁
- 문자열 ID와의 차이: ObjectId는 12바이트로 동일한 24자리 문자열(헥스)보다 저장 효율이 좋고 인덱싱 성능이 우수합니다. 시간 정렬도 자연스럽게 지원합니다.
- 타임존:
generation_time속성은 항상 UTC 기준입니다. 현지 시간이 필요하면astimezone()으로 변환하세요. - 직렬화 주의: JSON 응답에 ObjectId를 포함하려면 문자열로 변환(
str(oid))해야 합니다. JSON은 ObjectId 타입을 지원하지 않습니다.
실전 활용 예시: 웹 API 응답
from flask import Flask, jsonify
from bson import ObjectId
app = Flask(__name__)
@app.route('/user/')
def get_user(user_id):
# 문자열을 ObjectId로 변환
try:
oid = ObjectId(user_id)
except:
return {"error": "Invalid ID"}, 400
doc = collection.find_one({"_id": oid})
if not doc:
return {"error": "Not found"}, 404
# ObjectId를 문자열로 변환하여 반환
doc['_id'] = str(doc['_id'])
return jsonify(doc)