공부/인프런 - Rookiss

Part 6-7-5. WebAPI와 REST 서비스 : Unity와 WebApi 연동

셩잇님 2024. 10. 17. 15:39
반응형

 

 

🌀 WebAPI와 REST 서비스

 

 지난 시간에는 Blazor와 WebApi를 연동시켜주었다. 이를 통해 ApplicationDbContext를 사용하지 않고 HttpClient를 사용하여 Blazor와 WebApi와 서로 통신할 수 있도록 처리해주었다. 이 후 각각의 CRUD를 Json을 통해 전달하면서 SerializeObject, DeserializeObject를 통해 JSON 형태 ↔ Objectc 형태로 변환하는 과정을 통해 통신하였다. 이번 시간에는 Unity와 WebApi 프로젝트를 연동시켜 보는 시간을 가져보도록 한다.

 


 

🐊 Unity와 WebApi 연동

 

 기본적으로 Unity와 WebApi를 연동하는 것은 크게 Blazor와 다르지 않다. 유니티 허브에서 WebTest 프로젝트를 새롭게 만들어주도록 하자. 이 후 WebManager 스크립트를 생성하고, 빈 오브젝트를 하나 생성하여 이름을 WebManager로 설정해주자. 이 후 스크립트를 열어 코루틴을 이용하여 WebAPI와 통신해보자.

 

 해당 코루틴에서 뭐가 필요한지 곰곰히 생각해보면 상대방에게 보낼 URL 주소가 필요하고, 이 후에는 REST가 메서드인 'GET, POST, UPDATE, DELETE'가 필요할 것이다. 마지막으로 어떤 오브젝트가 있을 터이니 object를 하나 보내주도록 하자. URL의 주소의 경우 메인 주소는 거의 변경되지 않으므로 BaseURL 변수를 만들어 이를 설정해주도록 하자. 

 

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class WebManager : MonoBehaviour
{
    string _baseUrl = "http://localhost:44360";

    void Start()
    {

    }

    IEnumerator CoSendWebRequest(string url, string method, object obj)
    {
        yield return null;
    }
}

 

 이 후 통신을 위해 GameResult의 데이터가 필요하다. 지금 당장은 모든 데이터가 필요하지 않으므로 UserName과 Score만 설정해주도록 하자. 이 후 Start() 함수내에서 GameResult 생성자를 통해서 데이터를 생성하고 이를 보내준다고 가정해보자. 사실 Start 함수에서 하는 것이 아니라 이는 게임이 끝날 때, 혹은 필요 시 호출해야 하지만 지금 당장은 테스트를 위해 Start 함수에서 이를 진행해보도록 하자.

 

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class GameResult
{
    public string UserName;
    public int Score;
}

public class WebManager : MonoBehaviour
{
    string _baseUrl = "http://localhost:44360";

    void Start()
    {
        GameResult res = new GameResult()
        {
            UserName = "Shung",
            Score = 555
        };
        
        StartCoroutine.CoSendWebRequest("ranking", "POST", res);
    }

    IEnumerator CoSendWebRequest(string url, string method, object obj)
    {
        yield return null;
    }
}

 

 위와 같이 작성할 경우 Start 함수에서 유니티를 실행하자마자 POST Req를 보내어 새로운 정보를 만들어 줄 것이다. 이 후 어찌되었던 Object를 받았는데 이를 Blazor에서와 한 것과 동일하게 웹 상으로 이 데이터를 보내주기 위해서 Serialize를 처리해서 JOSN 형태로 변경한 뒤 웹으로 보내주어야 한다. 

 

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class GameResult
{
    public string UserName;
    public int Score;
}

public class WebManager : MonoBehaviour
{
    string _baseUrl = "http://localhost:44360";

    void Start()
    {
        GameResult res = new GameResult()
        {
            UserName = "Shung",
            Score = 555
        };
        
        StartCoroutine.CoSendWebRequest("ranking", "POST", res);
    }

    IEnumerator CoSendWebRequest(string url, string method, object obj)
    {
        yield return null;
        
        string sendUrl = $"{_baseUrl}/{url}";

        byte[] jsonBytes = null;
        if (obj != null)
        {
            string json = JsonUtility.ToJson(obj);
            jsonBytes = System.Text.Encoding.UTF8.GetBytes(json);
        }
    }
}

 

 이 다음으로는 WebRequest를 이용해야 한다. WebRequest는 Unity에서도 지원하고 있기 때문에 UnityWebRequest를 사용하면 된다. 이 후 업로드 핸들러와, 다운로드 핸들러를 각각 연동해준다. 업로드 시에는 바이트 배열을 연동해주고, 다운로드 시에는 응답이 왔을 때 사용할 수 있도록 버퍼 downloadHanlerBuffer를 할당해준다. 이 후 SetRequestHaer를 통해 우리가 보내는 정보가 어떤 형식으로 만들어져 있는지를 알려주기 위해 application/json으로 설정해주고 이를 보내면 된다.

 

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class GameResult
{
    public string UserName;
    public int Score;
}

public class WebManager : MonoBehaviour
{
    string _baseUrl = "http://localhost:44360";

    void Start()
    {
        GameResult res = new GameResult()
        {
            UserName = "Shung",
            Score = 555
        };
        
        StartCoroutine.CoSendWebRequest("ranking", "POST", res);
    }

    IEnumerator CoSendWebRequest(string url, string method, object obj)
    {
        yield return null;
        
        string sendUrl = $"{_baseUrl}/{url}";

        byte[] jsonBytes = null;
        if (obj != null)
        {
            string json = JsonUtility.ToJson(obj);
            jsonBytes = System.Text.Encoding.UTF8.GetBytes(json);
        }
        
        var uwr = new UnityWebRequest(sendUrl, method);
        uwr.uploadHandler = new UploadHandlerRaw(jsonBytes);
        uwr.downloadHandler = new DownloadHandlerBuffer();
        uwr.SetRequestHeader("Content-Type", "application/json");

        yield return uwr.SendWebRequest();
    }
}

 

 이 후 요청을 보낸 다음에 요청이 완료되면, SendWebRequest로 부터 결과가 올 것인데 해당 결과가 문제가 있는지 확인하기 위해 if 조건문을 통해 문제가 있을 시 로그를 통해 에러를 출력해준다. 만약 에러가 아니라면 받은 데이터가 궁금하기 때문에 이를 로그를 찍어주도록 한다. 이 후 콜백 방식을 통해 이 후에 필요한 작업을 처리하도록 한다. 예를 들어 UI를 띄워서 점수를 보여주는 현황판을 띄어주는 작업을 하는 것이다. 따라서 기존의 함수 내역을 아래와 같이 바꿔주도록 한다.

 

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class GameResult
{
    public string UserName;
    public int Score;
}

public class WebManager : MonoBehaviour
{
    string _baseUrl = "http://localhost:44360";

    void Start()
    {
        GameResult res = new GameResult()
        {
            UserName = "Shung",
            Score = 555
        };
        
        StartCoroutine.CoSendWebRequest("ranking", "POST", res, (uwr) =>
        {
        	Debug.Log("TODO : UI 갱신");
        }));
    }

    IEnumerator CoSendWebRequest(string url, string method, object obj, Action<UnityWebRequest> callback)
    {
        yield return null;

        string sendUrl = $"{_baseUrl}/{url}";

        byte[] jsonBytes = null;
        if (obj != null)
        {
            string json = JsonUtility.ToJson(obj);
            jsonBytes = System.Text.Encoding.UTF8.GetBytes(json);
        }

        var uwr = new UnityWebRequest(sendUrl, method);
        uwr.uploadHandler = new UploadHandlerRaw(jsonBytes);
        uwr.downloadHandler = new DownloadHandlerBuffer();
        uwr.SetRequestHeader("Content-Type", "application/json");

        yield return uwr.SendWebRequest();

        if (uwr.isNetworkError || uwr.isHttpError)
        {
            Debug.Log(uwr.error);
        }
        else
        {
            Debug.Log(uwr.downloadHandler.text);
            callback.Invoke(uwr);
        }
    }
}

 

 변경된 CoSendWebRequest에 따라 함수 호출 인자를 추가로 작성해주고, 해당 콜백 함수가 실행 될 때 UI 갱신과 같은 작업들을 처리해주도록 한다. 지금은 테스트를 위해 Debug.Log를 통해 UI 갱신이라는 함수를 임시로 작성하였다. 이 방식을 통해 GET 메서드로 처리할 수 있겠지만 이를 재사용성을 높게 하기 위해 이 또한 함수로 빼주도록 하자.

 

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class GameResult
{
    public string UserName;
    public int Score;
}

public class WebManager : MonoBehaviour
{
    string _baseUrl = "http://localhost:44360";

    void Start()
    {
        GameResult res = new GameResult()
        {
            UserName = "Shung",
            Score = 555
        };

        // CRUD
        SendPostRequest("ranking", res, (uwr) =>
        {
            Debug.Log("TODO");
        });

        SendGetAllRequest("ranking", (uwr) =>
        {
            Debug.Log("TODO");
        });
    }

    public void SendPostRequest(string url, object obj, Action<UnityWebRequest> callback)
    {
        StartCoroutine(CoSendWebRequest(url, "POST", obj, callback));
    }

    public void SendGetAllRequest(string url, Action<UnityWebRequest> callback)
    {
        StartCoroutine(CoSendWebRequest(url, "GET", null, callback));
    }

    IEnumerator CoSendWebRequest(string url, string method, object obj, Action<UnityWebRequest> callback)
    {
        yield return null;

        ...
    }
}

 

 위와 같이 함수를 이용하면 스크립트를 보다 깔끔하게 관리할 수 있다. 이 후 테스트를 위해 실행을 해보도록 하자. 먼저 서버를 구동해야하기 때문에 Ranking 프로젝트를 켜서 실행시켜주고, 유니티를 다시 구동해보도록 하자. 유니티를 구동하면 아래와 같이 로그가 뜨는 것을 볼 수 있다.

 

 

 각각의 데이터를 살펴보면 GetAll에 대한 데이터와 데이터를 추가한 다음 이 후의 로그임을 알 수 있다. 이 때 자세히 살펴보면 순서가 뒤바뀌었는데 이는 클라이언트에서 관여하는 부분이 아니며, 웹 서버에서 위와 같이 응답을 했기 때문에 웹 서버를 살펴보아야 한다.

 

 마지막으로 REST는 GET, POST, UPDATE, DELETE가 있는데 이 때마다 POST 혹은 GET을 할 지 정해주는 것도 귀찮다. 비록 REST가 비공식적인 표준이긴 하지만, 어찌됐던 표준인 방식처럼 지켜주는 것 처럼 만들어주었다. 하지만 유니티와 웹 서버를 연동할 때에도 이렇게 표준에 집착해야하는가?는 또 다른 문제다. 

 

 따라서 경우에 따라선 모든 내용을 POST 혹은 GET만 이용해서 구현할 수 있다. 예를 들어 어떤 게임의 진행을 위해 무엇인가를 보낸 경우 이를 POST로 처리해야하는지, UPDATE로 해야하는지 애매한 상황이 있을 것이다. 따라서 그 때마다 의미에 중점을 두지말고 그냥 다 똑같은 방식으로 보내서 웹 서버에서 처리하는 방법도 나쁘지 않다. 따라서 꼭 REST의 표준을 무조건적으로 지켜야되는 것이 아니며, 적당히 유도리있게 맞춰주는 것도 나쁘지 않다. 

 

 

반응형