대항해시대 게임 운영을 한다고 가정하고, 사용자들의 패턴을 분석해 모든 행동을 기록하여 MongoDB에 적재해보자
<aside> 1️⃣ 하나의 user 도큐먼트에 해당 사용자의 로그 정보를 모두 저장
// DB 생성
> use game
// 사용자 추가
> db.users.insertOne({
_id: "tom",
joinDate: new Date(),
server: "Sevilla",
job: "merchant",
logInfo: []
})
// 추가된 사용자 확인
> db.users.find()
[
{
_id: 'tom',
joinDate: ISODate("2023-05-16T15:09:18.688Z"),
server: 'Sevilla',
job: 'merchant',
logInfo: []
}
]
// 로그 데이터 생성
> log = {
loginTime: new Date(),
visits: [],
sails: [],
trades: [],
battles: [],
quests: [],
fishings: [],
gambles: [],
castings: [],
farmings: []
}
> log.visits.push({
... location: "Barcelona",
... time: new Date()
... })
> log.visits.push({
... location: "Sevilla",
... time: new Date()
... })
> log.visits.push({
... location: "London",
... time: new Date()
... })
> log.trades.push({
... item: "Musket",
... qty: 50,
... price: 1800
... })
> log.trades.push({
... item: "Musket",
... qty: -50,
... price: 2300
... })
> log.quests.push({
... name: "Cave Invenstigation",
... reward: 50000
... })
// 사용자 로그 업데이트
> db.users.updateOne(
... { _id: "tom" },
... {
... $addToSet: {
... logInfo: log
... }
... }
... )
// 업데이트된 사용자 데이터 확인
> db.users.find()
[
{
_id: 'tom',
joinDate: ISODate("2023-05-16T15:09:18.688Z"),
server: 'Sevilla',
job: 'merchant',
logInfo: [
{
loginTime: ISODate("2023-05-16T15:10:20.639Z"),
visits: [
{
location: 'Barcelona',
time: ISODate("2023-05-16T15:10:52.600Z")
},
{
location: 'Sevilla',
time: ISODate("2023-05-16T15:10:52.618Z")
},
{
location: 'London',
time: ISODate("2023-05-16T15:11:00.073Z")
}
],
sails: [],
trades: [
{ item: 'Musket', qty: 50, price: 1800 },
{ item: 'Musket', qty: -50, price: 2300 }
],
battles: [],
quests: [ { name: 'Cave Invenstigation', reward: 50000 } ],
fishings: [],
gambles: [],
castings: [],
farmings: []
}
]
}
]
⚠️ user 도큐먼트 하나에 logs가 계속 쌓이는 구조 → 하나의 컬렉션 안에 배열이 너무 커지는 문제 발생
</aside>
<aside> 2️⃣ 로그 컬렉션에 사용자 정보를 필드로 저장
// 기존 컬렉션 삭제
> db.users.find()
> db.users.drop()
// 로그 데이터 재구성 및 logs 컬렉션에 추가
> log.user = "tom"
> log.logoutTime = new Date()
> db.logs.insertOne(log)
// logs 컬렉션 데이터 확인
> db.logs.find()
[
{
_id: ObjectId("64639e0e744ecd33df31c41e"),
loginTime: ISODate("2023-05-16T15:10:20.639Z"),
visits: [
{
location: 'Barcelona',
time: ISODate("2023-05-16T15:10:52.600Z")
},
{
location: 'Sevilla',
time: ISODate("2023-05-16T15:10:52.618Z")
},
{ location: 'London', time: ISODate("2023-05-16T15:11:00.073Z") }
],
sails: [],
trades: [
{ item: 'Musket', qty: 50, price: 1800 },
{ item: 'Musket', qty: -50, price: 2300 }
],
battles: [],
quests: [ { name: 'Cave Invenstigation', reward: 50000 } ],
fishings: [],
gambles: [],
castings: [],
farmings: [],
user: 'tom',
logoutTime: ISODate("2023-05-16T15:15:20.033Z")
}
]
⚠️ 문제점 : 필드가 너무 많아서 logs에 인덱스를 생성하기 어려워짐 (모든 필드에 대해 인덱스를 생성해야 함 → 관리가 어려움)
</aside>
<aside> 3️⃣ Attribute Pattern을 사용한 모델링
<aside> ✅ Attribute Pattern
key-value 형태로 묶어서 배열에 넣어주고, 하나의 인덱스만 생성해서 관리하는 패턴
Attrubute Pattern 예제
</aside>
// 기존 컬렉션 삭제
> db.users.find()
> db.users.drop()
// 저장할 데이터 구성
> date = new Date()
> log = {
... user: 'tom',
... loginTime: date,
... logoutTime: new Date(),
... actions: [
... { action: "visit", value: "Barcelona", time: date },
... { action: "visit", value: "Sevilla", time: date },
... { action: "visit", value: "London", time: date },
... { action: "trade", value: "Musket", type: "buy", qty: 50, price: 1800 },
... { action: "trade", value: "Musket", type: "sell", qty: 50, price: 2300 },
... { action: "quest", value: "Cave Investigation", reward: 50000, status: "In Progress" }
... ]
... }
// 로그 데이터 저장
> db.logs.insertOne(log)
// 저장한 로그 데이터 확인
> db.logs.find()
[
{
_id: ObjectId("6463a49b744ecd33df31c41f"),
user: 'tom',
loginTime: ISODate("2023-05-16T15:19:05.073Z"),
logoutTime: ISODate("2023-05-16T15:19:35.845Z"),
actions: [
{
action: 'visit',
value: 'Barcelona',
time: ISODate("2023-05-16T15:19:05.073Z")
},
{
action: 'visit',
value: 'Sevilla',
time: ISODate("2023-05-16T15:19:05.073Z")
},
{
action: 'visit',
value: 'London',
time: ISODate("2023-05-16T15:19:05.073Z")
},
{
action: 'trade',
value: 'Musket',
type: 'buy',
qty: 50,
price: 1800
},
{
action: 'trade',
value: 'Musket',
type: 'sell',
qty: 50,
price: 2300
},
{
action: 'quest',
value: 'Cave Investigation',
reward: 50000,
status: 'In Progress'
}
]
}
]
✔️ 장점: 인덱스에 대한 관리가 매우 간편해짐
</aside>