12. ORM Part 2: 오브젝트 정의하기

게임 개발자는 소스 트리에서 src/object_model/ 디렉토리 아래 JSON 파일로 오브젝트 모델을 정의하게 됩니다. 이 때 다음과 같은 형태로 오브젝트의 이름과 속성을 지정합니다.

{
  "ObjectName": {
    "AttributeName": "Type Flags",
    "AttributeName": "Type Flags",
    ...
  },
  "ObjectName": {
    "AttributeName": "Type Flags",
    "AttributeName": "Type Flags",
    ...
  },
  ...
}

여기서 ObjectNameCharacterWeapon 등 모델의 이름이 됩니다. 그리고 AttributeNameTitle 이나 Level 처럼 속성의 이름이 됩니다.

Important

Object, Attribute 이름은 가급적 Camel 형태로 입력하세요.

그렇지 않으면 자동 생성되는 클래스 이름이나 메소드 이름이 아래처럼 안 이쁘게 나옵니다.

  • (나쁜 예) my_character 로 할 때 자동 생성되는 함수 이름: Getmy_character()
  • (나쁜 예) myCharacter 로 할 때 자동 생성되는 함수 이름: GetmyCharacter()
  • (좋은 예) MyCharacter 로 할 때 자동 생성되는 함수 이름: GetMyCharacter()

Tip

JSON 에는 // .../* ... */ 형태의 주석을 사용할 수 없지만, 아이펀 엔진의 ORM 정의 파일에느 이와 같은 주석이 가능합니다. 이는 개발 과정에서는 모델이나 필드에 코멘트를 남길 때 유용합니다.

12.1. Attribute 의 Type

Attribute 의 타입으로는 아이펀 엔진이 제공하는 기본 타입 혹은 다른 모델 이름이 가능하며, Array 나 Map 형태도 가능합니다.

12.1.1. 기본 타입

  • Bool: 참/거짓을 의미합니다.

  • Integer: 정수 값을 의미합니다.

  • Double: 실수 값을 의미합니다.

  • String(n): n 글자 짜리 문자열을 의미합니다.

    Important

    String(n) 의 경우 Byte 단위가 아니라 유니코드 글자 단위로 셉니다. 필요한 경우, util::GetUtf8Length() 로 글자 수를 확인할 수 있습니다.

    n 글자보다 더 긴 문자열을 저장하면 n글자 이후 부분을 자르고 저장합니다. 만일 명시적으로 특정 글자 수 이하로 잘라야될 때에는 util::TruncateUtf8() 함수를 이용하시면 됩니다.

    Important

    String 은 더 이상 지원되지 않으며 String(n) 을 쓰셔야 됩니다.

    만일 기존에 이미 만들어진 프로젝트에서 String 을 쓰고 있다면 StringString(n) 은 혼용될 수 없기 때문에 계속해서 String 을 쓰시거나 기존의 것들을 String(n) 으로 바꿔주세요.

 // 14 글자, 36 바이트 문자열
 std::string s = "다람쥐 헌 쳇바퀴에 타고파";
 BOOST_ASSERT(14 == util::GetUtf8Length(s));
 BOOST_ASSERT(36 == s.size());

 // 처음 세글자만 자르기
 std::string t = util::TruncateUtf8(s, 3);
 BOOST_ASSERT("다람쥐" == t);

12.1.2. 오브젝트 타입

기본 타입 외에 다른 모델 이름을 타입으로 쓸 수 있습니다. 이 경우 해당 속성은 오브젝트 타입을 갖게 됩니다. 해당 모델이 반드시 앞에 선언될 필요는 없습니다.

아래 예에서 MyChracter 속성은 Chracter 라는 오브젝트 타입이 됩니다.

"User": {
  "MyCharacter": "Chracter"
},
"Character": {
  "Hp": "Integer",
  "Mp": "Integer"
}

12.1.3. 배열 (Array)

타입 뒤에 [] 을 추가하면 배열 형태가 됩니다.

예를 들어 Integer[], String(12)[], Character[] 는 각각 기본타입으로 주어지는 정수 배열, 길이 12글자짜리 문자열 배열, 그리고 사용자가 지정한 Character 라는 타입의 배열을 의미합니다.

배열 타입의 속성은 C++/C# 클래스가 생성될 때에도 InsertAt() 이나 EraseAt() 과 같이 인덱스를 이용한 배열 작업을 지원합니다.

Important

배열의 경우 중간에 있는 내용을 삭제하거나 추가하게 되면 배열 재정렬 때문에 느려질 수 있습니다. 자세한 내용은 Using Array and Map 을 참고하세요.

12.1.4. 맵 (Map)

<KeyType, ValueType> 형태로 지정합니다.

KeyType 에는 기본 타입만 가능하며, ValueType 에는 기본 타입뿐만 아니라 오브젝트 타입도 가능합니다.

예를 들어 <Integer, String(4)><String(64), Item> 는 각각 정수를 Key 로 가지면서 길이 4짜리 문자열을 Value 로 갖는 맵 타입과, 길이 64짜리 문자열이 Key 이면서 Item 이 Value 인 맵 타입을 의미합니다.

12.2. Attribute 의 Flag

12.2.1. Key

해당 속성을 key 로 만듭니다. Key 는 중복 값이 허용되지 않으며, 한번 값이 정해지면 값을 변경할 수 없습니다. Key 를 이용해 Object 를 불러올 수 있습니다. MySQL 상에는 Non-Clustered IndexUnique Constraint 로 설정됩니다.

아래 예제는 Character 의 Name 을 Key 로 지정한 경우입니다.

"Character": {
    "Name": "String Key"
}

12.2.2. Foreign

속성의 타입으로 기본 타입이 아닌 다른 모델의 이름을 쓰는 경우 Foreign 으로 지정할 수 있습니다. 이렇게 하면 오브젝트를 DB 에서 읽어올 때, 해당 속성의 오브젝트까지 자동으로 읽는대신 오브젝트 ID 값만 읽어오고 필요할 때 해당 속성 오브젝트를 읽어옵니다.

예를 들어, 다음과 같이 Character 와 Weapon 이라는 모델이 있다고 해보겠습니다.

"Character": {
    "MyWeapon": "Weapon"
},
"Weapon": {
    "Durability: "Integer"
}

이 경우 Character 를 DB 에서 읽어오게 되면 Weapon 타입인 MyWeapon 도 같이 읽어오게 됩니다.

명확하게 소유 관계가 있는 경우 이렇게 자동으로 읽어오는 기능은 매우 편리합니다. 그러나 공유되는 오브젝트나 남의 소유의 오브젝트를 지칭하는 경우에는 이렇게 자동으로 읽어오게 되면 문제가 될 수 있습니다. 그런 때는 참조 의 의미로 Foreign 을 지정할 수 있습니다.

"Character": {
    "MyWeapon": "Weapon Foreign"
},
"Weapon": {
    "Durability: "Integer"
}

위와 같이 Foreign 으로 지정하는 경우 Character 를 읽어오더라도 MyWeapon 을 자동으로 읽어오지 않습니다. 대신 MyWeapon 의 오브젝트 ID 를 저장해두고 나중에 MyWeapon 을 DB 에서 읽어와야 되는 경우 활용할 수 있게 해줍니다.

Tip

배열이나 맵의 경우는 Item[] Foreign 이나 <Integer, Item> Foreign 처름 배열과 맵 정의 뒤에 Foreign 을 씁니다.

12.3. 모델 정의 예제

아래는 CharacterItem 이란 Object 를 정의한 예제입니다. 이후 이 챕터와 ORM 관련 챕터들은 이 오브젝트 모델을 가정합니다.

src/object_model/example.json

{
  "User": {
    "Id": "String(16) KEY",
    "MyCharacter": "Character"
  },

  "Character": {
    "Name": "String(16) Key",
    "Exp": "Integer",
    "Level": "Integer",
    "Hp": "Integer",
    "Mp": "Integer",

    "Inventory": "Item[]",

    "QuickSlot": "<Integer, Item>",
    "EquippedItem": "<String(9), Item>"
  },

  "Item": {
    "Type": "String(16)"
  }
}

약 20 줄 정도의 간단한 Object Modeling 작업으로 User, Character, Item 이라는 Object 구현이 모두 완료되었습니다. 이제 이 JSON 에 따라 자동 생성되는 클래스를 활용하면, Database 작업, Cache 구현, 분산처리 구현, 멀티스레딩 처리 등을 아이펀 엔진이 자동으로 처리합니다.