본문으로 건너뛰기

기본 환경 설정

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));
}
}