재화(코인)에 대한 기술 가이드

개요

배경

  • 게임에서 유저는 결제 또는 인게임 컨텐츠 및 운영 지급 등을 통해서 재화(보석, 골드, 젬과 같은 코인류)를 습득 및 사용할 수 있습니다.

    • 예) 각 결제 스토어에서 현금 결제, 게임 미션 달성 보상지급, 운영에서 출석 보상으로 우편함에 코인 지급

  • 이러한 재화는 유저가 어떤 방식으로 획득했는지를 구분해서 저장 및 관리 되어야하며 정책에서 정한 데로 차감되어야합니다.

    • 유저 환불이 필요할 때 유료 코인의 잔액만 확인이 가능해야 환불 금액을 산정 가능합니다.

    • 일본 자금 결제법에서는 일정 주기로 유료 잔액을 확인하고, 일정 금액 이상이라면 공탁금을 입금 해야 합니다.

    • 회계 규칙에 의해서 재화의 잔고 확인 및 증/차감 추적이 가능해야 합니다.

    • 일부 국가에서는 소비자 보호 등의 이유로 차감 순서가 틀릴 수 있습니다.

    • 정책 및 법은 변경될 수 있기에 개발은 가능하면 외부 변경에 유연하게 대응 할 수 있도록 설계 및 개발되면 좋습니다.

  • 게임 운영 과정에서 회수 기능이 필수입니다. 이때 유저의 코인 잔액은 음수가 될 수 있으니 꼭 감안하여 설계되어야 합니다.

    • 예) 게임 버그 등으로 재화 회수 → 이미 사용한 유저에게는 음수 값으로 차감(DB 음수 지원되는 sigined 타입 사용) → 유저가 결제 등으로 코인이 양수가 되면 정상 이용 가능(게임 내 UI에 음수 표시도 가능 해야함)

가이드의 한계

총 2가지의 코인 관리 DB 설계를 제공하니 게임 특성에 따라 알맞은 방법을 골라 참고하여 DB 설계 부탁 드립니다.

코인 충전 형태에 따른 코드 정의

코드 정의 표

  • 오타 등의 문제로 실수 가능성이 높은 상황에서는 가능하면 영문 코드를 사용

    • 예) API 통신에는 영문 코드로 통신하고 DB에 저장할 때는 맵핑 정의에 따른 숫자 포맷으로 저장해서 DB 인덱스 사이즈를 줄임

      • 정합성 보장 등을 위해서 PK나 유니크 인덱스에 숫자 포맷 값으로 인덱스를 생성해두시는게 좋습니다.

영문 코드
이름
숫자포맷
재무(회계) 유료 여부
일본 자금결제법상 유료 여부
설명

PAID

유료

1

유료

유료

유저가 실제 돈을 주고 구입한 재화로 보너스는 제외

PAID_BONUS

유료의 보너스

2

무료 (정책 변경될 수 있음)

무료

유저가 구입한 유료 코인에 덤으로 주는 보너스(자금결제법에서는 무료 처리해도 무방, 공탁금 줄일 수 있음)

PAID_INVEN

유료인데 상품 보관함 용

7

유료

무료

상품 보관함에서 임시로 지급되는 유료 재화

PAID_INVEN_BONUS

유료의 보너스인데 상품 보관함 용

8

유료

무료

상품 보관함에서 임시로 지급되는 덤 코인, 보너스 코인

FREE_BUY_PRODUCT

무료 상품 구매

14

무료

무료

회계 및 자금결제법상 무료로 관리되는 '권리' 형태의 상품을 구입 후 충전할 때 사용 ' - 예) 로그인하면 무료 코인을 주는 상품 구입 -> 게임에서는 내일 로그인하면 해당 타입으로 코인 충전(단, BM에 구매 즉시 지급되는 코인을 추가하고 해당 코인은 유료으로 처리)

FREE_AD

무료 광고

19

무료

무료

광고로 발생한 무료 재화. ' - 예)유니티 또는 구글 광고 SDK를 붙여서 BM을 준비할때. 광고를 보면 코인을 지급

FREE_OP

무료 운영

21

무료

무료

푸시 및 쿠폰, 로그인 보상 등의 이유로 내부 운영 목적상에서 충전된 코인

FREE_SVC

무료 서비스

25

무료

무료

게임내의 컨텐츠 요소로 발생한 충전된 무료 재화. ' - 예) 게임 던전 클리어시 무료 코인 지급

AUCTION_BIDDING

경매장 입찰용

31

무료

무료

경매장 입찰에 사용되는 임시 코인(낙찰 시점에 유/무료 처리 등 고민해야할 부분이 있음)

코인 차감 순서

  • 유료 코인을 먼저 차감합니다. 이용약관 10조 5항 3호에도 관련 내용이 명시되어 있습니다.(2023-11-28 기준)

    • 유료 코인을 먼저 차감하지 않는다면 게임 종료, 환불 등의 이슈가 발생하면 개발사 및 회사는 큰 손해를 입을 수 있습니다.

      • 예) 운영 성격의 코인이 많이 지급되었는데 무료을 먼저 사용하게 된다면, 유저의 유료(유료) 코인 잔액은 차감이 안되고 계속 남아있어서 모두 환불 금액에 포함되어야하는 문제가 발생

이용약관 유료 먼저 차감한다는 내용 발췌

③ 회원이 구매한 아이템은 획득 방식에 따라 유/무료 속성으로 구분됩니다. 회원이 유/무료 속성의 아이템을 모두 보유하고 있고 일부를 사용하였을 경우, 유료 속성이 우선 차감되고, 그 이후 무료 속성이 차감됩니다. 유료 속성만 보유 시에 차감 순서는 선입선출 방식(먼저 획득한 순서대로 차감되는 방식)에 따릅니다. 단, 사정에 따라 다르게 적용될 수 있으며 이 경우 서비스 내 또는 커뮤니티, 홈페이지 등을 통해 공지합니다.

코인 관리 DB 설계(참고용) - RDBMS

  • 아래 예제는 Mysql 기준으로 참고를 위해서 안내드리는 수준의 내용입니다.

  • 해당 row(또는 column)외 필요하신 추가 내용을 적용하셔도 무방합니다.

  • 다만, 꼭 관련 테이블들은 트랜잭션으로 묶어서 정합성에 문제가 생기지 않도록 해야합니다.

    • 이때 외부 API 호출 등 응답이 느린 로직이 있다면 트랜잭션 lock time이 길어지고, 다른 유저가 해당 데이터를 변경해야하는 로직이 있다면 데드락에 빠질 수 있으니 주의

(권장)확장성을 고려해서 충전 타입을 행 형태로 사용한 설계

  • 장점: 정책 또는 법규 등이 변경되어도 DB에 alter를 수행하지 않아도 되는 확장성의 장점

  • 단점: 유저의 코인 잔액을 조회하거나 변경할 때 여러 row를 읽어야하며 동시성에 대해서 더 잘 고민해야 함

    • 아래 샘플 내용은 참고를 위한 테이블 설계 및 쿼리입니다. 상황에 맞춰서 테이블명과 컬럼명 변경, 인덱스 조정 등이 진행되어도 괜찮습니다. 다만 증/차감 누락이나 중복 충전 등 크리티컬한 문제는 주의해주세요.

테이블 DDL 샘플

-- 유저의 코인 잔액 테이블(마스터 테이블)
CREATE TABLE `coin_balance`
(
    `player_id`   VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '플레이어ID(유저ID)',
    `coin_cd`     VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '코인 종류(GEM 등)',
    `charge_type` TINYINT UNSIGNED                                             NOT NULL COMMENT '코인의 충전타입 구분(유상,무상, 보너스 등)',
    `balance`     BIGINT                                                       NOT NULL COMMENT '코인의 현재 잔액(운영상의 이슈로 마이너스 잔액 지원 되어야함. 예. 버그 악용 유저의 재화 회수. 마이너스 통장 개념)',
    `created_at`  TIMESTAMP                                                    NOT NULL COMMENT '최초 저장 일시',
    `updated_at`  TIMESTAMP                                                    NOT NULL COMMENT '변경일시(최초에는 저장될때와 같은 일시로 저장)',
    PRIMARY KEY (`player_id`, `coin_cd`, `charge_type`)
) ENGINE = INNODB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_unicode_ci COMMENT ='유저의 현재 코인(재화)의 잔액 관리 테이블(charge_type이 늘어날 수 있음을 감안하여 설계)'
;
-- 유저별 일자별 코인 증/차감 데이터(단, 해당일에 증/차감이 발생하지 않은 유저의 데이터는 존재하지 않음)
CREATE TABLE `coin_balance_daily`
(
    `target_ymd`  DATE                                                         NOT NULL COMMENT '대상일',
    `player_id`   VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '플레이어ID(유저ID)',
    `coin_cd`     VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '코인 종류(GEM 등)',
    `charge_type` TINYINT UNSIGNED DEFAULT NULL COMMENT '코인의 충전타입 구분(유상,무상, 보너스 등)',
    `gain_coin`   BIGINT UNSIGNED  DEFAULT NULL COMMENT '충전으로 획득한 코인',
    `use_coin`    BIGINT UNSIGNED  DEFAULT NULL COMMENT '사용하게되어 소모된 코인',
    `balance`     BIGINT                                                       NOT NULL COMMENT '잔액',
    `created_at`  TIMESTAMP                                                    NOT NULL COMMENT '최초 저장일시',
    `updated_at`  TIMESTAMP                                                    NOT NULL COMMENT '변경일시(최초에는 저장될때와 같은 일시로 저장)',
    PRIMARY KEY (`target_ymd`, `player_id`, `coin_cd`)
) ENGINE = INNODB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_unicode_ci COMMENT ='유저의 일자별 코인 증차감 통계 데이터(재화 증차감 발생할 때 같은 트랜잭션내에 같이 저장)'
;
CREATE TABLE `coin_balance_hist`
(
    `hist_id`             BIGINT UNSIGNED                                                        NOT NULL AUTO_INCREMENT COMMENT 'PK',
    `req_id`              VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci          NOT NULL COMMENT '요청ID(중복 요청 방어 목적 및 추적용)',
    `player_id`           VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci           NOT NULL COMMENT '유저ID',
    `coin_cd`             VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci           NOT NULL COMMENT '코인 종류(GEM 등)',
    `charge_type`         TINYINT UNSIGNED                                                       NOT NULL COMMENT '코인의 충전타입 구분(유상,무상, 보너스 등)',
    `player_country`      VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci  DEFAULT NULL COMMENT '유저의 국가정보(국가별 법률 준수 목적으로 필요시 저장)',
    `amount`              INT                                                                    NOT NULL COMMENT '증감되는 변경 양',
    `balance`             BIGINT                                                                 NOT NULL COMMENT '재화 잔액',
    `balance_change_type` ENUM ('PLUS','MINUS') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '잔액 변경 종류(증/차감)',
    `reason`              VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci          NOT NULL COMMENT '증감되는 사유',
    `memo`                VARCHAR(300) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '메모(필요시 저장)',
    `hist_created_at`     TIMESTAMP                                                              NOT NULL COMMENT '이력저장일시',
    PRIMARY KEY (`hist_id`),
    UNIQUE KEY `IDX_reqId_coinCd_chargeType` (`req_id`, `coin_cd`, `charge_type`),
    KEY `IDX_playerId_histCreatedAt` (`player_id`, `hist_created_at`)
) ENGINE = INNODB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_unicode_ci COMMENT ='유저의 재화 잔액 변경 이력(N개의 charge type에 증/차감이 발생하면 N개의 이력이 한번에 insert될 수 있으며 이때의 req_id는 같은 값)'
;

참고용 쿼리 샘플

-- 유저의 특정 코인 총 잔액을 조회(charge_type을 제외하고 group by 후 sum)
SELECT coin_cd      AS coinCd,
       SUM(balance) AS balance
FROM coin_balance
WHERE player_id = 'playerId'
  AND coin_cd = 'GEM'
GROUP BY coin_cd
ORDER BY NULL
;
-- 차감을 위한 잔액 조회 쿼리(charge_type에 따른 차감 순서를 정의하고 싶으면, DB쿼리의 charge_type 필드를 대상으로 소팅해서 조회 후 처리하거나 로직에서 소팅해서 처리)
-- FIELD기능을 꼭 사용할 필요 없으며, 사용하게되면 하드코딩 안하도록 주의
SELECT charge_type AS chargetType,
       coin_cd     AS coinCd,
       balance     AS balance
FROM coin_balance
WHERE player_id = 'playerId'
  AND coin_cd = 'GEM'
ORDER BY FIELD(chargetType, 1, 2, 7, 8, 14, 19, 21, 25, 31) ASC
;
-- GEM이라는 코인에 대해서 유료 충전. DB로 동시성 이슈에 대한 방어 및 트랜잭션 처리를 명확하게 하기위해 upsert쿼리를 사용해서 insert(최초 추가) 또는 update(증가) 처리
INSERT INTO `coin_balance`
(`player_id`,
 `coin_cd`,
 `charge_type`,
 `balance`,
 `created_at`,
 `updated_at`)
VALUES ('playerId',
        'GEM',
        7,
        100,
        NOW(),
        NOW())
ON DUPLICATE KEY
    UPDATE balance    = balance + 100,
           updated_at = NOW()
;
-- 차감
-- update 쿼리로 차감하되 동시성 이슈를 막기 위해서 현재 잔액 조회를 다른 쿼리에서 진행하지않고, 같은 쿼리에서 처리
예) 
	UPDATE
			user_coin_balance_#{tableShardNo} 
		SET	
			balance = balance - (#{amount}),
      블라블라
   WHERE 블라블라      
-----
기타 쿼리는 생략

단순화된 열 형태의 설계

  • 여러 코인 충전 형태 중 필수인 일부 충전 방식에 대해서 컬럼 기반으로 관리

  • 1개의 행(DB row)에 충전 타입에 따른 컬럼을 개별적으로 추가해서 잔액을 관리

    • 유료, 무료, 유료 보너스 3가지 구분은 필수

    • 아래 샘플 내용은 참고를 위한 테이블 설계 및 쿼리입니다. 상황에 맞춰서 테이블명과 컬럼명 변경, 인덱스 조정 등이 진행되어도 괜찮습니다. 다만 증/차감 누락이나 중복 충전 등 크리티컬한 문제는 주의해주세요.

개발은 쉽지만 확장성이 부족한 열(컬럼) 잔액을 관리하는 DB 설계

테이블 DDL 샘플


CREATE TABLE `coin_balance_column_type`
(
    `player_id`          VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '플레이어ID(유저ID)',
    `coin_cd`            VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '코인 종류(GEM 등)',
    `balance_paid`       BIGINT                                                       NOT NULL COMMENT '유료 코인의 현재 잔액',
    `balance_paid_bonus` BIGINT                                                       NOT NULL COMMENT '유료 보너스 코인의 현재 잔액',
    `balance_free`       BIGINT                                                       NOT NULL COMMENT '무료 코인의 현재 잔액',
    `created_at`         TIMESTAMP                                                    NOT NULL COMMENT '최초 저장 일시',
    `updated_at`         TIMESTAMP                                                    NOT NULL COMMENT '변경일시(최초에는 저장될때와 같은 일시로 저장)',
    PRIMARY KEY (`player_id`, `coin_cd`)
) ENGINE = INNODB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_unicode_ci COMMENT ='유저의 현재 코인(재화)의 잔액 관리 테이블(간소화 버전)'
;

CREATE TABLE `coin_balance_daily_column_type`
(
    `target_ymd`         DATE                                                         NOT NULL COMMENT '대상일',
    `player_id`          VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '플레이어ID(유저ID)',
    `coin_cd`            VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '코인 종류(GEM 등)',
    `gain_coin`          BIGINT UNSIGNED DEFAULT NULL COMMENT '충전으로 획득한 코인',
    `use_coin`           BIGINT UNSIGNED DEFAULT NULL COMMENT '사용하게되어 소모된 코인',
    `balance_paid`       BIGINT                                                       NOT NULL COMMENT '유료 코인의 현재 잔액',
    `balance_paid_bonus` BIGINT                                                       NOT NULL COMMENT '유료 보너스 코인의 현재 잔액',
    `balance_free`       BIGINT                                                       NOT NULL COMMENT '무료 코인의 현재 잔액',
    `created_at`         TIMESTAMP                                                    NOT NULL COMMENT '최초 저장일시',
    `updated_at`         TIMESTAMP                                                    NOT NULL COMMENT '변경일시(최초에는 저장될때와 같은 일시로 저장)',
    PRIMARY KEY (`target_ymd`, `player_id`, `coin_cd`)
) ENGINE = INNODB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_unicode_ci COMMENT ='유저의 일자별 코인 증차감 통계 데이터(재화 증차감 발생할 때 같은 트랜잭션내에 같이 저장)'
;

CREATE TABLE `coin_balance_hist_column_type`
(
    `hist_id`             BIGINT UNSIGNED                                                        NOT NULL AUTO_INCREMENT COMMENT 'PK',
    `req_id`              VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci          NOT NULL COMMENT '요청ID(중복 요청 방어 목적 및 추적용)',
    `player_id`           VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci           NOT NULL COMMENT '유저ID',
    `coin_cd`             VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci           NOT NULL COMMENT '코인 종류(GEM 등)',
    `player_country`      VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci  DEFAULT NULL COMMENT '유저의 국가정보(국가별 법률 준수 목적으로 필요시 저장)',
    `amount`              INT                                                                    NOT NULL COMMENT '증감되는 변경 양',
    `balance_paid`        BIGINT                                                                 NOT NULL COMMENT '유료 코인의 현재 잔액',
    `balance_paid_bonus`  BIGINT                                                                 NOT NULL COMMENT '유료 보너스 코인의 현재 잔액',
    `balance_free`        BIGINT                                                                 NOT NULL COMMENT '무료 코인의 현재 잔액',
    `balance_change_type` ENUM ('PLUS','MINUS') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '잔액 변경 종류(증/차감)',
    `reason`              VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci          NOT NULL COMMENT '증감되는 사유',
    `memo`                VARCHAR(300) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '메모(필요시 저장)',
    `hist_created_at`     TIMESTAMP                                                              NOT NULL COMMENT '이력저장일시',
    PRIMARY KEY (`hist_id`),
    UNIQUE KEY `IDX_reqId_coinCd` (`req_id`, `coin_cd`),
    KEY `IDX_playerId_histCreatedAt` (`player_id`, `hist_created_at`)
) ENGINE = INNODB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_unicode_ci COMMENT ='유저의 재화 잔액 변경 이력'

참고용 쿼리 샘플

  • row 방식 쿼리를 참고하되 charge_type에 대해서 행을 열로 변경한 부분을 고려해서 수정해서 사용

코인 관리 DB 설계(참고용) - NoSQL

단순화된 열 형태의 설계

  • 여러 코인 충전 형태 중 필수인 일부 충전 방식에 대해서 필드 기반으로 관리

  • 1개의 행(Document)에 충전 타입에 따른 필드를 개별적으로 추가해서 잔액을 관리

    • 유료, 무료, 유료 보너스 3가지 구분은 필수

    • 샘플 내의 내용은 참고를 위한 내용입니다. 상황에 맞춰서 컬렉션(필드 및 인덱스 등)은 변경되어도 괜찮습니다. 다만 증/차감 누락이나 중복 충전 등 크리티컬한 문제는 주의해주세요.

샘플 컬렉션 정의 - coin_balance

  • 유저의 현재 코인(재화)의 잔액을 관리하는 마스터 컬렉션

  • 재화의 증/차감 변경이 발생할 때마다 데이터가 insert 또는 update되어 저장되는 컬렉션

  • 코인 코드에 따라서 유저는 N개의 Document를 보유할 수 있음

<필드 정의>

필드
데이터 타입
설명

_id

object

자동 생성되는 objectId 필드로 개발자가 생성할 수도 있지만 일반적으로 드라이버에서 생성됨

6698b68bd8cd923a387c3572

player_id

string

플레이어ID(유저ID)

test_user

coin_cd

string

코인 종류(GEM 등)

GEM

balance_paid

number

유료 코인의 현재 잔액

5

balance_paid_bonus

number

유료 보너스 코인의 현재 잔액

3

balance_free

number

무료 코인의 현재 잔액

0

created_at

object

최초 저장 일시

ISODate("2024-07-18T06:43:44.099Z")

updated_at

object

변경일시(최초에는 저장될때와 같은 일시로 저장)

ISODate("2024-07-18T06:43:44.099Z")

상세 참고사항

- 유저의 코인 잔액 컬렉션(마스터)

1. 컬렉션 생성
db.createCollection('coin_balance');

2. 인덱스 생성(유니크)
-- 유니크 인덱스를 생성하여 데이터 정합성 문제를 방지
db.coin_balance.createIndex( { player_id:1, coin_cd:1}, { unique: true } );

3. 테스트용 커맨드
-- 신규 충전 insert
db.coin_balance.insert( {"player_id":"test_user", "coin_cd":"GEM", "balance_paid":5, "balance_paid_bonus":3, "balance_free":0, "created_at": new Date(), "updated_at": new Date()} );
db.coin_balance.insert( {"player_id":"test_user", "coin_cd":"GOLD", "balance_paid":5, "balance_paid_bonus":3, "balance_free":0, "created_at": new Date(), "updated_at": new Date()} );

-- find결과(필수 필드들 참고)
> db.coin_balance.find().pretty()
{
        "_id" : ObjectId("6698b9a0d8cd923a387c3574"),
        "player_id" : "test_user",
        "coin_cd" : "GEM",
        "balance_paid" : 5,
        "balance_paid_bonus" : 3,
        "balance_free" : 0,
        "created_at" : ISODate("2024-07-18T06:43:44.099Z"),
        "updated_at" : ISODate("2024-07-18T06:43:44.099Z")
}
;

-- 데이터 타입 확인을 위한 커맨드
var doc = db.coin_balance.findOne({ _id: ObjectId("6698b9a0d8cd923a387c3574") });
for (var key in doc) {
  print(key + ": " + typeof doc[key]);
}
;

-- 데이터 타입 확인 결과 내용 샘플
_id: object
player_id: string
coin_cd: string
balance_paid: number
balance_paid_bonus: number
balance_free: number
created_at: object
updated_at: object

컬렉션 정의 - coin_balance_hist

  • 유저의 재화 잔액 변경 이력을 저장하는 컬렉션

  • 재화의 증/차감 변경이 발생할 때마다 데이터가 insert되어 저장되는 컬렉션(Update가 발생하면 안됨)

<필드 정의>

필드
데이터 타입
설명

_id

object

자동 생성되는 objectId 필드로 개발자가 생성할 수도 있지만 일반적으로 드라이버에서 생성됨

6698b68bd8cd923a387c3572

req_id

string

요청ID(중복 요청 방어 목적 및 추적용)

uuid-player_id-balance-plus-01

player_id

string

플레이어ID(유저ID)

test_user

coin_cd

string

코인 종류(GEM 등)

GEM

player_country

string

유저의 국가정보(국가별 법률 준수 목적으로 필요시 저장)

KR

amount

number

증감되는 변경 양

5

balance_paid

number

유료 코인의 현재 잔액

5

balance_paid_bonus

number

유료 보너스 코인의 현재 잔액

3

balance_free

number

무료 코인의 현재 잔액

0

balance_change_type

string

잔액 변경 종류(증/차감)

  • PLUS 또는 MINUS

PLUS

reason

string

증감되는 사유

plus_test

memo

string

메모(필요시 저장)

memo_test

hist_created_at

object

이력저장일시

ISODate("2024-07-18T07:54:53.388Z")

<상세 참고 사항>

-- 유저의 재화 잔액 변경 이력을 저장하는 컬렉션(이력 컬렉션)

1. 컬렉션 생성
db.createCollection('coin_balance_hist');

2. 인덱스 생성
-- req_id를 이용한 유니크 인덱스로 데이터 오류 방지 
db.coin_balance_hist.createIndex( { req_id:1, coin_cd:1}, { unique: true } );
db.coin_balance_hist.createIndex( { player_id:1, hist_created_at:-1});

3. 테스트용 커맨드
-- 신규 이력 insert
db.coin_balance_hist.insert( 
    {
    "req_id":"uuid-player_id-balance-plus-01",
    "player_id":"test_user",
    "coin_cd":"GEM",
    "player_country":"KR",
    "amount":5,
    "balance_paid":5,
    "balance_paid_bonus":3,
    "balance_free":0,
    "balance_change_type":"PLUS",
    "reason":"plus_test",
    "memo":"memo_test",
    "hist_created_at":new Date()
    }
);

-- find결과(필수 필드들 참고)
> db.coin_balance_hist.find().pretty()
{
        "_id" : ObjectId("6698ca4dd8cd923a387c3578"),
        "req_id" : "uuid-player_id-balance-plus-01",
        "player_id" : "test_user",
        "coin_cd" : "GEM",
        "player_country" : "KR",
        "amount" : 5,
        "balance_paid" : 5,
        "balance_paid_bonus" : 3,
        "balance_free" : 0,
        "balance_change_type" : "PLUS",
        "reason" : "plus_test",
        "memo" : "memo_test",
        "hist_created_at" : ISODate("2024-07-18T07:54:53.388Z")
}


-- 데이터 타입 확인을 위한 커맨드
var doc = db.coin_balance_hist.findOne({ _id: ObjectId("6698ca4dd8cd923a387c3578") });
for (var key in doc) {
  print(key + ": " + typeof doc[key]);
}


-- 데이터 타입 확인 결과 내용 샘플
_id: object
req_id: string
player_id: string
coin_cd: string
player_country: string
amount: number
balance_paid: number
balance_paid_bonus: number
balance_free: number
balance_change_type: string
reason: string
memo: string
hist_created_at: object

Last updated