
BSON(Binary JSON)은 JSON(JavaScript Object Notation)을 이진(binary)형식으로 확장한 데이터 표현 방식
즉,
"JSON의 구조를 유지하면서도, 속도와 효율성을 위해 바이너리 형태로 표현한 포맷"
| 타입 | 설명 | 예시 |
|---|---|---|
ObjectId | MongoDB의 고유 ID (12바이트) | ObjectId("507f1f77bcf86cd799439011") |
Date | UTC 기준 날짜 | "ISODate("2025-11-07T00:00:00Z")" |
Binary | 순수 이진 데이터 | 파일, 이미지 등 |
Int32 / Int64 | 정수 타입 구분 | 32비트 / 64비트 |
Decimal128 | 고정소수점 | 금융 데이터에 사용 |
Regex | 정규표현식 | /pattern/i |
Timestamp | 내부 타임스탬프 | oplog 등에서 사용 |
| 비교 항목 | JSON | BSON |
|---|---|---|
| 형식 | 텍스트 기반 | 바이너리 기반 |
| 데이터 타입 | 문자열, 숫자, 불리언, 배열, 객체 등 | JSON + 확장 타입 (날짜, ObjectId, 64비트 정수, 이진 데이터 등) |
| 크기 | 크다 (문자열로 표현되므로) | 작다 (바이너리 표현) |
| 속도 | 느림 (파싱 필요) | 빠름 (직렬화/역직렬화가 효율적) |
| 가독성 | 높음 (사람이 읽을 수 있음) | 낮음 (기계용 포맷) |
| MongoDB 저장 방식 | 변환 후 저장 | 기본 저장 포맷 |
JSON 문서 예시
{
"name": "Alice",
"age": 25,
"skills": ["Go", "MongoDB", "Python"]
}
위 JSON 문서를 BSON으로 표현하면(바이너리 형태)
\x31\x00\x00\x00\x02name\x00\x06\x00\x00\x00Alice\x00\x10age\x00\x19\x00\x00\x00\x04skills\x00...
go get go.mongodb.org/mongo-driver/mongo
go get go.mongodb.org/mongo-driver/bson
import (
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
| Go 타입 | BSON 타입 | 설명 |
|---|---|---|
string | String | UTF-8 문자열 |
int32 | Int32 | 32비트 정수 |
int64 | Int64 | 64비트 정수 |
float64 | Double | 배정도 부동소수점 |
bool | Boolean | 불린값 |
[]byte | Binary | 바이너리 데이터 |
time.Time | DateTime | 날짜/시간 |
primitive.ObjectID | ObjectId | MongoDB ObjectID |
primitive.Decimal128 | Decimal128 | 고정소수점 |
// ObjectID 사용 예제
type User struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
Username string `bson:"username"`
Email string `bson:"email"`
}
// Decimal128 사용 예제 (금융 데이터)
type Product struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
Name string `bson:"name"`
Price primitive.Decimal128 `bson:"price"`
}
type Employee struct {
ID primitive.ObjectID `bson:"_id,omitempty"` // MongoDB _id 필드
EmployeeNo string `bson:"employee_no"` // 필드명 변경
Name string `bson:"name"` // 기본 매핑
Email string `bson:"email,omitempty"` // 빈값 시 생략
Password string `bson:"-"` // 저장하지 않음
Salary float64 `bson:"salary,truncate"` // 소수점 자르기
IsActive bool `bson:"is_active"` // 스네이크 케이스
CreatedAt time.Time `bson:"created_at"` // 날짜 필드
UpdatedAt time.Time `bson:"updated_at,omitempty"` // 업데이트 시에만 저장
}
| 옵션 | 설명 | 예제 |
|---|---|---|
omitempty | 빈 값일 때 필드 생략 | bson:"field,omitempty" |
minsize | 최소 크기로 저장 | bson:"field,minsize" |
truncate | 소수점 자르기 | bson:"field,truncate" |
- | 필드 무시 | bson:"-" |
// 구조체 정의
user := User{
ID: primitive.NewObjectID(),
Username: "john_doe",
Email: "john@example.com",
}
// BSON으로 변환
bsonData, err := bson.Marshal(user)
if err != nil {
log.Fatal(err)
}
// 또는 bson.D 사용
doc := bson.D{
{Key: "_id", Value: primitive.NewObjectID()},
{Key: "username", Value: "john_doe"},
{Key: "email", Value: "john@example.com"},
}
// BSON 데이터를 구조체로 변환
var user User
err := bson.Unmarshal(bsonData, &user)
if err != nil {
log.Fatal(err)
}
// MongoDB 문서에서 직접 변환
err = collection.FindOne(context.TODO(), bson.M{"_id": objectID}).Decode(&user)
if err != nil {
log.Fatal(err)
}
BSON 데이터의 직렬화(serialize) / 역직렬화(deserialize) 과정
bson.Marshal()
Go 데이터 -> BSON(Binary)
bson.Unmarshal()
BSON(Binary) -> Go 데이터
package main
import (
"fmt"
"go.mongodb.org/mongo-driver/bson"
)
type User struct {
Name string
Age int
}
func main() {
// ✅ Go 구조체 → BSON 변환
user := User{Name: "Alice", Age: 25}
data, err := bson.Marshal(user)
if err != nil {
panic(err)
}
fmt.Printf("BSON bytes: %v\n", data)
// ✅ BSON → Go 구조체 변환
var decoded User
err = bson.Unmarshal(data, &decoded)
if err != nil {
panic(err)
}
fmt.Printf("Decoded struct: %+v\n", decoded)
}
BSON bytes: [29 0 0 0 2 78 97 109 101 0 6 0 0 0 65 108 105 99 101 0 16 65 103 101 0 25 0 0 0 0]
Decoded struct: {Name:Alice Age:25}
특징
map[string]interfsce{}와 동일합니다.// 기본 필터링
filter := bson.M{
"status": "active",
"age": bson.M{"$gte": 18},
}
// 복잡한 쿼리
complexFilter := bson.M{
"$and": []bson.M{
{"status": "active"},
{"$or": []bson.M{
{"role": "admin"},
{"role": "manager"},
}},
},
}
// 업데이트 문서
update := bson.M{
"$set": bson.M{
"last_login": time.Now(),
"status": "online",
},
"$inc": bson.M{"login_count": 1},
}
특징
[]bson.E (Element의 슬라이스)로 구현합니다.// 인덱스 생성 (순서 중요)
indexKeys := bson.D{
{Key: "user_code", Value: 1}, // 오름차순
{Key: "created_at", Value: -1}, // 내림차순
}
// 집계 파이프라인 (단계 순서 중요)
pipeline := bson.D{
{Key: "$match", Value: bson.M{"status": "active"}},
{Key: "$lookup", Value: bson.M{
"from": "departments",
"localField": "dept_id",
"foreignField": "_id",
"as": "department",
}},
{Key: "$unwind", Value: "$department"},
}
// 정렬 (여러 필드 순서 중요)
sort := bson.D{
{Key: "priority", Value: -1},
{Key: "created_at", Value: 1},
}
특징
[]interface{}와 동일합니다.$in, $or 등의 연산자와 함께 사용합니다.// $in 연산자와 함께
filter := bson.M{
"status": bson.M{
"$in": bson.A{"active", "pending", "approved"},
},
}
// $or 연산자 배열
orConditions := bson.A{
bson.M{"role": "admin"},
bson.M{"role": "manager"},
bson.M{"permissions": bson.M{"$in": bson.A{"write", "admin"}}},
}
// 집계 파이프라인 배열
pipeline := bson.A{
bson.M{"$match": bson.M{"status": "active"}},
bson.M{"$group": bson.M{
"_id": "$department",
"count": bson.M{"$sum": 1},
}},
}
특징
// bson.E 직접 사용
element := bson.E{Key: "user_id", Value: "user123"}
// bson.D 구성
document := bson.D{
bson.E{Key: "name", Value: "홍길동"},
bson.E{Key: "age", Value: 30},
bson.E{Key: "email", Value: "hong@example.com"},
}
| 사용 상황 | 권장 타입 | 이유 |
|---|---|---|
| 일반적인 필터/쿼리 | bson.M | 직관적이고 사용하기 쉬움 |
| 인덱스 생성 | bson.D | 필드 순서가 성능에 영향 |
| 집계 파이프라인 | bson.D | 단계 순서가 중요 |
| 정렬 조건 | bson.D | 정렬 우선순위 순서 보장 |
| 배열 데이터 | bson.A | 배열 형태 데이터 표현 |
| MongoDB 명령어 | bson.D | 명령어 파라미터 순서 중요 |
// ERP 사용자 검색 - 다양한 방법
func SearchUsers() {
// 1. bson.M 사용 (일반적인 경우)
simpleFilter := bson.M{
"is_active": true,
"role": bson.M{"$in": bson.A{"admin", "manager"}},
}
// 2. bson.D 사용 (순서가 중요한 경우)
orderedFilter := bson.D{
{Key: "is_active", Value: true},
{Key: "role", Value: bson.M{"$in": bson.A{"admin", "manager"}}},
}
// 3. 복합 인덱스 생성 (bson.D 필수)
indexModel := mongo.IndexModel{
Keys: bson.D{
{Key: "company_id", Value: 1}, // 첫 번째 정렬 기준
{Key: "department_id", Value: 1}, // 두 번째 정렬 기준
{Key: "user_code", Value: 1}, // 세 번째 정렬 기준
},
}
// 4. 집계 파이프라인 (bson.D로 순서 보장)
pipeline := bson.D{
{Key: "$match", Value: bson.M{"is_active": true}},
{Key: "$lookup", Value: bson.M{
"from": "departments",
"localField": "department_id",
"foreignField": "_id",
"as": "dept_info",
}},
{Key: "$sort", Value: bson.D{
{Key: "dept_info.name", Value: 1},
{Key: "user_code", Value: 1},
}},
}
}