[unity] 모듈 제작 : UniTask 활용한 Network system 제작
Unity Restful network manager 를 제작하여, 웹서버와 통신하는 모듈을 만들어 보자.
유니티에서 가장 흔하게 사용하는 방식은 UnityWebRequest와 코루틴을 통한 웹서버<->클라이언트 간 통신 방법이 있다.
코루틴을 사용한 방법들은 유니티 레퍼런스에 잘 설명되어 있으니 참고해보면 좋을 것 같다.
https://docs.unity3d.com/ScriptReference/Networking.UnityWebRequest.html
이번 모듈은 코루틴이 아닌 UniTask를 활용하여 제작해 보도록 하겠다.
UniTask는 C#의 Task를 유니티에 맞춰 래핑한 비동기 라이브러리라고 생각하면 좋다.
C#의 async/await를 유니티에 맞게 최적화하여, 기본 Task 보다 가볍다.
유니티의 메인 쓰레드 기반으로 동작하므로 다양한 플랫폼(WebGL,WASM,Android,IOS) 등에서도 활용이 가능하다.
그동안 코루틴과 콜백으로 처리했던 통신 구조를 비동기로 쉽게 바꿔줄 수 있다.(콜백 지옥 안녕~)
그럼 UniTask 라이브러리를 추가해 보도록 하겠다.
현재 깃허브에 공개 프로젝트로 올라와 있으니 패키지 파일을 다운받아 보자.
https://github.com/Cysharp/UniTask/releases
현재 시점에서는 UniTask.2.3.1 이 최신 버전이다.
유니티를 열고, 패키지를 압축풀어준다.
압축을 풀면, Plugins 안에 UniTask란 항목으로 생성이 된다.
UniTask 폴더를 Library 폴더로 이동하여, 라이브러리들만 따로 관리를 하자.
Modules 안에 Network 폴더와 자식 폴더인 Scripts 폴더를 만들어 준 후, NetworkManager, URI 클래스 2개를 만들어 준다.
URI.cs 클래스는 도메인 정보 및 필요한 URL 정보를 정의해준다.
public static class URI
{
public static readonly string DOMAIN = "http://localhost:8080/";
}
NetworkManager.cs 클래스는 restful api 통신 스크립트이다.
using System;
using System.Text;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
namespace Container.Network
{
public enum SENDTYPE
{
GET,
POST,
PUT,
DELETE
}
public class NetworkManager
{
protected static double timeout = 5; //5초 타임아웃.
/// <summary>
/// 서버 전송 함수.
/// </summary>
/// <typeparam name="T">Return Class</typeparam>
/// <param name="url">api url 정보</param>
/// <param name="sendType">Get,Post,Put,Delete</param>
/// <param name="jsonBody">body 정보</param>
/// <returns></returns>
public static async UniTask<T> SendToServer<T>(string url, SENDTYPE sendType, string jsonBody = null)
{
//1. 네트워크 체크.
await CheckNetwork();
//2. API URL 생성.
string requestURL = URI.DOMAIN + url;
//3. Timeout 설정.
var cts = new CancellationTokenSource();
cts.CancelAfterSlim(TimeSpan.FromSeconds(timeout));
//4. 웹 요청 생성(Get,Post,Delete,Update)
UnityWebRequest request = new UnityWebRequest(requestURL, sendType.ToString());
//5. Body 정보 입력
request.downloadHandler = new DownloadHandlerBuffer();
if (!string.IsNullOrEmpty(jsonBody))
{
byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonBody);
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
}
//6. Header 정보 입력
SetHeaders(request);
try
{
var res = await request.SendWebRequest().WithCancellation(cts.Token);
T result = JsonUtility.FromJson<T>(res.downloadHandler.text);
return result;
}
catch (OperationCanceledException ex)
{
if (ex.CancellationToken == cts.Token)
{
Debug.Log("Timeout");
//TODO: 네트워크 재시도 팝업 호출.
//재시도.
return await SendToServer<T>(url, sendType, jsonBody);
}
}
catch (Exception e)
{
Debug.Log(e.Message);
return default;
}
return default;
}
private static async UniTask CheckNetwork()
{
if (Application.internetReachability == NetworkReachability.NotReachable)
{
//TODO: 네트워크 오류 팝업 호출.
Debug.LogError("The network is not connected.");
await UniTask.WaitUntil(() => Application.internetReachability != NetworkReachability.NotReachable);
Debug.Log("The network is connected.");
}
}
private static void SetHeaders(UnityWebRequest request)
{
//필요한 Header 추가.
request.SetRequestHeader("Content-Type", "application/json");
}
}
}
코드를 살펴보면, 상단에 timeout 값을 설정해준다. (서버에서 몇 초 동안 응답없을 경우 처리할지 정의 )
SendToServer UniTask를 사용한 비동기 함수는 웹 서버로 요청하는 함수이다.
기본 인자로 api url 과 전송방식, jsonBody 입력값을 받는다.
함수 플로우는 다음과 같다.
1. 네트워크 연결 여부 체크
2. URL 생성
3. Timeout 생성
4. 웹 요청 정보 생성
5. Json Body 정보 입력
6. Header 정보 입력
7. 요청
8. 응답 처리
9. 리턴 or 재시도(타임아웃 시)
해당 함수를 호출하는 예제 코드는 다음과 같다.
using System;
using Container.Network;
using Cysharp.Threading.Tasks;
using UnityEngine;
[Serializable]
public class UserInfo
{
public string token;
public string username;
public string name;
}
[Serializable]
public class RequestSignInData
{
public string username;
public string password;
public RequestSignInData(string newUsername, string newPassword)
{
username = newUsername;
password = newPassword;
}
}
public class NetworkTest : MonoBehaviour
{
public async UniTask Login(RequestSignInData data)
{
string json = JsonUtility.ToJson(data);
UserInfo info = await NetworkManager.SendToServer<UserInfo>("/login", SENDTYPE.POST, json);
Debug.Log(info);
}
}
username, password 를 입력해 로그인 한 후 UserInfo 데이터를 받는 샘플 코드이다.
실제 프로젝트 진행 시 호출부에 있는 데이터 클래스(UserInfo, RequestSignInData 등)는 따로 분리하는게 좋다.
댓글