- 마지막 업데이트
팝업
이해하기
공지·이벤트·쿠폰 화면 등을 게임 안에서 이용자에게 보여주는 SDK 팝업 서비스예요. 각 화면은 파트너스에서 다양하게 설정할 수 있어요.
Mobile(Android/iOS)과 PC를 모두 지원하며, 플랫폼에 따라 제공 방식이 달라요.
팝업 종류
| 종류 | 설명 | Mobile | PC |
|---|---|---|---|
| 자동 팝업 | 로비 진입 시 조작 없이 전면 팝업을 순차 노출. 광고·이벤트용. | ✅ | ✅ |
| 수동 팝업 | 지정한 location 호출 시 등록된 화면 노출. 특정 조작·조건에서 띄울 때 사용. | ✅ | ✅ |
| 뉴스 팝업 | 공지 게시글을 모아 노출. 내용이 없으면 "준비중" 표시. | ✅ | ✅ |
| 쿠폰 팝업 | 발급받은 쿠폰 번호로 쿠폰 기능을 사용. (PC는 멀티플랫폼 게임만) | ✅ | ✅ |
| 커뮤니티 | 스토브 커뮤니티를 팝업으로 노출. 로그인 유지 상태로 호출. | ✅ | — |
| 커스텀 URL | 직접 제작한 웹페이지를 내장 WebView로 노출. | ✅ | — |
| 본인인증 팝업 | 인게임 본인인증 절차를 팝업으로 진행. (한국 전용) | — | ✅ |
쿠폰 팝업 연동은 별도 페이지에서 안내해요
쿠폰 팝업 연동 방법은 기능별 가이드의 쿠폰 페이지를 참고해 주세요.
Mobile / PC 비교
| 항목 | Mobile | PC |
|---|---|---|
| SDK | Mobile SDK | PCSDK View SDK (v3.1.0 이상) |
| 팝업 노출 방식 | SDK 내장 WebView로 팝업 노출 | View SDK 내장 WebView로 팝업 노출 (Windows만 지원) |
| 초기화 순서 | 이용자 로그인 완료 후 팝업 호출 가능 | Base SDK 초기화 → View SDK 초기화 순서 필수 |
| 지원 플랫폼 | Android (Kotlin/Java), iOS, Unity, Unreal | Native C/C++, Unity, Unreal |
| 팝업 관리 | 파트너스를 통해 각 이벤트 화면별 설정 | 파트너스를 통해 각 이벤트 화면별 설정 (모든 게임 유형 지원) |
| Overlay 기능 | View 2.8.2 이상에서 Overlay UI 사용 설정 별도 필요 | 해당 없음 |
PC — View SDK 초기화 순서에 주의해요
ㆍ View SDK 초기화 전에 Base SDK 연동·초기화를 먼저 완료해야 해요(미완료 시 View SDK 기능 사용 불가).
ㆍ View SDK 초기화는 다른 View SDK 기능을 사용하기 전에 진행해 주세요.
팝업 공통 설정
뉴스 팝업, 자동 팝업, 수동 팝업에서 아래 항목을 공통으로 설정할 수 있어요.
| 설정 항목 | 설명 |
|---|---|
| 닫기 버튼 | 커스텀 이미지 또는 정해진 템플릿(뉴스/자동: 6종, 수동: 3종) 중 선택 가능. |
| 하단 Navigation | ON/OFF 설정 가능. (Back, Forward, Refresh, Home ON/OFF) |
| 오늘은 그만 | 버튼 ON/OFF 및 1일/7일 중 선택 설정 가능. (뉴스·자동 팝업만 지원, 수동 팝업 미지원) |
연동 가이드
연동 준비
Mobile
| 항목 | 내용 |
|---|---|
| 이용자 로그인 | 이용자 로그인 완료 필수. |
| 파트너스 팝업 등록 | 파트너스에 팝업 정보 등록 (직접 호출 방식 제외). |
| Overlay UI 설정 (선택) | Overlay 사용 시 별도 설정 (View 2.8.2 이상). |
PC (PCSDK)
| 항목 | 내용 |
|---|---|
| Base SDK 연동 및 초기화 | View SDK 초기화 전 Base SDK 연동·초기화 완료. |
| 파트너스 팝업 등록 | 파트너스에 기본 정보(world id) 등록 후 노출 설정. |
| 게임프로필 설정 | ㆍ 필드·캐릭터 정보 사용 시, 팝업 기능 전 게임프로필 설정 필수 ㆍ BaseSDK의 Base_SetGameProfile API 사용 |
| 팝업 지원 게임 확인 | ㆍ PC SDK 팝업 API 전 파트너스에 팝업 메타데이터 등록 선행 ㆍ 멀티플랫폼 게임 및 PC only 게임 모두 사용 가능 |
개발하기
모바일
STOVE SDK View 모듈의 각 팝업 API를 호출하는 방법이에요. 플랫폼별 코드는 탭으로 구분되어 있으니 사용하는 플랫폼만 골라 확인하세요.
뉴스 팝업
공지성 게시글을 한 번에 모아서 보여주는 팝업이에요. 파트너스에서 각 이벤트 화면별로 세부 설정할 수 있어요.
- 닫기 버튼: 커스텀 이미지 또는 정해진 템플릿 6종
- 하단 Navigation (Back/Forward/Refresh/Home) ON/OFF
- "오늘은 그만" 버튼 ON/OFF (1일/7일 선택)

public void News()
{
ViewUI.News((Result result, Dictionary<string, string> dictionary) =>
{
if (result.IsSuccessful)
{
}
else
{
OperationUI.HandleResult(result, (Result operationResult) =>
{
/** ex) 현재 화면 유지 **/
});
}
});
}
private fun newsPopup(activity: Activity) {
ViewUI.news(activity) { result, map ->
//closed view
}
}
ErrorCodes
| Domain | ErrorCode | Description |
|---|---|---|
| com.stove.success | 0 | Success |
| com.stove.server | 404 | path variable을 입력하지 않거나 API 주소를 잘못 쓴 경우 |
| com.stove.base.network | 10001 | NoConnectionError |
| com.stove.base.network | 10002 | TimeoutError |
자동 팝업
게임 로비 화면에서 가장 많이 노출되는 광고·이벤트 팝업이에요. 인게임 진입 시 이용자 터치 없이도 전면 팝업이 순차적으로 노출돼요.
- 닫기 버튼: 커스텀 이미지 또는 정해진 템플릿 6종
- 하단 Navigation (Back/Forward/Refresh/Home) ON/OFF
- "오늘은 그만" 버튼 ON/OFF (1일/7일 선택)
- 자동 팝업 중에는 파트너스 설정에 따라 상점 팝업이 노출될 수 있고, 웹 내에서 아이템 결제가 가능해요.


결과 처리 가이드
자동 팝업은 이용자 터치 없이 노출되는 광고성 영역이라 콜백 시점에 게임 흐름을 막지 마세요. 성공 콜백에서는 화면을 그대로 유지하고, 상점 팝업으로 결제가 발생한 경우에는 게임 서버에 결제 결과 동기화를 별도로 요청하세요. 실패는 OperationUI.HandleResult(result, ...)에 위임해 SDK가 적절한 안내 화면을 띄우게 하세요.
public void AutoPopup()
{
ViewUI.Popup((Result result, Dictionary<string, string> dictionary) =>
{
if (result.IsSuccessful)
{
}
else
{
OperationUI.HandleResult(result, (Result operationResult) =>
{
/** ex) 현재 화면 유지 **/
});
}
});
}
private fun autoPopup(activity: Activity) {
ViewUI.popup(activity) { result, map ->
//closed view
}
}
ErrorCodes
| Domain | ErrorCode | Description |
|---|---|---|
| com.stove.success | 0 | Success |
| com.stove.server | 1000 | WRONG_API_USAGE |
| com.stove.server | 2000 | SERVICE_ERROR |
| com.stove.server | 90000 | Error: request accessToken is not exists. |
| com.stove.server | 90001 | Error: accessToken invalid. |
| com.stove.base.network | 10001 | NoConnectionError |
| com.stove.base.network | 10002 | TimeoutError |
수동 팝업
게임에서 원하는 위치에 정해진 location으로 수동 팝업을 호출하면 등록된 이벤트 화면이 노출돼요. 인게임에서 특정 UI 터치 또는 화면 진입 시 조건부로 팝업을 보여줄 때 사용해요.
- 닫기 버튼: 커스텀 이미지 또는 정해진 템플릿 6종
- 하단 Navigation (Back/Forward/Refresh/Home) ON/OFF

Parameters
| Parameter | Type | Description |
|---|---|---|
| location | int | 팝업 위치 (1~5) |
public void ManualPopup()
{
/**
* location : 팝업 위치 (int)
**/
ViewUI.Popup("location", (Result result, Dictionary<string, string> dictionary) =>
{
if (result.IsSuccessful)
{
}
else
{
OperationUI.HandleResult(result, (Result operationResult) =>
{
/** ex) 현재 화면 유지 **/
});
}
});
}
private fun manualPopup(activity: Activity) {
ViewUI.popup(activity, 1) { result, map ->
//closed view
}
}
ErrorCodes
| Domain | ErrorCode | Description |
|---|---|---|
| com.stove.success | 0 | Success |
| com.stove.server | 404 | path variable을 입력하지 않거나 API 주소를 잘못 쓴 경우 |
| com.stove.server | 70004 | ui_location 값이 0인 경우 |
| com.stove.base.network | 10001 | NoConnectionError |
| com.stove.base.network | 10002 | TimeoutError |
쿠폰 사용
파트너스에서 발급받은 쿠폰 번호를 전달해 쿠폰 기능을 사용해요. Android는 인게임 화면에서 ViewUI.coupon을 사용하면 스토브 쿠폰 입력 창이 표시돼요.
- 쿠폰 등록 방법: 파트너스-쿠폰관리 메뉴얼 참고
- 서버 개발 가이드: 이용자가 쿠폰을 입력하고 지급 조건이 충족되면 아이템박스(STOVE ItemBox)에 지급 정보가 보관돼요. 게임서버는 실시간 전달(Notification) 또는 요청(API Call) 방식 중 하나를 구현할 수 있어요. 아이템박스 연동하기 참고.
에러 코드 그룹별 분기 처리
쿠폰 에러는 이용자 안내 메시지와 재시도 가능 여부가 달라요. 아래 그룹대로 콜백에서 분기를 작성하세요.
- 재입력 유도 (
5105잘못된 쿠폰 /5031일일 인증 횟수 초과 /6026사용 횟수 초과): 메시지 노출 후 입력 창을 유지해 다시 입력받으세요. - 입력 창 닫기 + 안내 (
5125이미 사용 /5130사용 중지 /5135유효기간 만료 /5161등록 기간 만료 /5169사용 기간 아님 /5202이미 등록): 메시지 노출 후 입력 창을 닫으세요. 같은 쿠폰 재시도 의미 없음. - 자격 안내 (
5162국가 /5164월드 /5200사용 대상 /5165쿠폰함 전용 /100PC방 전용 /2605멤버쉽 사용 불가): 자격 미달임을 안내하고 입력 창을 닫으세요. - 인증 재처리 (
997Not Verify /998Expired):Auth.accessToken을 재조회하거나 재로그인 흐름으로 유도하세요. 그 외 시스템 에러(999,6002)는OperationUI.HandleResult에 위임하세요.
public void UseCoupon()
{
/**
* code : 쿠폰 입력 (string)
**/
View.UseCoupon("code", (Result result) =>
{
if (result.IsSuccessful)
{
}
else
{
OperationUI.HandleResult(result, (Result operationResult) =>
{
/** ex) 현재 화면 유지 **/
});
}
});
}
fun useCoupon(context: Context, code: String) {
View.useCoupon(context, code) { result, map ->
}
}
ErrorCodes
| Domain | ErrorCode | Description |
|---|---|---|
| com.stove.server | 100 | PC방에서만 사용 가능한 쿠폰입니다. |
| com.stove.server | 997 | Not Verify AccessToken |
| com.stove.server | 998 | Expired AccessToken |
| com.stove.server | 999 | System Error |
| com.stove.server | 2605 | 해당 멤버쉽 계정은 사용할 수 없습니다. |
| com.stove.server | 5031 | 일일 인증 횟수를 초과하였습니다. |
| com.stove.server | 5105 | 잘못된 쿠폰 번호입니다. |
| com.stove.server | 5125 | 이미 사용한 쿠폰입니다. |
| com.stove.server | 5130 | 사용 중지된 쿠폰입니다. |
| com.stove.server | 5135 | 유효기간이 만료된 쿠폰입니다. |
| com.stove.server | 5155 | 해당 쿠폰의 사용 가능 횟수를 초과하였습니다. |
| com.stove.server | 5161 | 등록 기간이 만료된 쿠폰입니다. |
| com.stove.server | 5162 | 해당 국가에서 사용할 수 없는 쿠폰입니다. |
| com.stove.server | 5164 | 해당 월드에서는 사용할 수 없는 쿠폰입니다. |
| com.stove.server | 5165 | 쿠폰함에서만 사용 가능한 쿠폰입니다. |
| com.stove.server | 5169 | 해당 쿠폰의 사용 기간이 아닙니다. |
| com.stove.server | 5200 | 해당 쿠폰에 대한 사용 대상이 아닙니다. |
| com.stove.server | 5202 | 이미 쿠폰함에 등록된 쿠폰입니다. |
| com.stove.server | 6002 | 해당 쿠폰번호를 더 이상 사용하실 수 없습니다. |
| com.stove.server | 6026 | 사용 횟수가 초과되었습니다. |
커뮤니티
인게임에서 스토브 커뮤니티를 팝업으로 노출해요. 커뮤니티가 정상 노출되려면 파트너스에 커뮤니티가 사전 세팅되어 있고, SDK Config의 VIEW > community_id에 파트너스 키값이 등록된 상태여야 해요.

public void Community()
{
ViewUI.Community((Result result, Dictionary<string, string> dictionary) =>
{
if (result.IsSuccessful)
{
if (dictionary != null && dictionary.ContainsKey("received_data"))
{
string receivedData = dictionary["received_data"];
Dictionary<string, object> data = Json.Deserialize(receivedData) as Dictionary<string, object>;
if (data != null)
{
if (data.TryGetValue("code", out object codeObj) && codeObj is long errorCode)
{
if (errorCode == 40104) {
/** SDK 인증 토큰 만료 — 로그아웃 후 초기화면으로 이동 **/
}
}
}
}
}
else
{
OperationUI.HandleResult(result, (Result operationResult) => { });
}
});
}
private fun community(activity: Activity) {
ViewUI.community(activity) { result, map ->
//closed view
}
}
ErrorCodes
| Domain | ErrorCode | Description |
|---|---|---|
| com.stove.success | 0 | Success |
| com.stove.server | 11236 | Error : Access token is wrong. |
| com.stove.base.network | 10001 | NoConnectionError |
| com.stove.base.network | 10002 | TimeoutError |
커뮤니티 URL 직접 호출
커뮤니티 내 특정 URL을 일반적인 방법으로 호출하면 로그인이 풀린 상태로 호출돼요. 아래 인터페이스를 사용하면 로그인 유지 상태로 호출할 수 있어요.

received_data 응답 처리
성공 콜백의 userInfo["received_data"]는 JSON 문자열이에요. 파싱 후 code == 40104이면 SDK 인증 토큰이 만료된 상태이므로 재로그인 흐름으로 유도하세요. 그 외 코드는 게임이 정의한 형식에 맞춰 처리하거나 무시할 수 있어요. 실패 콜백은 OperationUI.HandleResult(result, ...)에 위임하세요.
public void CommunityWithURL()
{
/**
* url : url 입력 (string)
**/
ViewUI.Community("url", (Result result, Dictionary<string, string> dictionary) =>
{
if (result.IsSuccessful)
{
if (dictionary != null && dictionary.ContainsKey("received_data"))
{
string receivedData = dictionary["received_data"];
Dictionary<string, object> data = Json.Deserialize(receivedData) as Dictionary<string, object>;
if (data != null)
{
if (data.TryGetValue("code", out object codeObj) && codeObj is long errorCode)
{
if (errorCode == 40104) {
/** SDK 인증 토큰 만료 **/
}
}
}
}
}
else
{
OperationUI.HandleResult(result, (Result operationResult) => { });
}
});
}
private fun community(activity: Activity, url: String) {
ViewUI.community(activity, url) { result, map ->
//closed view
}
}
ErrorCodes
| Domain | ErrorCode | Description |
|---|---|---|
| com.stove.success | 0 | Success |
| com.stove.server | 11236 | Error : Access token is wrong. |
| com.stove.base.network | 10001 | NoConnectionError |
| com.stove.base.network | 10002 | TimeoutError |
커스텀 URL
게임에서 원하는 URL을 WebView로 노출해요. ViewConfiguration으로 부분 화면 또는 전체 화면 표시 방식을 선택할 수 있어요.
Parameters
| Parameter | Type | Description |
|---|---|---|
| viewRequest | ViewRequest | View를 보여주기 위한 config 항목 |
플랫폼별 구현 — 부분 화면
public void Load()
{
ViewConfiguration viewConfiguration = ViewConfiguration.Partial();
ViewRequest viewRequest = new ViewRequest("url", viewConfiguration);
ViewUI.Load(viewRequest, (Result result, Dictionary<string, string> dictionary) =>
{
if (result.IsSuccessful)
{
if (result.UserInfo != null && result.UserInfo.TryGetValue("userAction", out string userAction))
{
if (!string.IsNullOrEmpty(userAction) && userAction.Equals("withdraw_complete"))
{
/** ex) 게임 탈퇴 완료 **/
}
}
}
else
{
OperationUI.HandleResult(result, (Result operationResult) => { });
}
});
}
private fun load(activity: Activity) {
val url = "https://www.onstove.com/"
val viewConfiguration = ViewConfiguration.partial()
val viewRequest = ViewRequest(url, viewConfiguration = viewConfiguration)
ViewUI.load(activity, viewRequest) { result, map ->
if (result.isSuccessful()) {
result.userInfo?.let { userInfo ->
when (userInfo["userAction"]) {
"withdraw_complete" -> {
// 탈퇴 상태
}
}
}
}
}
}
플랫폼별 구현 — 전체 화면
public void Load()
{
ViewConfiguration viewConfiguration = ViewConfiguration.Full();
ViewRequest viewRequest = new ViewRequest("url", viewConfiguration);
ViewUI.Load(viewRequest, (Result result, Dictionary<string, string> dictionary) =>
{
if (result.IsSuccessful)
{
if (result.UserInfo != null && result.UserInfo.TryGetValue("userAction", out string userAction))
{
if (!string.IsNullOrEmpty(userAction) && userAction.Equals("withdraw_complete"))
{
/** ex) 게임 탈퇴 완료 **/
}
}
}
else
{
OperationUI.HandleResult(result, (Result operationResult) => { });
}
});
}
WebView 공통 가이드
게임이 직접 제작한 웹페이지를 SDK WebView로 노출할 때(뉴스·자동·수동 팝업, 커뮤니티, 커스텀 URL 등) 공통으로 사용하는 헤더 정보, URI scheme, JavascriptInterface, 데이터 송수신 패턴을 안내해요.
WebView 호출 시 SDK가 header에 넘기는 정보
게임 운영을 위해 웹페이지를 직접 제작하는 경우, 다음 데이터를 활용할 수 있어요. 아래 데이터는 header에 포함돼요.
| Key | Description |
|---|---|
| authorization | 스토브 인증 서버로부터 발급받은 token |
| characterno | 이용자의 캐릭터 번호 (게임에서 사용하는 경우에만 포함) |
| ServerID | 이용자의 월드 (게임에서 사용하는 경우에만 포함) |
| SDK-Version | View 모듈의 버전 (ex: 2.0.0) |
| Accept-Language | 디바이스 또는 게임에서 설정한 언어 (ex: ko) |
Web과 통신하기
Stove SDK는 미리 정의된 JavascriptInterface와 URI scheme을 지원해요.
URI : 공통
| 정의 | 내용 | 예시 | 사용 가능 버전 |
|---|---|---|---|
stovewebs:// | 외부 링크(사파리, 크롬)로 이동 | stovewebs://naver.com → https://naver.com 브라우저로 이동 | 2.0.0 |
stovecommunitys:// | 스토브 커뮤니티 오픈 | stovecommunitys를 https로 변환 후 커뮤니티 뷰 이동 | 2.0.0 |
URI : Android 에서만 제공
| 정의 | 내용 | 예시 | 사용 가능 버전 |
|---|---|---|---|
intent:// | 외부 링크로 이동 | stovewebs://naver.com → https://naver.com로 이동 | 2.0.0 |
| http/https가 아닌 scheme | ACTION_VIEW Intent를 받을 수 있는 Activity 시작 | market://details?id=com.stove.mstove.google → PlayStore, twitch://open?link_click_id=... → Twitch | 2.0.0 |
JavascriptInterface
| 정의 | 내용 |
|---|---|
closeWebview | 현재 뷰 닫기 (SDK에 데이터 전달 가능) |
getDeviceInfo | 디바이스 정보 조회 (StoveJSBridge.callback으로 전달받음) |
getValue | 게임의 property 조회. 파라미터로 key 전달 (StoveJSBridge.callback으로 전달받음) |
코드 예제
function closeWebview(){
if (window._StoveJSBridge) {
window._StoveJSBridge.invoke("closeWebview", "게임 클라이언트에 전달할 데이터", null);
} else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.StoveJS) {
var message = { method: 'closeWebview', parameter: '게임 클라이언트에 전달할 데이터' };
window.webkit.messageHandlers.StoveJS.postMessage(message);
}
}
function getDeviceInfo(){
if (window._StoveJSBridge) {
window._StoveJSBridge.invoke("getDeviceInfo", null, "getDeviceInfoCallbackId");
} else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.StoveJS) {
var message = { method: 'getDeviceInfo', callbackId: 'getDeviceInfoCallbackId' };
window.webkit.messageHandlers.StoveJS.postMessage(message);
}
}
function getValue(){
if (window._StoveJSBridge) {
window._StoveJSBridge.invoke("getValue", '{"key":"fetchKey"}', "getValueCallbackId");
//기본값 지정이 필요하면 '{"key":"fetchKey", "default":"testValue"}' 형태로 사용
} else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.StoveJS) {
var message = { method: 'getValue', callbackId: 'getValueCallbackId' };
window.webkit.messageHandlers.StoveJS.postMessage(message);
}
}
var StoveJSBridge = {
callback: function(callbackId, error, result) {
if(callbackId === "getDeviceInfoCallbackId") {
const resultJSON = JSON.parse(result);
const marketGameId = resultJSON.market_game_id;
const deviceId = resultJSON.device_info.device_id;
const osName = resultJSON.device_info.os_name;
const adid = resultJSON.device_info.adid;
} else if(callbackId === "getValueCallbackId") {
const resultJSON = JSON.parse(result);
const returnCode = resultJSON.return_code;
if(returnCode === 0){
const key = resultJSON.key;
const value = resultJSON.value;
} else if(returnCode === 39403) {
//Not Exist Key
} else if(returnCode === 39002) {
//Invalid Params
}
}
}
};
SDK에서 JavascriptInterface로부터 데이터 수신하기
웹페이지에서 closeWebview로 전달한 데이터를 SDK 콜백의 received_data 키로 받아올 수 있어요.
private void News()
{
ViewUI.News((Result result, Dictionary<string, string> dictionary) =>
{
if (dictionary != null && dictionary.ContainsKey("received_data"))
{
string value = dictionary["received_data"];
}
});
}
SDK에서 웹페이지에서 조회할 properties 설정하기
Game Client ↔ SDK ↔ Web 간 데이터 저장 및 조회 목적으로 사용돼요. @since 2.1.0
public void SetProperties()
{
AccessToken accessToken = Auth.AccessToken;
if (accessToken == null) { return; }
Dictionary<string, object> properties = new Dictionary<string, object>();
properties.Add("testKey", "testValue");
GameProfile gameProfile = accessToken.User.GameProfile;
if (gameProfile == null)
{
accessToken.User.GameProfile = new GameProfile();
}
accessToken.User.GameProfile.Properties = properties;
}
외부 브라우저 열기 (+SSO 연동)
인증 세션을 유지한 상태로 외부 브라우저에서 ON스토브 페이지(쿠폰/커뮤니티/고객센터 등)를 오픈해요.
⚠️ 주의
게스트 계정은 본 기능을 사용할 수 없어요.
스토브와 다른 인증서(서명키)로 로그인된 계정도 SSO 외부 브라우저 호출 금지. 반드시 '스토브 계정 전환' 완료 후 호출하세요.
/**
* url : "외부브라우저를 연동할 url : 쿠폰 or 커뮤니티 등";
**/
public void OpenExternalUrl()
{
AccessToken accessToken = Auth.AccessToken;
if (accessToken == null) { return; }
string url = "외부브라우저를 연동할 url : 쿠폰 or 커뮤니티 등";
ViewUI.OpenExternalUrl(url, (Result result) =>
{
if (result.IsSuccessful)
{
// 외부브라우저 연동 성공
}
else
{
OperationUI.HandleResult(result, (Result operationResult) => { });
}
});
}
PC (PCSDK)
사전 준비
- ViewSDK는 Base SDK 초기화 완료 후
View_Initialize로 초기화해요. 내부 모드 팝업의 부모 HWND를 지정하려면View_InitializeWithWndInfo(hwnd)를 사용해요. 외부 브라우저 모드만 사용한다면 부모 HWND 없이View_Initialize를 호출해도 돼요. - 게임 루프에서
Base_RunCallback()이 주기적으로 호출돼야 비동기 콜백이 동작해요. - 파트너스 설정에서 캐릭터/필드 정보를 사용한다면
Base_SetGameProfile로 게임 프로파일을 먼저 설정해요. - 본인인증 팝업은 한국 외 국가에서
NOT_SUPPORTED_COUNTRY(31)을 반환해요. - 쿠폰 팝업은 별도 페이지에서 다뤄요. (쿠폰(Itembox) 참고)
- 외부 브라우저로 ON스토브 페이지(커뮤니티/고객센터 등)를 여는 기능은 ViewSDK(팝업) 모듈이 아닌 BaseSDK 모듈 의 API(
Base_OpenExternalUrl)예요. 따라서Base_Initialize완료만 되면 호출할 수 있어요. 스토브 관련 도메인만 허용되며 SSO 로그인이 유지돼요.
개발 흐름
- 초기화: Base SDK 초기화(
Base_Initialize또는Base_InitializeEx)를 완료한 뒤View_Initialize로 ViewSDK를 초기화해요. 내부 모드 팝업을 사용하려면 게임 메인 HWND를 넘기는View_InitializeWithWndInfo를 사용해요. - 팝업 노출: 시점/요건에 맞는 API를 호출해요. 모든 팝업 API는 첫 번째 인자로
WebViewMode(EXTERNAL: 외부 브라우저 /INTERNAL: SDK 내장 웹뷰)를 받아요. 자동·수동·뉴스 팝업은 Ex 버전 API를 사용하면 표시 완료 콜백과 별개로 팝업 종료 콜백(OnViewPopupDestroyFinished)을 함께 받을 수 있어요.- 자동 팝업:
View_AutoPopup(mode, onFinished)(종료 이벤트가 필요하면View_AutoPopupEx) - 수동 팝업:
View_ManualPopup(resourceKey, mode, onFinished)(resourceKey로 노출할 팝업 지정, 종료 이벤트가 필요하면View_ManualPopupEx) - 뉴스 팝업:
View_NewsPopup(mode, onFinished)(종료 이벤트가 필요하면View_NewsPopupEx) - 본인인증 팝업:
View_VerifyIdentificationPopup(compareIdentifier, mode, onFinished, onDestroy)(compareIdentifier지정, 한국 전용) - 쿠폰 팝업: 쿠폰(Itembox) 페이지 참고
- 자동 팝업:
- 이벤트 처리: 표시 완료 콜백과 팝업 종료 콜백을 받아 게임 진행을 분기해요. 팝업 닫힘 후 게임 흐름을 재개해야 한다면 Ex 버전 API의 종료 콜백에서 처리해요.
- 부가 제어: 필요 시
View_SetPopupDisallowed로 다시 보지 않기 설정,View_CloseAllPopups로 일괄 닫기 처리해요. - 외부 브라우저 연동(BaseSDK): 인증 세션을 유지한 채 ON스토브 페이지(커뮤니티/고객센터 등)를 외부 브라우저로 열려면 ViewSDK가 아닌 BaseSDK 모듈의
Base_OpenExternalUrl을 호출해요. 스토브 관련 도메인만 허용되며 SSO 로그인이 유지돼요. 비동기 API라 결과는Base_RunCallback()시점의 콜백으로 받아요. - 정리: 게임 종료 직전
View_UnInitialize()로 ViewSDK를 정리한 뒤Base_UnInitialize()를 호출해요.
트러블슈팅
| 상황 | 원인 | 해결 방법 |
|---|---|---|
| 게임 시작 후 자동 팝업을 띄웠는데 아무 창도 뜨지 않아요 | View_Initialize 완료 전에 팝업 API를 호출했어요. ViewSDK 기능은 Base SDK 초기화 후 View_Initialize(내부 모드 팝업은 View_InitializeWithWndInfo)로 ViewSDK를 초기화해야 사용할 수 있어요. | Base SDK 초기화를 완료한 뒤 View_Initialize(내부 모드 팝업은 게임 메인 HWND를 넘기는 View_InitializeWithWndInfo)로 ViewSDK 초기화를 끝내고 팝업 API를 호출하면 문제없어요. |
| 팝업 결과 콜백이 한참 호출되지 않아요 | 메인 루프에서 Base_RunCallback()을 호출하지 않으면 SDK가 결과를 게임에 전달할 시점을 잡지 못해요. 콜백은 Base_RunCallback()을 호출한 스레드에서 실행되도록 설계돼 있어요. | 게임 메인 루프(렌더 루프)에서 매 프레임 또는 일정 주기로 Base_RunCallback()을 호출해야 해요. 보통 입력 처리와 렌더링 사이에 한 번 호출하면 문제없어요. |
한국 외 국가 빌드에서 본인인증 팝업이 NOT_SUPPORTED_COUNTRY(31)로 실패해요 | 본인인증 팝업은 한국 서비스 한정 기능이라 한국 외 국가에서는 항상 31번 오류를 반환해요. 글로벌 빌드에 그대로 호출하면 이용자에게 의미 없는 실패 메시지가 노출돼요. | Base_GetGds로 받은 StovePCGds의 nation(ISO 3166-1 ALPHA-2, 한국이면 "KR") 값을 먼저 확인하고, 한국이 아닐 때는 본인인증 팝업 호출을 스킵해야 해요. 한국 빌드에서만 호출 흐름에 진입하도록 분기를 두면 문제없어요. |
| 새 팝업을 띄웠더니 이전에 떠 있던 팝업이 갑자기 닫혀요 | ViewSDK는 새 팝업 호출 시 동일 채널에 떠 있던 기존 창을 모두 종료해요. 동시에 두 팝업이 노출되는 것을 막기 위한 의도된 동작이에요. | 동시 노출은 지원되지 않으므로, 이전에 떠 있던 팝업이 닫히는 것은 정상 동작이에요. 별도 처리 없이 새 팝업 호출만 진행하면 문제없어요. |
| 팝업이 게임 창 뒤에 가려져 클릭되지 않아요 | ViewSDK 초기화 시 부모 창 핸들을 설정하지 않으면 팝업이 별도 윈도우로 떠서 게임 창보다 뒤로 갈 수 있어요. Exclusive fullscreen 모드에서는 internal 모드 팝업이 Windows API 한계로 게임 창 위로 올라오지 못해요. | View_InitializeWithWndInfo에 게임의 메인 HWND를 넘겨 ViewSDK를 초기화해야 해요. Exclusive fullscreen 모드 게임이라면 팝업의 WebViewMode를 반드시 external 모드로 설정하면 문제없어요. |
외부 브라우저 API(Base_OpenExternalUrl)가 컴파일/링크되지 않거나 호출되지 않아요 | 외부 브라우저 연동은 ViewSDK가 아닌 BaseSDK 모듈 API라, ViewSDK 헤더만 포함하면 심볼을 찾지 못해요. | BaseSDK 헤더(BaseSDK.h)를 포함하고 BaseSDK 초기화가 완료된 상태에서 호출하세요. ViewSDK 초기화 여부와는 무관해요. |
| 외부 브라우저로 연 페이지에서 로그인(SSO)이 풀려요 | 외부 브라우저 연동은 스토브 관련 도메인에서만 SSO 로그인이 유지돼요. 열린 페이지에서 다른 SSO 로그인을 요구하는 페이지로 이동하면 세션이 풀릴 수 있어요. | 스토브 관련 웹페이지(커뮤니티/고객센터 등) URL만 전달하고, 외부 도메인으로 이동하는 흐름은 피하세요. |
팝업 닫힘 후 게임 흐름을 재개하려면 Ex 버전 API의 종료 콜백을 사용해요View_AutoPopupEx, View_ManualPopupEx, View_NewsPopupEx는 표시 완료 콜백과 별개로, 팝업 창이 닫힌 시점에 호출되는 OnViewPopupDestroyFinished 콜백을 함께 받아요. 닫힘 시점에 다음 화면으로 진입하는 등 게임 흐름을 재개해야 한다면 이 종료 콜백에서 처리하세요.
샘플 코드
#include "ViewSDK.h"
using namespace Stove::PCSDK;
using namespace Stove::PCSDK::Base;
using namespace Stove::PCSDK::View;
// 1) ViewSDK 초기화 (Base_Initialize 완료 이후)
auto initResult = View_Initialize();
if (!initResult.IsSuccessful())
{
// 초기화 실패 시 로직을 구현해 주세요.
return;
}
// 2) 자동 팝업 (종료 이벤트 필요 시 Ex)
// 첫 번째 인자는 WebViewMode (EXTERNAL: 외부 브라우저 / INTERNAL: SDK 내장 웹뷰)
View_AutoPopupEx(
WebViewMode::EXTERNAL,
[](CallbackResult openResult) {
if (openResult.GetResult().IsSuccessful())
{
// 팝업 열림 처리
}
},
[](CallbackResult destroyResult) {
// 팝업 종료 시 게임 흐름 재개
}
);
// 3) 수동 팝업 (resourceKey 로 노출할 팝업 지정)
View_ManualPopup(L"your_resource_key", WebViewMode::EXTERNAL, /* onFinished */ nullptr);
// 4) 본인인증 팝업 (한국 전용)
// compareIdentifier=true: SDI 검증 / false: simKey 전달
View_VerifyIdentificationPopup(
true, WebViewMode::INTERNAL,
[](CallbackResult result) {
if (!result.GetResult().IsSuccessful())
{
// NOT_SUPPORTED_COUNTRY(31) 등 실패 시 로직
}
},
[](CallbackResult result, const wchar_t* simKey) {
// 팝업 종료 시 처리 (compareIdentifier=false 일 때 simKey 전달)
});
// 5) 외부 브라우저 열기 (BaseSDK 모듈 API — ViewSDK 아님, BaseSDK.h)
// 스토브 관련 도메인만 허용, SSO 유지.
{
std::wstring url = L"https://www.onstove.com";
Base_OpenExternalUrl(url.c_str(), [](CallbackResult callbackResult) {
if (callbackResult.GetResult().IsSuccessful())
{
// 외부 브라우저 연동 성공 처리
}
});
}
// 6) 종료 시 정리
View_UnInitialize();
// 이후 Base_UnInitialize 호출