본문으로 건너뛰기

기본 환경 설정

Unity C#에서 PlayNANOO REST API를 사용하기 위한 기본 환경설정 방법을 안내합니다.

1. 사전 준비사항​

PlayNANOO 콘솔에서 다음 정보를 미리 발급받아야 합니다.

API 인증 정보 발급​

PlayNANOO 콘솔에 접속하여 다음 정보를 확인하세요.

발급 경로: 콘솔 > 채널 > 설정 > API & 플랫폼 관리

발급받아야 할 정보:

  • Game ID: 채널 고유 식별자
  • Service Key: API 요청 시 사용되는 서비스 키
  • Secret Key: 보안 서명 생성에 사용되는 비밀 키
보안 주의사항

Secret Key는 절대로 노출되어서는 안되며, 환경변수나 별도 설정파일로 관리하세요.

2. Unity C# 구현​

Unity에서 PlayNANOO API를 호출하기 위한 HTTP 클라이언트를 구현합니다.

2.1 기본 설정​

먼저 발급받은 인증 정보를 설정합니다.

public static class HttpClient
{
// 콘솔에서 발급받은 인증 정보
private static string ServiceKey = "발급받은_서비스_키";
private static string GameId = "발급받은_게임_아이디";
private static string SecretKey = "발급받은_시크릿_키";

public static string GetServiceKey
{
get { return ServiceKey; }
}

public static string GetGameId
{
get { return GameId; }
}

public static string GetSecretKey
{
get { return SecretKey; }
}
}

2.2 요청 클래스 작성 방법​

모든 API 요청 클래스는 DeviceInfo를 상속받아야 합니다.

[Serializable]
public class Req : DeviceInfo
{
public string my_param; // API 고유 파라미터
}

DeviceInfo는 다음 정보를 자동으로 포함합니다:

  • uuid, nickname: 플레이어 정보 (로그인 후 자동 포함)
  • device_id, version, platform: 기기 정보
  • device_model, device_os, device_language: 기기 상세 정보
  • device_country, device_timeoffset: 지역/시간대 정보
팁

DeviceInfo 클래스와 Utils 헬퍼 함수의 전체 구현은 섹션 3. 전체 예제 코드를 참고하세요.

2.3 HTTP 요청 함수 구현​

API 요청을 위한 범용 Send 함수를 구현합니다.

using System;
using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;

public static IEnumerator Send<TReq, TRes>(
string method, // HTTP 메서드 (GET, POST, PUT, DELETE)
string url, // API 전체 URL
bool requireToken, // 인증 토큰 필요 여부
TReq body, // 요청 바디 (GET 제외)
Action<TRes> onSuccess, // 성공 콜백
Action<BaseResponse> onError, // 실패 콜백
int timeoutSec = 10) // 타임아웃 (기본 10초)
{
UnityWebRequest www;

// GET 요청
if (method == UnityWebRequest.kHttpVerbGET)
{
www = UnityWebRequest.Get(url);
}
// POST, PUT, DELETE 등
else
{
string json = body != null ? JsonUtility.ToJson(body) : "{}";
www = new UnityWebRequest(url, method);
www.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(json));
www.downloadHandler = new DownloadHandlerBuffer();
www.SetRequestHeader("Content-Type", "application/json");
}

if (www.downloadHandler == null)
www.downloadHandler = new DownloadHandlerBuffer();

// 공통 헤더 설정
www.SetRequestHeader("Accept", "application/json");
www.SetRequestHeader("X-Playnanoo-Key", ServiceKey);
www.SetRequestHeader("X-Playnanoo-Id", GameId);

// 인증 토큰이 필요한 경우 Authorization 헤더 추가
if (requireToken && DataManager.Instance.AccessToken != null)
{
www.SetRequestHeader("Authorization", $"Bearer {DataManager.Instance.AccessToken}");
}

www.timeout = timeoutSec;
yield return www.SendWebRequest();

// 응답 처리
if (www.result == UnityWebRequest.Result.Success &&
www.responseCode >= 200 && www.responseCode < 300)
{
var text = www.downloadHandler.text;
var data = string.IsNullOrWhiteSpace(text)
? default
: JsonUtility.FromJson<TRes>(text);
onSuccess?.Invoke(data);
}
else
{
var text = www.downloadHandler.text;
BaseResponse data;

if (string.IsNullOrWhiteSpace(text))
{
// 응답 본문이 없으면 HTTP 상태 코드와 에러 메시지로 BaseResponse 생성
data = new BaseResponse
{
ErrorCode = www.responseCode.ToString(),
Message = www.error ?? "Unknown error"
};
}
else
{
data = JsonUtility.FromJson<BaseResponse>(text);
}

onError?.Invoke(data);
}

www.Dispose();
}

2.4 유틸리티 메서드​

API 호출에 필요한 유틸리티 메서드들입니다.

GenerateAuthKey()​

헬프데스크 인증용 AuthKey를 생성합니다.

public static string GenerateAuthKey(string userId)
{
string data = $"{GameId}{userId}";
return Convert.ToBase64String(Encoding.UTF8.GetBytes(data));
}

사용 예시:

string userId = DataManager.Instance.UUID;
string authKey = HttpClient.GenerateAuthKey(userId);

// 헬프데스크 URL에 포함
string url = $"https://help.playnanoo.com/{HttpClient.GetGameId}?userId={userId}&authKey={authKey}";

3. 전체 예제 코드​

위의 내용을 모두 포함한 전체 코드입니다. DeviceInfo와 HttpClient는 별도 파일로 관리됩니다.

DeviceInfo.cs​

기기 정보를 자동으로 수집하는 베이스 클래스입니다.

using System;
using UnityEngine;

// 기기 정보 베이스 클래스
[Serializable]
public class DeviceInfo
{
public string uuid;
public string nickname;
public string device_id;
public string version;
public string platform;
public string device_model;
public string device_os;
public string device_language;
public string device_country;
public string device_timeoffset;

public DeviceInfo()
{
uuid = DataManager.Instance.UUID;
nickname = DataManager.Instance.Nickname;
device_id = SystemInfo.deviceUniqueIdentifier;
version = DataManager.Instance.VersionNumber.ToString();
platform = DataManager.Instance.Platform;
device_model = SystemInfo.deviceModel;
device_os = SystemInfo.operatingSystem;
device_language = GetDeviceLanguage();
device_country = GetCountryCode();
device_timeoffset = GetTimeOffset().ToString();
}

private string GetDeviceLanguage()
{
switch (Application.systemLanguage)
{
case SystemLanguage.Afrikaans: return "AF";
case SystemLanguage.Arabic: return "AR";
case SystemLanguage.Basque: return "EU";
case SystemLanguage.Belarusian: return "BE";
case SystemLanguage.Bulgarian: return "BG";
case SystemLanguage.Catalan: return "CA";
case SystemLanguage.Chinese:
case SystemLanguage.ChineseSimplified: return "zh-CN";
case SystemLanguage.ChineseTraditional: return "zh-TW";
case SystemLanguage.Czech: return "CS";
case SystemLanguage.Danish: return "DA";
case SystemLanguage.Dutch: return "NL";
case SystemLanguage.English: return "EN";
case SystemLanguage.Estonian: return "ET";
case SystemLanguage.Faroese: return "FO";
case SystemLanguage.Finnish: return "FI";
case SystemLanguage.French: return "FR";
case SystemLanguage.German: return "DE";
case SystemLanguage.Greek: return "EL";
case SystemLanguage.Hebrew: return "IW";
case SystemLanguage.Hungarian: return "HU";
case SystemLanguage.Icelandic: return "IS";
case SystemLanguage.Indonesian: return "IN";
case SystemLanguage.Italian: return "IT";
case SystemLanguage.Japanese: return "JA";
case SystemLanguage.Korean: return "KO";
case SystemLanguage.Latvian: return "LV";
case SystemLanguage.Lithuanian: return "LT";
case SystemLanguage.Norwegian: return "NO";
case SystemLanguage.Polish: return "PL";
case SystemLanguage.Portuguese: return "PT";
case SystemLanguage.Romanian: return "RO";
case SystemLanguage.Russian: return "RU";
case SystemLanguage.SerboCroatian: return "SH";
case SystemLanguage.Slovak: return "SK";
case SystemLanguage.Slovenian: return "SL";
case SystemLanguage.Spanish: return "ES";
case SystemLanguage.Swedish: return "SV";
case SystemLanguage.Thai: return "TH";
case SystemLanguage.Turkish: return "TR";
case SystemLanguage.Ukrainian: return "UK";
case SystemLanguage.Vietnamese: return "VI";
default: return "unknown";
}
}

private string GetCountryCode()
{
switch (Application.systemLanguage)
{
case SystemLanguage.Afrikaans: return "ZA";
case SystemLanguage.Arabic: return "SA";
case SystemLanguage.Basque: return "EU";
case SystemLanguage.Belarusian: return "BY";
case SystemLanguage.Bulgarian: return "BE";
case SystemLanguage.Catalan: return "EU";
case SystemLanguage.Chinese:
case SystemLanguage.ChineseSimplified: return "CN";
case SystemLanguage.ChineseTraditional: return "TW";
case SystemLanguage.Czech: return "CZ";
case SystemLanguage.Danish: return "DK";
case SystemLanguage.Dutch: return "NL";
case SystemLanguage.English: return "US";
case SystemLanguage.Estonian: return "EE";
case SystemLanguage.Faroese: return "FO";
case SystemLanguage.Finnish: return "FI";
case SystemLanguage.French: return "FR";
case SystemLanguage.German: return "DE";
case SystemLanguage.Greek: return "GR";
case SystemLanguage.Hebrew: return "IL";
case SystemLanguage.Hungarian: return "HU";
case SystemLanguage.Icelandic: return "IS";
case SystemLanguage.Indonesian: return "ID";
case SystemLanguage.Italian: return "IT";
case SystemLanguage.Japanese: return "JP";
case SystemLanguage.Korean: return "KR";
case SystemLanguage.Latvian: return "LV";
case SystemLanguage.Lithuanian: return "LT";
case SystemLanguage.Norwegian: return "NO";
case SystemLanguage.Polish: return "PL";
case SystemLanguage.Portuguese: return "PT";
case SystemLanguage.Romanian: return "RO";
case SystemLanguage.Russian: return "RU";
case SystemLanguage.SerboCroatian: return "HR";
case SystemLanguage.Slovak: return "SK";
case SystemLanguage.Slovenian: return "SI";
case SystemLanguage.Spanish: return "ES";
case SystemLanguage.Swedish: return "SE";
case SystemLanguage.Thai: return "TH";
case SystemLanguage.Turkish: return "TR";
case SystemLanguage.Ukrainian: return "UA";
case SystemLanguage.Vietnamese: return "VN";
default: return "US";
}
}

private double GetTimeOffset()
{
DateTime now = DateTime.Now;
DateTime utc = DateTime.UtcNow;
TimeSpan timeSpan = now.Subtract(utc);
return Math.Round(timeSpan.TotalSeconds);
}
}

HttpClient.cs​

PlayNANOO API 호출을 위한 HTTP 클라이언트입니다.

using System;
using System.Collections;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;

public static class HttpClient
{
private static string ServiceKey = "발급받은_서비스_키";
private static string GameId = "발급받은_게임_아이디";
private static string SecretKey = "발급받은_시크릿_키";

public static string GetServiceKey
{
get { return ServiceKey; }
}

public static string GetGameId
{
get { return GameId; }
}

public static string GetSecretKey
{
get { return SecretKey; }
}

public static IEnumerator Send<TReq, TRes>(
string method,
string url,
bool requireToken,
TReq body,
Action<TRes> onSuccess,
Action<BaseResponse> onError,
int timeoutSec = 10)
{
UnityWebRequest www;

// GET 요청
if (method == UnityWebRequest.kHttpVerbGET)
{
www = UnityWebRequest.Get(url);
}
// POST, PUT, DELETE 등
else
{
string json = body != null ? JsonUtility.ToJson(body) : "{}";
www = new UnityWebRequest(url, method);
www.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(json));
www.downloadHandler = new DownloadHandlerBuffer();
www.SetRequestHeader("Content-Type", "application/json");
}

if (www.downloadHandler == null)
www.downloadHandler = new DownloadHandlerBuffer();

// 공통 헤더 설정
www.SetRequestHeader("Accept", "application/json");
www.SetRequestHeader("X-Playnanoo-Key", ServiceKey);
www.SetRequestHeader("X-Playnanoo-Id", GameId);

// 인증 토큰이 필요한 경우 Authorization 헤더 추가
if (requireToken && DataManager.Instance.AccessToken != null)
{
www.SetRequestHeader("Authorization", $"Bearer {DataManager.Instance.AccessToken}");
}

www.timeout = timeoutSec;
yield return www.SendWebRequest();

// 응답 처리
if (www.result == UnityWebRequest.Result.Success &&
www.responseCode >= 200 && www.responseCode < 300)
{
var text = www.downloadHandler.text;
var data = string.IsNullOrWhiteSpace(text)
? default
: JsonUtility.FromJson<TRes>(text);
onSuccess?.Invoke(data);
}
else
{
var text = www.downloadHandler.text;
BaseResponse data;

if (string.IsNullOrWhiteSpace(text))
{
// 응답 본문이 없으면 HTTP 상태 코드와 에러 메시지로 BaseResponse 생성
data = new BaseResponse
{
ErrorCode = www.responseCode.ToString(),
Message = www.error ?? "Unknown error"
};
}
else
{
data = JsonUtility.FromJson<BaseResponse>(text);
}

onError?.Invoke(data);
}

www.Dispose();
}

public static string GenerateAuthKey(string userId)
{
string data = $"{GameId}{userId}";
return Convert.ToBase64String(Encoding.UTF8.GetBytes(data));
}
}