기본 기능
최초 랩 파일 만들기
지금부터는 가상의 모바일 게임 foo 를 위한 로그를 설계하는 예제를
통해 로그랩의 활용법을 하나씩 살펴보겠다.
로그랩은 랩(lab) 파일 로 불리는 JSON 파일에 로그 명세를 기술하는
것으로 로그를 설계한다. 랩 파일은 로그랩에서 제공하는 JSON 스키마 형식에
맞추어 작성하며, 확장자는 .lab.json 을 사용한다. VS
Code 등 JSON 스키마를 지원하는
에디터를 이용하면 인텔리센스 (IntelliSense) 기능이 지원되어 편집에
용이할 것이다.
먼저 빈 작업 디렉토리를 하나를 만들고, 에디터를 사용해 아래와 같은
내용으로 foo.lab.json 파일을 만들자.
{
"domain": {
"name": "foo",
"desc": "최고의 모바일 게임"
},
"events": {
"Login": {
"desc": "계정 로그인"
}
}
}
처음으로 나오는 domain 최상단 요소는 랩 파일의 도메인 (domain)
정보 를 기술하는데 사용한다. 도메인 요소는 랩 파일이 어떤 서비스를
위한 것인가에 대한 정보를 담는데, 모든 랩 파일에 꼭 있어야 하는 필수
요소이다.
위 예처럼 domain 요소 아래 도메인 이름 name 및 설명 desc
요소를 입력하면 되는데, 도메인 이름은 나중에 해당 랩 파일을 식별하는
용도로 사용되기에, 알파벳 소문자와 숫자, 그리고 밑줄 문자 _ 만을
사용해 식별 가능한 범위에서 간략하게 기술한다.
참고
관용적으로 랩 파일의 이름과 도메인 이름을 같게 하는 것을 추천한다.
로그랩에서는 로깅의 대상이 되는 각 사건을 이벤트 (event) 라고
하는데, events 최상단 요소에 하나 이상의 이벤트를 기술할 수 있다.
예제에서는 계정 로그인 이벤트를 위한 Login 을 만들었다. 그 아래의
desc 요소는 이벤트에 대한 설명을 위한 것이다. 이것은 필수 요소는
아니지만 문서화를 위해서는 꼭 필요하다.
각 이벤트에는 관련된 하나 이상의 필드 (field) 를 기술할 수 있는데, 예에서는 아직 필드 정보는 없다.
이제 작업 디텍토리에서 loglab 의 show 명령을 사용할텐데, 먼저
--help 옵션을 통해 도움말을 살펴보자.
$ loglab show --help
Usage: loglab show [OPTIONS] LABFILE
로그 구성 요소 출력.
Options:
-c, --custom-type 커스텀 타입 그대로 출력
-n, --name TEXT 출력할 요소 이름 패턴
-k, --keep-text 긴 문자열 그대로 출력
-l, --lang TEXT 로그랩 메시지 언어
--help Show this message and exit.
show 는 첫 인자로 랩 파일 LABFILE 을 필요로 하는 것을 알 수
있다. 아래와 같이 실행한다.
$ loglab show foo.lab.json
Domain : foo
Description : 최고의 모바일 게임
Event : Login
Description : 계정 로그인
+----------+----------+---------------+
| Field | Type | Description |
|----------+----------+---------------|
| DateTime | datetime | 이벤트 일시 |
+----------+----------+---------------+
show 명령은 랩 파일을 참고해 로그의 구성 요소들을 텍스트로 출력한다.
랩 파일에 기술된 도메인 정보, 이벤트 이름과 설명, 그리고 테이블 형식의
필드 정보가 출력되는 것을 확인할 수 있다.
아직 Login 이벤트에는 아무런 필드를 명시하지 않았음에도 DateTime
이라는 필드가 보인다. 이것은 모든 로그에 이벤트의 발생 일시는 꼭
필요하기에 로그랩에서 자동으로 생성 해준 것이다.
참고
앞의 Login 과 DateTime 에서 알 수 있듯, 로그랩의 이벤트 및
필드 이름은 대문자로 시작 후 다음 합성어의 시작을 대문자로 (Pascal
Case) 하고, 타입 이름은 소문자로 시작 후 다음 합성어의 시작을
대문자로 (Camel Case) 하는 것을 권장한다.
스키마와 도메인 정보 지정하기
복잡한 구조의 JSON 파일을 편집하다보면 어떤 내용이 기술될 수 있는지
기억하기 어렵고 틀리기 쉽다. 이런 경우 해당 형식의 JSON
스키마 가 있다면 편리하다. 로그랩에서는 랩
파일을 위한 JSON 스키마를 제공한다. foo.lab.json 파일에 다음처럼
$schema 요소를 추가해 보자.
{
"$schema": "https://raw.githubusercontent.com/haje01/loglab/master/loglab/schema/lab.schema.json",
"domain": {
"name": "foo",
"desc": "최고의 모바일 게임"
},
"events": {
"Login": {
"desc": "계정 로그인"
}
}
}
사용하는 에디터가 VS Code 처럼 JSON 스키마를 지원한다면, 이제 아래와 같은 가이드를 볼 수 있을 것이다.
스키마 가이드
또한, 문맥에 맞지 않는 요소나 틀린 철자 등도 찾아주기에 편리하다.
필드의 추가
참고
지금부터는 공간을 아끼기 위해 표준 출력 및 JSON 파일의 맥락상 동일한
부분은 ... 표시 후 생략하도록 하겠다.
필드는 이벤트에 관한 상세 정보를 표현한다. 기본으로 생성된 DateTime
외 필드를 추가해보자.
필드는 이벤트 요소 아래 fields 리스트에 기술하는데, 각 필드는 3 개
항목을 가지는 리스트로 표현한다. 아래와 같은 형식이다.
{
// ...
"fields": [
[필드_이름, 필드_타입, 필드_설명]
]
// ...
}
로그랩에서 사용할 수 있는 필드의 기본 타입은 다음과 같다.
string: 문자열integer: 정수number: 실수 (float과 일치)boolean: 불린 (true또는false)datetime: 일시(날짜+시간). RFC3339 를 따른다.
참고
RFC3399 일시의 예로 2021-09-12T23:41:50.52Z (또는 2021-09-12T23:41:50.52+00:00 도 가능) 은 UTC 기준 2021년
9월 12일 23시 41분 50.52 초이며, 2021-09-14T16:39:57+09:00 은
한국 표준시 (KST) 로 2021년 9월 14일 16시 39분 57초 이다.
예제의 Login 이벤트의 경우 로그인한 계정 ID 정보가 필요할 것이다.
아래와 같이 AcntId 필드를 추가한다.
{
// ...
"events": {
"Login": {
"desc": "계정 로그인",
"fields": [
["AcntId", "integer", "계정 ID"]
]
}
}
참고
이 글에서 필드 이름의 접미사 아이디 (Id) 가 붙은 것은 임의의 값으로 특정 개체를 가리키는 용도로, 코드 (Cd) 는 미리 정해진 값으로 특정 범주값을 가리키는데 사용하겠다.
이제 show 명령을 내려보면,
$ loglab show foo.lab.json
# ...
Event : Login
Description : 계정 로그인
+----------+----------+---------------+
| Field | Type | Description |
|----------+----------+---------------|
| DateTime | datetime | 이벤트 일시 |
| AcntId | integer | 계정 ID |
+----------+----------+---------------+
AcntId 필드가 추가된 것을 확인할 수 있다.
참고
DateTime 과 Event 는 로그랩이 직접 사용하는 필드이기에,
사용자는 이 이름을 피해서 자신의 필드를 만들도록 하자.
새로운 이벤트의 추가
계정의 로그인 이벤트가 있다면, 로그아웃도 있어야 하지 않을까? 다음과 같이 추가해보자.
{
// ...
"events": {
"Login": {
"desc": "계정 로그인",
"fields": [
["AcntId", "integer", "계정 ID"]
]
},
"Logout": {
"desc": "계정 로그아웃",
"fields": [
["AcntId", "integer", "계정 ID"]
]
}
}
}
show 명령을 내려보면,
$ loglab show foo.lab.json
# ...
Event : Login
Description : 계정 로그인
+----------+----------+---------------+
| Field | Type | Description |
|----------+----------+---------------|
| DateTime | datetime | 이벤트 일시 |
| AcntId | integer | 계정 ID |
+----------+----------+---------------+
Event : Logout
Description : 계정 로그아웃
+----------+----------+---------------+
| Field | Type | Description |
|----------+----------+---------------|
| DateTime | datetime | 이벤트 일시 |
| AcntId | integer | 계정 ID |
+----------+----------+---------------+
Logout 이 잘 추가된 것을 알 수 있다.
그런데 로그인, 로그아웃 이벤트 모두 AcntId 필드을 가지고 있다.
앞으로 계정에 관한 다른 이벤트를 만든다면 거기에도 모두 이 필드를 만들어
주어야 할 것이다. 반복적인 작업을 방지할 수 없을까?
믹스인을 활용한 리팩토링
믹스인 (mixin) 은 다른 요소의 필드를 가져다 쓰는 방법이다. 믹스인을 활용하면 다양한 이벤트에서 공통적으로 필요한 필드를 공유할 수 있다.
믹스인을 효율적으로 하기 위해서는 공통 필드를 추출하여 베이스 (base)
로 만드는 것을 권장한다. 베이스는 이벤트와 비슷하나, 그 자체로 로그에
직접 출력되지는 않고, 이벤트나 다른 베이스에서 참조되기 위한 용도이다.
베이스는 랩 파일의 bases 최상단 요소에 다음과 같은 형식으로
정의한다.
{
// ...
"bases": {
"Account": {
"desc": "계정 정보",
"fields": [
["AcntId", "integer", "계정 ID"]
]
}
},
"events": {
"Login": {
"desc": "계정 로그인",
"mixins": ["bases.Account"]
},
"Logout": {
"desc": "계정 로그아웃",
"mixins": ["bases.Account"]
}
}
}
위 예에서 bases 아래 Account 라는 베이스를 만들었다. 여기에 계정
관련 공용 필드를 기술하면 되는데, 아직은 AcntId 필드만 있다. 기존
Login, Logout 이벤트의 fields 요소는 제거하고, 대신
mixin 리스트를 만든 후 bases.Account 를 기입한다. 이처럼
믹스인할 베이스 요소는 bases.베이스_이름 식의 경로로 지정한다.
참고
이벤트는 베이스 뿐만 아니라 다른 이벤트도 믹스인할 수 있다. 이 경우
events.이벤트_이름 형식으로 경로를 지정하면 된다. 그러나,
베이스는 이벤트를 믹스인할 수 없다.
이제 각 이벤트는 Account 베이스에 등록된 필드를 모두 가져다 쓰게
된다. show 명령으로 확인하면,
$ loglab show foo.lab.json
# ...
Event : Login
Description : 계정 로그인
+----------+----------+---------------+
| Field | Type | Description |
|----------+----------+---------------|
| DateTime | datetime | 이벤트 일시 |
| AcntId | integer | 계정 ID |
+----------+----------+---------------+
Event : Logout
Description : 계정 로그아웃
+----------+----------+---------------+
| Field | Type | Description |
|----------+----------+---------------|
| DateTime | datetime | 이벤트 일시 |
| AcntId | integer | 계정 ID |
+----------+----------+---------------+
두 이벤트에 fields 항목이 없지만 Account 베이스의 필드를 가져와
앞에서와 같은 결과가 나오는 것을 알 수 있다. 이런 식으로 서로 다른
요소에서 공통 요소를 추출해 공유하는 것을 리팩토링 (refactoring)
이라고 한다.
참고
리팩토링의 필요성
예를 들어 계정에 관한 필드를 갖는 로그 이벤트가 30 개 있다고 하자. 어느날 계정 정보에 필드 하나가 추가되어야 한다는 요청이 들어오면, 리팩토링이 되지 않은 경우 30 개나 되는 이벤트를 일일이 찾아 수정해야 할 것이다. 미리 계정 관련 베이스를 만들어 리팩토링 해두었다면, 단 한 번의 수정으로 모든 이벤트에 추가 필드를 적용할 수 있을 것이다.
참고로, 위에서 알 수 있듯 show 명령의 결과에 베이스는 출력되지
않는다. 베이스는 참조되어 사용되어질 뿐, 그 자체로 이벤트는 아니기
때문이다.
게임관련 이벤트와 필드의 추가
이제 기본적인 랩 파일 작성 방법을 알게 되었다. 지금까지 배운 것을 활용하여 실제 게임에서 발생할 수 있는 다양한 이벤트와 필드를 추가해보자.
서버 번호 필드
게임 서비스내 대부분 이벤트는 특정 서버에서 발생하기 마련이다. 몇 번
서버의 이벤트인지 표시하기 위해 다음처럼 Server 베이스를 추가한다.
{
// ...
"bases": {
// ...
"Server": {
"desc": "서버 정보",
"fields": [
["ServerNo", "integer", "서버 번호"]
]
}
},
// ...
}
Login, Logout 이벤트도 당연히 특정 서버에 관한 것이기에, 다음과
같이 믹스인에 추가한다.
{
// ...
"events": {
"Login": {
"desc": "계정 로그인",
"mixins": ["bases.Account", "bases.Server"]
},
"Logout": {
"desc": "계정 로그아웃",
"mixins": ["bases.Account", "bases.Server"]
}
}
}
show 명령으로 두 이벤트에 ServerNo 필드가 추가된 것을 확인할 수
있다.
$ loglab show foo.lab.json
# ...
Event : Login
Description : 계정 로그인
+----------+----------+---------------+
| Field | Type | Description |
|----------+----------+---------------|
| DateTime | datetime | 이벤트 일시 |
| AcntId | integer | 계정 ID |
| ServerNo | integer | 서버 번호 |
+----------+----------+---------------+
Event : Logout
Description : 계정 로그아웃
+----------+----------+---------------+
| Field | Type | Description |
|----------+----------+---------------|
| DateTime | datetime | 이벤트 일시 |
| AcntId | integer | 계정 ID |
| ServerNo | integer | 서버 번호 |
+----------+----------+---------------+
믹스인의 처리 순서
믹스인은 mixin 리스트에 등장하는 순서대로 수행되며, 앞 항목과 뒤
항목에 일치하는 필드가 있다면 뒤의 것으로 덮어쓰게 된다. 이것을 이용하면
특정 필드의 출력 순서를 조정하거나 필드를 재정의 할 수 있다. 예를 들어
위 예에서 ServerNo 필드가 AcntId 보다 먼저 나오게 하고 싶다면
mixin 리스트의 항목 순서를 다음과 같이 바꿔주면 된다.
{
// ...
"Login": {
"desc": "계정 로그인",
"mixins": ["bases.Server", "bases.Account"]
},
// ...
show 결과는 다음과 같다.
$ loglab show foo.lab.json
# ...
Event : Login
Description : 계정 로그인
+----------+----------+---------------+
| Field | Type | Description |
|----------+----------+---------------|
| DateTime | datetime | 이벤트 일시 |
| ServerNo | integer | 서버 번호 |
| AcntId | integer | 계정 ID |
+----------+----------+---------------+
# ...
베이스간 믹스인
베이스는 다른 베이스를 믹스인할 수 있다. 예제에서 계정에 관한 이벤트가
항상 서버 단위로 일어난다면, 아래와 같이Account 베이스에
Server 베이스를 믹스인할 수 있다.
{
// ...
"bases": {
"Server": {
"desc": "서버 이벤트",
"fields": [
["ServerNo", "integer", "서버 번호"]
]
},
"Account": {
"desc": "계정 이벤트",
"mixins": ["bases.Server"],
"fields": [
["AcntId", "integer", "계정 ID"]
]
}
},
"events": {
"Login": {
"desc": "계정 로그인",
"mixins": ["bases.Account"]
},
"Logout": {
"desc": "계정 로그아웃",
"mixins": ["bases.Account"]
}
}
}
이제 Account 베이스 자체가 Server 를 믹스인하기에, Login,
Logout 이벤트는 Account 베이스만 믹스인하면 된다.
참고
bases 나 events 내 요소들의 기술 순서는 중요하지 않다. 위의
경우 Account 가 Server 앞에 오더라도 문제가 없다.
옵션 필드
지금까지 등장한 모든 필드들은 기본적으로 로그 이벤트에 반드시 나와야
하는 필수 (required) 필드들이었다. 만약 나올 수도 있고 안 나와도
괜찮은 필드가 있다면, 옵션 (option) 으로 만들 수 있다. 그것은 필드
리스트의 4번째 항목에 true 또는 false 를 지정하여 만들 수 있다
(false 인 경우 기본값이기에 굳이 기술할 필요가 없겠다).
예를 들어 Logout 이벤트에서, 로그인 이후 플레이한 시간을 선택적으로
포함하게 하려면 다음과 같이 할 수 있다.
{
// ...
"events": {
// ...
"Logout": {
"desc": "계정 로그아웃",
"mixins": ["bases.Account"],
"fields": [
["PlayTime", "number", "플레이 시간 (초)", true]
]
},
// ...
}
show 명령으로 보면 아래와 같다.
$ loglab show foo.lab.json
# ...
Event : Logout
Description : 계정 로그아웃
+----------+----------+------------------+------------+
| Field | Type | Description | Optional |
|----------+----------+------------------+------------|
| DateTime | datetime | 이벤트 일시 | |
| ServerNo | integer | 서버 번호 | |
| AcntId | integer | 계정 ID | |
| PlayTime | number | 플레이 시간 (초) | true |
+----------+----------+------------------+------------+
# ...
지금까지 없던 Optional 컬럼이 보이고 PlayTime 필드만이
true로 표시된다. 또한 여기에서 mixin 과 fields 를 다
사용하고 있는데, 이런 식으로 mixin 만으로 부족할 때 fields 를
통해 필드를 추가할 수 있다. 만약 이 과정에서 중복 필드가 있나면 나중에
나오는 것이 남게 된다.
캐릭터 관련 이벤트
보통 온라인 게임에서 실제 플레이를 하는 것은 계정이 아니라 계정에 속한
캐릭터이다. 이에 캐릭터 관련 이벤트를 추가해보겠다 (일반적으로 한 계정은
하나 이상의 캐릭터를 소유하고 선택하여 플레이한다). 다음과 같이
Character 베이스를 추가한다.
{
// ...
"bases": {
// ...
"Character": {
"desc": "캐릭터 정보",
"mixins": ["bases.Account"],
"fields": [
["CharId", "integer", "캐릭터 ID"]
]
}
},
// ...
}
캐릭터는 자신이 속한 계정의 정보를 필요로 하기에 Account 를
믹스인하였다. 이제 Character 베이스를 이용해, 캐릭터의 로그인/아웃
이벤트를 추가하겠다.
{
// ...
"events": {
// ...
"CharLogin": {
"desc": "캐릭터 로그인",
"mixins": ["bases.Character"]
},
"CharLogout": {
"desc": "캐릭터 로그아웃",
"mixins": ["bases.Character", "events.Logout"]
}
}
}
CharLogin 은 캐릭터 베이스만을 사용해서 구현하였으나, CharLogout
은 캐릭터 베이스에 더해 계정 로그아웃 이벤트인 Logout 을 믹스인
해보았다. 이렇게 하면 중복되는 계정 정보 필드외 PlayTime 필드가
추가되게 된다.
show 명령으로 확인하면 CharLogin 과 CharLogout 이벤트를
확인할 수 있다.
$ loglab show foo.lab.json
# ...
Event : CharLogin
Description : 캐릭터 로그인
+----------+----------+---------------+
| Field | Type | Description |
|----------+----------+---------------|
| DateTime | datetime | 이벤트 일시 |
| ServerNo | integer | 서버 번호 |
| AcntId | integer | 계정 ID |
| CharId | integer | 캐릭터 ID |
+----------+----------+---------------+
Event : CharLogout
Description : 캐릭터 로그아웃
+----------+----------+------------------+------------+
| Field | Type | Description | Optional |
|----------+----------+------------------+------------|
| DateTime | datetime | 이벤트 일시 | |
| ServerNo | integer | 서버 번호 | |
| AcntId | integer | 계정 ID | |
| CharId | integer | 캐릭터 ID | |
| PlayTime | number | 플레이 시간 (초) | True |
+----------+----------+------------------+------------+
이렇게 이벤트는 필요에 따라 다른 이벤트를 믹스인하여 사용할 수 있다.
몬스터와 아이템
좀 더 실제 게임과 가깝게 하기 위해 몬스터와 아이템 관련 이벤트도 만들겠다. 우선, 다음과 같은 베이스를 추가한다.
{
// ...
"bases": {
// ...
"Position": {
"desc": "맵상의 위치 정보",
"fields": [
["MapCd", "integer", "맵 코드"],
["PosX", "number", "맵상 X 위치"],
["PosY", "number", "맵상 Y 위치"],
["PosZ", "number", "맵상 Z 위치"]
]
},
"Monster": {
"desc": "몬스터 정보",
"mixins": ["bases.Server"],
"fields": [
["MonsterCd", "integer", "몬스터 타입 코드"],
["MonsterId", "integer", "몬스터 개체 ID"]
]
}
// ...
}
Position 베이스는 이벤트가 맵상의 특정 위치에서 발생하는 경우를 위한
것이다. 몬스터를 잡거나, 아이템을 습득하는 등 게임내 많은 이벤트가
맵상의 위치에서 일어나기에 필요하다. Monster 베이스는 다양한 몬스터
이벤트를 위한 것이다. 몬스터도 서버 내에서만 존재할 수 있기에
bases.Server 를 믹스인하였다.
이 베이스들을 이용해 캐릭터가 몬스터를 잡은 경우의 이벤트 KillMonster 를 추가한다.
{
// ...
"events": {
// ...
"KillMonster": {
"desc": "몬스터를 잡음",
"mixins": ["bases.Character", "bases.Position", "bases.Monster"]
}
}
참고
Character 및 Monster 베이스 둘 다 Server 베이스를 가지고
있으나, 둘을 함께 믹스인하여도 같은 필드는 덮어 써지기에 문제는 없다.
아래는 show 의 결과이다.
$ loglab show foo.lab.json
# ...
Event : KillMonster
Description : 몬스터를 잡음
+-----------+----------+------------------+
| Field | Type | Description |
|-----------+----------+------------------|
| DateTime | datetime | 이벤트 일시 |
| ServerNo | integer | 서버 번호 |
| AcntId | integer | 계정 ID |
| CharId | integer | 캐릭터 ID |
| MapCd | integer | 맵 코드 |
| PosX | number | 맵상 X 위치 |
| PosY | number | 맵상 Y 위치 |
| PosZ | number | 맵상 Z 위치 |
| MonsterCd | integer | 몬스터 타입 코드 |
| MonsterId | integer | 몬스터 개체 ID |
+-----------+----------+------------------+
# ...
믹스인한 베이스의 정보, 즉 계정 및 캐릭터, 지도상의 위치, 몬스터 개체에 관한 정보들이 잘 결합된 것을 확인할 수 있다. 이런 식으로 베이스를 만들고 그것을 믹스인하는 것 만으로, 다양한 로그 이벤트를 쉽게 만들 수 있다.
이제 아이템 관련 베이스를 추가해보자.
{
// ...
"bases": {
// ...
"Item": {
"desc": "아이템 정보",
"fields": [
["ItemCd", "integer", "아이템 타입 코드"],
["ItemId", "integer", "아이템 개체 ID"]
]
}
// ...
}
이것을 이용해 몬스터가 아이템을 떨어뜨리는 이벤트를 만든다.
{
// ...
"events": {
// ...
"MonsterDropItem": {
"desc": "몬스터가 아이템을 떨어뜨림",
"mixins": ["bases.Monster", "bases.Position", "bases.Item"]
}
}
몬스터가 주체이기에 지금까지와는 달리 계정이나 캐릭터 베이스가
믹스인되지 않았다. show 의 결과는 다음과 같다.
$ loglab show foo.lab.json
# ...
Event : MonsterDropItem
Description : 몬스터가 아이템을 떨어뜨림
+-----------+----------+------------------+
| Field | Type | Description |
|-----------+----------+------------------|
| DateTime | datetime | 이벤트 일시 |
| ServerNo | integer | 서버 번호 |
| MonsterCd | integer | 몬스터 타입 코드 |
| MonsterId | integer | 몬스터 개체 ID |
| MapCd | integer | 맵 코드 |
| PosX | number | 맵상 X 위치 |
| PosY | number | 맵상 Y 위치 |
| PosZ | number | 맵상 Z 위치 |
| ItemCd | integer | 아이템 타입 코드 |
| ItemId | integer | 아이템 개체 ID |
+-----------+----------+------------------+
비슷하게 캐릭터의 아이템 습득 이벤트도 간단히 만들 수 있다.
{
// ...
"events": {
// ...
"GetItem": {
"desc": "캐릭터의 아이템 습득",
"mixins": ["bases.Character", "bases.Position", "bases.Item"]
}
}
show 의 결과는 아래와 같다.
$ loglab show
# ...
Event : GetItem
Description : 캐릭터의 아이템 습득
+----------+----------+------------------+
| Field | Type | Description |
|----------+----------+------------------|
| DateTime | datetime | 이벤트 일시 |
| ServerNo | integer | 서버 번호 |
| AcntId | integer | 계정 ID |
| CharId | integer | 캐릭터 ID |
| MapCd | integer | 맵 코드 |
| PosX | number | 맵상 X 위치 |
| PosY | number | 맵상 Y 위치 |
| PosZ | number | 맵상 Z 위치 |
| ItemCd | integer | 아이템 타입 코드 |
| ItemId | integer | 아이템 개체 ID |
+----------+----------+------------------+
# ...