대항해시대 게임 운영을 한다고 가정하고, 사용자들의 패턴을 분석해 모든 행동을 기록하여 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 예제

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>