기본 기능 =============== 최초 랩 파일 만들기 ------------------- 지금부터는 가상의 모바일 게임 ``foo`` 를 위한 로그를 설계하는 예제를 통해 로그랩의 활용법을 하나씩 살펴보겠다. 로그랩은 **랩(lab) 파일** 로 불리는 JSON 파일에 로그 명세를 기술하는 것으로 로그를 설계한다. 랩 파일은 로그랩에서 제공하는 JSON 스키마 형식에 맞추어 작성하며, 확장자는 ``.lab.json`` 을 사용한다. `VS Code `__ 등 JSON 스키마를 지원하는 에디터를 이용하면 인텔리센스 (IntelliSense) 기능이 지원되어 편집에 용이할 것이다. 먼저 빈 작업 디렉토리를 하나를 만들고, 에디터를 사용해 아래와 같은 내용으로 ``foo.lab.json`` 파일을 만들자. .. code:: json { "domain": { "name": "foo", "desc": "최고의 모바일 게임" }, "events": { "Login": { "desc": "계정 로그인" } } } 처음으로 나오는 ``domain`` 최상단 요소는 랩 파일의 **도메인 (domain) 정보** 를 기술하는데 사용한다. 도메인 요소는 랩 파일이 어떤 서비스를 위한 것인가에 대한 정보를 담는데, 모든 랩 파일에 꼭 있어야 하는 필수 요소이다. 위 예처럼 ``domain`` 요소 아래 도메인 이름 ``name`` 및 설명 ``desc`` 요소를 입력하면 되는데, 도메인 이름은 나중에 해당 랩 파일을 식별하는 용도로 사용되기에, 알파벳 소문자와 숫자, 그리고 밑줄 문자 ``_`` 만을 사용해 **식별 가능한 범위에서 간략하게 기술**\ 한다. .. note:: 관용적으로 랩 파일의 이름과 도메인 이름을 같게 하는 것을 추천한다. 로그랩에서는 로깅의 대상이 되는 각 사건을 **이벤트 (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`` 이라는 필드가 보인다. 이것은 모든 로그에 이벤트의 발생 일시는 꼭 필요하기에 **로그랩에서 자동으로 생성** 해준 것이다. .. note:: 앞의 ``Login`` 과 ``DateTime`` 에서 알 수 있듯, 로그랩의 이벤트 및 필드 이름은 대문자로 시작 후 다음 합성어의 시작을 대문자로 (Pascal Case) 하고, 타입 이름은 소문자로 시작 후 다음 합성어의 시작을 대문자로 (Camel Case) 하는 것을 권장한다. 스키마와 도메인 정보 지정하기 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 복잡한 구조의 JSON 파일을 편집하다보면 어떤 내용이 기술될 수 있는지 기억하기 어렵고 틀리기 쉽다. 이런 경우 해당 형식의 `JSON 스키마 `__ 가 있다면 편리하다. 로그랩에서는 랩 파일을 위한 JSON 스키마를 제공한다. ``foo.lab.json`` 파일에 다음처럼 ``$schema`` 요소를 추가해 보자. .. code:: json { "$schema": "https://raw.githubusercontent.com/haje01/loglab/master/loglab/schema/lab.schema.json", "domain": { "name": "foo", "desc": "최고의 모바일 게임" }, "events": { "Login": { "desc": "계정 로그인" } } } 사용하는 에디터가 VS Code 처럼 JSON 스키마를 지원한다면, 이제 아래와 같은 가이드를 볼 수 있을 것이다. .. figure:: _static/guide.png :alt: 스키마 가이드 스키마 가이드 또한, 문맥에 맞지 않는 요소나 틀린 철자 등도 찾아주기에 편리하다. 필드의 추가 ----------- .. note:: 지금부터는 공간을 아끼기 위해 표준 출력 및 JSON 파일의 맥락상 동일한 부분은 ``...`` 표시 후 생략하도록 하겠다. 필드는 이벤트에 관한 상세 정보를 표현한다. 기본으로 생성된 ``DateTime`` 외 필드를 추가해보자. 필드는 이벤트 요소 아래 ``fields`` 리스트에 기술하는데, 각 필드는 3 개 항목을 가지는 리스트로 표현한다. 아래와 같은 형식이다. .. code:: { // ... "fields": [ [필드_이름, 필드_타입, 필드_설명] ] // ... } 로그랩에서 사용할 수 있는 필드의 기본 타입은 다음과 같다. - ``string`` : 문자열 - ``integer``: 정수 - ``number`` : 실수 (``float`` 과 일치) - ``boolean`` : 불린 (``true`` 또는 ``false``) - ``datetime`` : 일시(날짜+시간). `RFC3339 `__ 를 따른다. .. note:: 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`` 필드를 추가한다. .. code:: json { // ... "events": { "Login": { "desc": "계정 로그인", "fields": [ ["AcntId", "integer", "계정 ID"] ] } } .. .. note:: 이 글에서 필드 이름의 접미사 **아이디 (Id)** 가 붙은 것은 임의의 값으로 특정 개체를 가리키는 용도로, **코드 (Cd)** 는 미리 정해진 값으로 특정 범주값을 가리키는데 사용하겠다. 이제 ``show`` 명령을 내려보면, :: $ loglab show foo.lab.json # ... Event : Login Description : 계정 로그인 +----------+----------+---------------+ | Field | Type | Description | |----------+----------+---------------| | DateTime | datetime | 이벤트 일시 | | AcntId | integer | 계정 ID | +----------+----------+---------------+ ``AcntId`` 필드가 추가된 것을 확인할 수 있다. .. note:: ``DateTime`` 과 ``Event`` 는 로그랩이 직접 사용하는 필드이기에, 사용자는 이 이름을 피해서 자신의 필드를 만들도록 하자. 새로운 이벤트의 추가 -------------------- 계정의 로그인 이벤트가 있다면, 로그아웃도 있어야 하지 않을까? 다음과 같이 추가해보자. .. code:: json { // ... "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`` 최상단 요소에 다음과 같은 형식으로 정의한다. .. code:: json { // ... "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.베이스_이름`` 식의 경로로 지정한다. .. note:: 이벤트는 베이스 뿐만 아니라 다른 이벤트도 믹스인할 수 있다. 이 경우 ``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)** 이라고 한다. .. note:: **리팩토링의 필요성** 예를 들어 계정에 관한 필드를 갖는 로그 이벤트가 30 개 있다고 하자. 어느날 계정 정보에 필드 하나가 추가되어야 한다는 요청이 들어오면, 리팩토링이 되지 않은 경우 30 개나 되는 이벤트를 일일이 찾아 수정해야 할 것이다. 미리 계정 관련 베이스를 만들어 리팩토링 해두었다면, 단 한 번의 수정으로 모든 이벤트에 추가 필드를 적용할 수 있을 것이다. 참고로, 위에서 알 수 있듯 ``show`` 명령의 결과에 베이스는 출력되지 않는다. 베이스는 참조되어 사용되어질 뿐, 그 자체로 이벤트는 아니기 때문이다. 게임관련 이벤트와 필드의 추가 ----------------------------- 이제 기본적인 랩 파일 작성 방법을 알게 되었다. 지금까지 배운 것을 활용하여 실제 게임에서 발생할 수 있는 다양한 이벤트와 필드를 추가해보자. 서버 번호 필드 ~~~~~~~~~~~~~~ 게임 서비스내 대부분 이벤트는 특정 서버에서 발생하기 마련이다. 몇 번 서버의 이벤트인지 표시하기 위해 다음처럼 ``Server`` 베이스를 추가한다. .. code:: json { // ... "bases": { // ... "Server": { "desc": "서버 정보", "fields": [ ["ServerNo", "integer", "서버 번호"] ] } }, // ... } ``Login``, ``Logout`` 이벤트도 당연히 특정 서버에 관한 것이기에, 다음과 같이 믹스인에 추가한다. .. code:: json { // ... "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`` 리스트의 항목 순서를 다음과 같이 바꿔주면 된다. .. code:: json { // ... "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`` 베이스를 믹스인할 수 있다. .. code:: json { // ... "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`` 베이스만 믹스인하면 된다. .. note:: ``bases`` 나 ``events`` 내 요소들의 기술 순서는 중요하지 않다. 위의 경우 ``Account`` 가 ``Server`` 앞에 오더라도 문제가 없다. 옵션 필드 ~~~~~~~~~ 지금까지 등장한 모든 필드들은 기본적으로 로그 이벤트에 반드시 나와야 하는 **필수 (required)** 필드들이었다. 만약 나올 수도 있고 안 나와도 괜찮은 필드가 있다면, **옵션 (option)** 으로 만들 수 있다. 그것은 필드 리스트의 4번째 항목에 ``true`` 또는 ``false`` 를 지정하여 만들 수 있다 (``false`` 인 경우 기본값이기에 굳이 기술할 필요가 없겠다). 예를 들어 ``Logout`` 이벤트에서, 로그인 이후 플레이한 시간을 선택적으로 포함하게 하려면 다음과 같이 할 수 있다. .. code:: json { // ... "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`` 베이스를 추가한다. .. code:: json { // ... "bases": { // ... "Character": { "desc": "캐릭터 정보", "mixins": ["bases.Account"], "fields": [ ["CharId", "integer", "캐릭터 ID"] ] } }, // ... } 캐릭터는 자신이 속한 계정의 정보를 필요로 하기에 ``Account`` 를 믹스인하였다. 이제 ``Character`` 베이스를 이용해, 캐릭터의 로그인/아웃 이벤트를 추가하겠다. .. code:: json { // ... "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 | +----------+----------+------------------+------------+ 이렇게 이벤트는 필요에 따라 다른 이벤트를 믹스인하여 사용할 수 있다. 몬스터와 아이템 ~~~~~~~~~~~~~~~ 좀 더 실제 게임과 가깝게 하기 위해 몬스터와 아이템 관련 이벤트도 만들겠다. 우선, 다음과 같은 베이스를 추가한다. .. code:: json { // ... "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`` 를 추가한다. .. code:: json { // ... "events": { // ... "KillMonster": { "desc": "몬스터를 잡음", "mixins": ["bases.Character", "bases.Position", "bases.Monster"] } } .. .. note:: ``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 | +-----------+----------+------------------+ # ... 믹스인한 베이스의 정보, 즉 계정 및 캐릭터, 지도상의 위치, 몬스터 개체에 관한 정보들이 잘 결합된 것을 확인할 수 있다. 이런 식으로 베이스를 만들고 그것을 믹스인하는 것 만으로, 다양한 로그 이벤트를 쉽게 만들 수 있다. 이제 아이템 관련 베이스를 추가해보자. .. code:: json { // ... "bases": { // ... "Item": { "desc": "아이템 정보", "fields": [ ["ItemCd", "integer", "아이템 타입 코드"], ["ItemId", "integer", "아이템 개체 ID"] ] } // ... } 이것을 이용해 몬스터가 아이템을 떨어뜨리는 이벤트를 만든다. .. code:: json { // ... "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 | +-----------+----------+------------------+ 비슷하게 캐릭터의 아이템 습득 이벤트도 간단히 만들 수 있다. .. code:: json { // ... "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 | +----------+----------+------------------+ # ...