Skip to content
스토브
마지막 업데이트

게임 제재

이해하기


부정행위 등으로 특정 이용자의 게임 이용을 막아야 할 때, 운영자는 스토브 파트너스에서 해당 이용자를 제재할 수 있어요.
제재가 등록되면 스토브 플랫폼이 게임 서버로 Kick 이벤트를 전달해요.
게임 서버는 이 이벤트를 받아 제재된 이용자를 게임에서 즉시 내보낼 수 있어요.

제재 시점별 동작 방식

제재는 이용자의 접속 상태에 따라 다르게 적용돼요.

상태 모바일 PC
접속 중(로그인된) 상태 정상 이용 가능 (제재 팝업 없음)
토큰 만료로 재로그인 시 제재 얼럿 노출
정상 이용 가능 (제재 팝업 없음)
접속 전(로그인 안된) 상태 로그인 시 제재 얼럿 노출 런처 게임 시작 시 제재 얼럿 노출

모바일 (로그인 시 제재 얼럿 노출)

PC (런처 게임 시작 시 제재 얼럿 노출)

접속 중 이용자 인게임 Kick 처리 필요
ㆍ 접속 중(로그인된) 상태에서는 게임이 스토브 제재 상태를 인지하지 못해요.
ㆍ 제재 등록 시 게임 서버에서 Kick, 클라이언트에서 로그아웃·종료 처리를 직접 해야 해요.

제재 이벤트 흐름

단계 동작
① 이벤트 전달 제재 등록 시 스토브가 게임에 등록된 URL로 Kick 이벤트 전달
② 게임 처리 제재 발생 안내, 인게임 Kick과 스토브 Logout 처리
③ 재로그인 이용자 재로그인 시 팝업으로 상세 제재 사유 확인

제재 해소 시 별도 이벤트 없음
ㆍ 게임은 제재 이용자에 대한 Kick 처리만 진행하면 돼요.

연동 전 게임 서버 환경 준비사항
ㆍ 게임 API 서버는 내부망 구성을 권장하며, 스토브 ↔ 게임 서버 호출에는 ACL 설정이 필요해요.
ㆍ Inbound ACL이 있다면 스토브 Event Broker의 환경별 NAT IP를 허용해요. (IP는 스토브 기술 담당자에게 문의)

개발하기


연동 개요

  • 이용자 제재(Kick 이벤트) 처리 흐름

  • PC 실행 흐름 — 재로그인 이용자 제재 여부 체크

  • 모바일 실행 흐름 — 재로그인 이용자 제재 여부 체크

  • 우회 이용자에 대한 추가 검증 흐름

Callback API 구성 가이드

CP사(개발사)가 직접 구현해 운영하는 엔드포인트로, STOVE Event Broker가 Kick 이벤트 발생 시 해당 URL을 호출해요.


API 엔드포인트

항목 내용
URL CP사 제공 (예: https://game.example.com/stove/kick)
Method POST
Protocol HTTPS (TLS 1.2 이상 필수)
Content-Type application/json
재전송 정책 HTTP 200 미응답 시 동일 이벤트 최대 3회 재전송

Request — Body 명세

Name Type Required Description
event_reason String Y 이벤트 발생 사유. 게임 제재의 경우 GAME_RESTRICT.
event_message String Y 이벤트 메시지.
event_time Long Y 이벤트 발생 일시 (milliseconds).
target_users Long Array Y Kick 대상 이용자 식별자 목록.

Sample — Request

bash
curl --location --request POST '{endpoint}' \
--header 'Content-Type: application/json' \
--data-raw '{
  "event_reason": "GAME_RESTRICT",
  "event_message": "비정상적인 게임 내 거래 행위",
  "event_time": 1739145600000,
  "target_users": [ 20000000001, 20000000002, 20000000003, 20000000004, 20000000005 ]
}'

Sample — Response (Success)

http
HTTP/1.1 200 OK

{
  "code": 0,
  "message": "SUCCESS"
}

Sample — Response (Failure)

응답 코드가 200이 아니면 STOVE Event Broker가 동일 이벤트를 최대 3회 재전송해요.

http
HTTP/1.1 500 Internal Server Error

{
  "code": 500,
  "message": "FAILURE"
}

이용자 제재정보 조회 API

게임 서버에서 이용자 토큰 유효성 검증 후, 우회 진입 차단을 위해 호출하는 조회 API예요. guidgame_id로 이용자 제재 상태를 확인할 수 있고, 제재 상태인 경우 제재 코드와 제재 기간 정보가 응답돼요. (제재 사유는 응답하지 않아요)


식별자 용어 정리 (TBD — 확정 후 별도 가이드 페이지로 이동 예정)

memberNo 스토브 계정의 고유 식별자. 한 이용자에게 하나만 부여돼요. 게임 무관 글로벌 식별자.
guid 플랫폼이 게임별로 발급하는 이용자 식별자. 동일한 memberNo라도 게임마다 서로 다른 guid를 가져요.
개념상 memberNo + game_id → guid의 매핑 관계.
game_id 게임을 식별하는 키 (파트너스 등록 시 결정). 본 API의 path 파라미터로 사용.

※ 본 정의는 현재 정리 중이며, 정식 식별자 가이드 페이지가 준비되면 이 박스는 해당 페이지 링크로 대체될 예정이에요.


API 엔드포인트

항목 내용
URL GET /mmember/v1.0/signin/game/status/{guid}/{game_id}
Host (Live) https://api.onstove.com
Host (Sandbox) https://api.gate8.com

Request — Header

Name Type Required Description
Content-Type application/json Y 리소스 media type.
Authorization String Y 플랫폼 인증 토큰. Bearer {API AccessToken} 형식.
caller-id String Y API 호출자 식별 헤더. 정해진 룰은 없으며, 게임 서버가 자유롭게 정의 후 STOVE에 사전 공유해 주세요.
모니터링 지표(호출량·장애·트레이싱) 식별자로 사용돼요.
예시(권고): {service_id}_SERVER, {service_id}_HOME
service_id는 파트너스에 등록한 Game ID. 한 번 정한 값은 변경하지 않는 것을 권장.

Request — Path Params

Name Type Required Description
guid String Y Game User ID.
game_id String Y 게임 식별자 (파트너스 등록 Game ID).

Request — Query Params

Name Type Required Description
guid_yn String Y (guid 게임) guid 게임인 경우 Y 필수 입력.

Response — Body

Name Type Required Description
response_code int Y 응답 코드 (성공: 0).
response_message String Y 응답 메시지 (성공: Success).
value.RESTRICT Object N 제재 정보. 제재 상태인 경우에만 포함.

Response — 제재 정보 (value.RESTRICT)

Name Type Example Description
memberNo Long 1234 이용자의 고유 번호 (guid).
nickname String AUTONICK##9515338 캐릭터 닉네임.
banDay int 2 제재 남은 기간 (Day).
banCd String B026 제재 코드 (게임별 마스터 데이터에서 정의).
banStartDt Long 1649053320000 제재 시작일 (밀리초).
banEndDt Long 1649312520000 제재 종료일 (밀리초).

Response — 응답 코드 (response_code)

모든 응답은 HTTP 200으로 내려오며, 비즈니스 결과는 response_code 값으로 분기해 처리해야 해요.

코드 상수명 의미 / 처리 가이드
0 PALMPLE_OK 정상 응답. value.RESTRICT가 있으면 제재 상태, 없으면 제재 없음.
10003 PALMPLE_ERR_NO_DATA 제재 정보 없음. 정상 이용자로 처리.
10122 PALMPLE_ERR_BAN_MEMBER 제재된 이용자. 인게임 Kick 및 STOVE Logout 처리 필요.
90001 AUTH_ACCESS_TOKEN_INVALID 토큰 검증 실패. caller-id·Authorization 헤더 또는 path의 guid/game_id 일치 여부 확인 후 재요청.

Sample — Request

bash
curl --location 'https://api.gate8.com/mmember/v1.0/signin/game/status/12345/{game_id}?guid_yn=Y' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {API accessToken}' \
--header 'caller-id: {caller-id}'

Sample — Response (제재 없음)

json
{
  "response_code": 0,
  "response_message": "Success"
}

Sample — Response (제재 있음)

json
{
  "response_code": 0,
  "response_message": "Success",
  "value": {
    "RESTRICT": {
      "memberNo": 1234,
      "nickname": "AUTONICK##9515338",
      "banDay": 2,
      "banCd": "B026",
      "banStartDt": 1649053320000,
      "banEndDt": 1649312520000
    }
  }
}

트러블슈팅

상황원인권장 처리
Callback 이벤트 미수신Callback URL 미등록 또는 잘못된 URL 등록STOVE 기술 담당자에게 등록 URL 재확인 요청
Callback 이벤트 미수신ACL/방화벽에서 STOVE NAT IP 차단환경별 NAT IP가 Inbound 허용 목록에 있는지 점검
동일 이벤트 3회 수신Callback API가 200 외 응답 (4xx/5xx/timeout)처리 로직 예외 발생 시에도 200 응답 보장 (idempotent 처리)
조회 API response_code=90001caller-id·Authorization 헤더 누락/오타헤더 3개(Content-Type, Authorization, caller-id) 모두 포함되는지 점검
조회 API response_code=90001path의 guid/game_id가 토큰 권한과 불일치API AccessToken 재발급 시 game_id 권한 확인
한 이용자 중복 Kick 처리다중 Callback 인스턴스에서 동일 이벤트 처리event_time+memberNo 키로 dedup 처리 권장
제재 해제 후에도 Kick 유지제재 해제는 별도 이벤트 미전송 (정상 동작)게임 진입 시점에 조회 API 재호출로 최신 상태 확인

샘플 코드

본 예시는 게임 서버가 직접 운영하는 영역의 참고 구현이에요. 실제 환경에 맞게 의존성·예외 처리·로깅을 보완해 사용해 주세요.


1) Callback API 수신 — Kick 이벤트 핸들러

java
@RestController
@RequestMapping("/stove")
public class StoveKickController {

    private final UserKickService kickService;

    public StoveKickController(UserKickService kickService) {
        this.kickService = kickService;
    }

    /** STOVE Event Broker가 호출하는 Kick 이벤트 수신 엔드포인트 */
    @PostMapping(value = "/kick", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Map<String, Object>> onKickEvent(@RequestBody KickEvent event) {
        if (!"GAME_RESTRICT".equals(event.getEventReason())) {
            // 알 수 없는 이벤트는 200으로 응답해 재전송 루프를 막아요.
            return ResponseEntity.ok(Map.of("code", 0, "message", "IGNORED"));
        }

        kickService.kickAll(event.getTargetUsers(), event.getEventMessage(), event.getEventTime());

        // 정상 수신 → 200 OK 반환 (200 외 응답 시 STOVE Event Broker가 최대 3회 재전송)
        return ResponseEntity.ok(Map.of("code", 0, "message", "SUCCESS"));
    }

    @Getter @Setter
    public static class KickEvent {
        @JsonProperty("event_reason")  private String eventReason;
        @JsonProperty("event_message") private String eventMessage;
        @JsonProperty("event_time")    private Long eventTime;
        @JsonProperty("target_users")  private List<Long> targetUsers;
    }
}

2) 이용자 제재정보 조회 — REST 호출

java
@Component
public class StoveBanInfoClient {

    private static final String HOST_LIVE    = "https://api.onstove.com";
    private static final String HOST_SANDBOX = "https://api.gate8.com";

    private final RestClient restClient;

    @Value("${stove.api.access-token}") private String apiAccessToken;
    @Value("${stove.caller-id}")        private String callerId;
    @Value("${stove.profile:live}")     private String profile;

    public StoveBanInfoClient(RestClient.Builder builder) {
        this.restClient = builder.build();
    }

    /** 제재 상태 조회. 정상 사용자면 Optional.empty() 반환. */
    public Optional<BanInfo> getBanInfo(String guid, String gameId) {
        String host = "sandbox".equalsIgnoreCase(profile) ? HOST_SANDBOX : HOST_LIVE;
        String url  = host + "/mmember/v1.0/signin/game/status/" + guid + "/" + gameId + "?guid_yn=Y";

        BanResponse res = restClient.get()
                .uri(url)
                .header(HttpHeaders.AUTHORIZATION, "Bearer " + apiAccessToken)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .header("caller-id", callerId)
                .retrieve()
                .body(BanResponse.class);

        if (res == null) return Optional.empty();

        switch (res.responseCode) {
            case 0:                                       // 정상
                return res.value != null ? Optional.ofNullable(res.value.RESTRICT) : Optional.empty();
            case 10003:                                   // 제재 정보 없음
                return Optional.empty();
            case 10122:                                   // 제재된 사용자
                return res.value != null ? Optional.ofNullable(res.value.RESTRICT) : Optional.empty();
            case 90001:                                   // 토큰 검증 실패
                throw new IllegalStateException("STOVE accessToken / caller-id / path 불일치 — 설정 점검");
            default:
                throw new IllegalStateException("Unexpected response_code: " + res.responseCode);
        }
    }

    @Getter @Setter
    public static class BanResponse {
        @JsonProperty("response_code")    public int responseCode;
        @JsonProperty("response_message") public String responseMessage;
        public ValueBlock value;
    }

    @Getter @Setter
    public static class ValueBlock { public BanInfo RESTRICT; }

    @Getter @Setter
    public static class BanInfo {
        public Long memberNo;
        public String nickname;
        public int banDay;
        public String banCd;
        public Long banStartDt;
        public Long banEndDt;
    }
}

3) 게임 진입 시점에서의 사용 흐름

java
public class GameEntryService {
    private final StoveBanInfoClient banClient;
    private final GameSessionService sessionService;

    public void onPlayerEnter(String guid, String gameId) {
        banClient.getBanInfo(guid, gameId).ifPresentOrElse(
            ban -> sessionService.kick(ban.memberNo, ban.banCd, ban.banEndDt),
            ()  -> sessionService.allow(guid)
        );
    }
}

자주 묻는 질문



Q1. 접속 중인 이용자가 제재되어도 게임을 계속 플레이할 수 있나요?
A. 네, 모바일/PC 환경에서 접속 중(로그인된) 상태에서는 스토브 플랫폼의 제재 상태를 즉시 인지하지 못해요.
따라서 게임 서버사이드에서 이용자 Kick 처리와 클라이언트 측 로그아웃 또는 종료 처리를 반드시 별도로 구현해야 해요.
Kick 이벤트를 수신하는 Callback API를 구성하고, 이벤트 수신 시 즉시 해당 이용자를 처리해야 해요.
Q2. 제재가 해제되면 별도 이벤트를 받을 수 있나요?
A. 아니요, 제재 해제 시에는 별도 이벤트를 전달하지 않아요.
제재 등록 시에만 Kick 이벤트가 전달되며, 게임에서는 제재 이용자에 대한 Kick 요청 처리만 진행하면 돼요.
Q3. Callback API가 HTTP 200을 응답하지 못하면 어떻게 되나요?
A. HTTP 응답 코드가 200이 아닌 경우 스토브 플랫폼은 동일 이벤트를 기본 3회 재전송해요.
Callback API 구현 시 반드시 성공적으로 수신한 경우 HTTP 200을 응답하도록 구현해 주세요.
Q4. 클라이언트 우회로 게임에 진입하는 제재 이용자를 어떻게 차단하나요?
A. 게임 진입 시점에 이용자 토큰 유효성 검증 후, 제재 여부 조회 API(GET /mmember/v1.0/signin/game/status/{guid}/{game_id})를 추가로 호출해
제재 상태를 확인해요. 제재 상태이면 인게임 Kick과 STOVE Logout 처리를 수행해야 해요.
클라이언트 정보는 패킷 탈취·위변조에 취약하므로 반드시 서버 측에서 추가 검증을 진행해야 해요.
Q5. 이용자 제재정보 조회 API에서 제재 사유도 확인할 수 있나요?
A. 아니요, 제재 사유는 응답에 포함되지 않아요.
제재 상태인 경우 제재 코드(banCd), 제재 기간(banStartDt, banEndDt), 제재 남은 기간(banDay), 닉네임(nickname), 회원번호(memberNo) 정보가 응답돼요.
Q6. Callback API URL은 어떻게 등록하나요?
A. 현재 파트너스 내 URL 등록 메뉴는 준비 중이에요.
Callback API URL 정보를 게임 기술 담당자에게 전달해 주시면 기술 지원을 통해 등록해 드려요.



직접 문의하고 싶으신가요? stove.developers@smilegate.com