728x90
반응형

HTTP(Hyper Text Transfer Protocol) 통신이란?

  • Hyper Text라는 말에서 알 수 있듯이 텍스트 기반의 전송 통신이지만 이미지, 비디오 등의 이진 데이터도 전송할 수 있습니다.
  • HTTP는 여러 유저가 실시간으로 정보를 전송하는 것이 아니라 각각의 클라이언트에서 서버에 요청하고 응답을 보내주는 비동기적인 서버 통신 방법입니다.
  • 실시간 쌍방통신이 크게 필요하지 않은 경우에 주로 사용됩니다.
  • 클라이언트에서 HTTP Request를 통해 서버에 요청하면 서버 내부적으로 요청을 처리하고 HTTP Response로 결과를 전송합니다.

  • HTTP에는 다양한 메소드들이 있지만 Lost Taste는 Get과 Post만 사용했습니다.

 

유니티에서의 HTTP 통신

  • 유니티에서 HTTP 통신을 위해 UnityWebRequest를 사용했습니다.
  • UnityWebRequest는 GET, POST, PUT, DELETE 등 다양한 HTTP 메소드를 지원합니다. 이를 통해 REST API와의 상호작용이 가능하며, 게임 내에서 데이터를 생성, 조회, 수정, 삭제하는 다양한 네트워크 요청을 처리할 수 있습니다.
  • 유니티 Http 통신은 코루틴을 사용하는데, 코루틴은 작업을 순차적으로 실행하면서 필요에 따라 실행을 일시 중단하고 나중에 다시 시작할 수 있습니다. 주로 대기 시간이 긴 비동기 작업을 처리하는 데 유용합니다.
  • 일반 함수에서 HTTP 통신을 수행한다면, 유니티는 HTTP 요청을 보낸 후 다른 작업을 실행하지 않고 응답을 기다릴 것입니다. 이 때 코루틴을 사용한다면 요청 후 응답을 기다리는 동안 유니티에 제어권을 반납하고, 응답을 받으면 나머지 작업을 수행하게 됩니다.

 

Lost Taste에 사용한 HTTP 프로토콜

  • 사실 HTTP 통신의 수가 적었고 다른 신경 쓸 것도 많아서 날림으로 했던 것 같습니다.

 

GET

  • Get 방식은 요청 데이터의 정보를 주소에 담아서 전송한다. 유니티 환경에서도 이 방식은 동일하게 작동하지만, 웹 브라우저와 달리 주소창이 없기 때문에 URL이 사용자에게 직접 노출되지 않습니다.
  • GetCall(string path) 와 IEnumerator GET(string path)를 세트로 묶어서 관리합니다.
  • GetCall은 GET 요청을 시작하기 위해 사용되는 퍼블릭 메소드입니다.
  • 이 메소드 내에서 GET 코루틴을 호출하여 비동기적으로 HTTP GET 요청을 수행합니다.
  • GetCall은 주로 외부에서 쉽게 접근하고 사용할 수 있도록 인터페이스를 제공하는 역할을 하며, 실제 네트워크 통신 로직은 GET 코루틴 내에서 처리됩니다.
  • UnityWebRequest.Get를 사용해 api 주소로 get 통신을 보냅니다.
IEnumerator GET(string path)
{
    UnityWebRequest request = UnityWebRequest.Get(url + path);

 

  • HTTP 요청 헤더에 "Authorization" 헤더를 설정합니다.
  • 보안을 위해 사용자의 Access Token을 사용해서 서버와 통신합니다.
request.SetRequestHeader("Authorization", "Bearer " + UserInfo.GetInstance().getToken());

 

  • 해당 부분은 좀 날림으로 작성했는데 로그인이 성공하면 유저의 정보를 받아서 저장하는 부분입니다.
  • if (request.result != UnityWebRequest.Result.Success) 를 통해 성공 실패 로직을 정리합니다.
  • 성공한다면 UserInfo에 유저 정보를 저장합니다.
if (request.result != UnityWebRequest.Result.Success) 
    {
        if (path.Equals("user/profile"))
        {
            GameObject.Find("Canvas/Authentication Panel").GetComponent<Login>().ShowConnecting();
        }
    }
    else
    {
        if(path.Equals("user/profile"))
        {
            UserData data = JsonUtility.FromJson<UserData>(request.downloadHandler.text);
            

            UserInfo.GetInstance().SetId(data.id);
            UserInfo.GetInstance().SetNickName(data.nickname);

            GameObject.Find("Canvas/Authentication Panel").GetComponent<Login>().CloseAuthenticationPanel();
        }
    }

 

  • 전체 GET 함수
더보기
// GET
// 경로만 지정
IEnumerator GET(string path)
{
    UnityWebRequest request = UnityWebRequest.Get(url + path);
    request.SetRequestHeader("Authorization", "Bearer " + UserInfo.GetInstance().getToken());
    
    yield return request.SendWebRequest();

    if (request.result != UnityWebRequest.Result.Success) // Unity 2020.1 이후부터는 isNetworkError와 isHttpError 대신 result 사용
    {
        if (path.Equals("user/profile"))
        {
            GameObject.Find("Canvas/Authentication Panel").GetComponent<Login>().ShowConnecting();
        }
        
        //Debug.Log(request.error);
    }
    else
    {
        if(path.Equals("user/profile"))
        {
            UserData data = JsonUtility.FromJson<UserData>(request.downloadHandler.text);
            

            UserInfo.GetInstance().SetId(data.id);
            UserInfo.GetInstance().SetNickName(data.nickname);

            GameObject.Find("Canvas/Authentication Panel").GetComponent<Login>().CloseAuthenticationPanel();
        }
    }

}
public void GetCall(string path)
{
    StartCoroutine(GET(path));
}

 

POST

  • Post는 요청 데이터의 정보를 http 헤더에 담아서 전달하기 때문에 Get에 비해 보안성이 좋습니다.
  • POSTCall(string path, Dictionary<string, string> postParam) 와 IEnumerator POST(string path, Dictionary<string, string> postParam)를 세트로 묶어서 관리합니다.
  • Post 처리를 위해 json 문자열을 만들어야 하는데 파라미터를 Key-Value 형태의 Dictionary로 받았기 때문에 이것을 json 형태로 바꿔줘야 합니다.
  • ‘이렇게 하는 방법도 있다’로 봐주면 좋겠는데, 사실 클래스 형태로 만들면 [System.Serializable]를 사용해서 객체를 직렬화하고 json으로 변환 후 보낼 수 있고 데이터를 받을 때도 역직렬화로 바로 객체에 저장할 수 있습니다.
  • 이 방법은 for문을 돌려서 json 형태의 문자열을 만듭니다.
 // Dictionary를 직접 JSON 문자열로 변환
 StringBuilder jsonDataBuilder = new StringBuilder("{");
 foreach (var item in postParam)
 {
     jsonDataBuilder.Append($"\\"{item.Key}\\":\\"{item.Value}\\",");
 }
 if (jsonDataBuilder.Length > 1) // 마지막 쉼표를 제거하기 위함
 {
     jsonDataBuilder.Remove(jsonDataBuilder.Length - 1, 1);
 }
 jsonDataBuilder.Append("}");

 

  • 그 후 StringBuilder를 string 형태로 변환합니다.
string jsonData = jsonDataBuilder.ToString();

 

  • 변환된 데이터는 byte 형태의 배열로 변경되는데, byte를 사용하는 이유는 데이터를 네트워크를 통해 전송하거나 저장할 때 더 효율적으로 처리할 수 있기 때문입니다.
  • 데이터를 그대로 이진 형태로 전송하므로, 변환 과정에서 발생할 수 있는 오버헤드가 줄어들어서 데이터 전송 과정에서 발생할 수 있는 여러 가지 복잡한 문제들을 단순화할 수 있습니다.
byte[] jsonToSend = new UTF8Encoding().GetBytes(jsonData);

 

  • post 요청을 보내기 위해 UnityWebRequest객체를 만드는데 이 때 파라미터로 경로와 사용할 메소드를 전달해 서버의 특정 주소로 POST 방식으로 전송하겠다는 의미 입니다.
  • 서버로 보낼 데이터를 처리하는 UploadHandlerRaw를 생성하고 이를 postRequest의 uploadHandler 속성에 할당합니다. UploadHandlerRaw 생성자의 인자로는 보낼 데이터의 바이트 배열을 넣습니다.
  • 서버의 응답을 받기 위해 DownloadHandlerBuffer를 사용하여 postRequest의 downloadHandler 속성을 설정합니다. 이 핸들러는 메모리 버퍼에 데이터를 저장하며, 응답을 받은 후에 이를 읽을 수 있습니다.
  • HTTP 요청 헤더를 설정합니다. 여기서는 "Content-Type" 헤더를 "application/json"으로 설정하여, 전송하는 컨텐츠의 타입이 JSON임을 명시합니다.
UnityWebRequest postRequest = new UnityWebRequest(url + path, "POST");
postRequest.uploadHandler = (UploadHandler)new UploadHandlerRaw(jsonToSend);
postRequest.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); 
postRequest.SetRequestHeader("Content-Type", "application/json");

 

  • postRequest가 결과를 받을 때 까지 대기합니다.
yield return postRequest.SendWebRequest();

 

  • response 받는 데 실패했다면 if문을 사용해서 받은 경로에 따라 결과를 다르게 해야 합니다.
  • msg = data.message[0] 에러 메세지 중 가장 앞의 메세지를 출력합니다.
if (postRequest.result != UnityWebRequest.Result.Success)
{
    UserData data = JsonUtility.FromJson<UserData>(postRequest.downloadHandler.text);

    if (path.Equals("user")) // 회원가입일 경우
    {
        // 로그인 화면으로 이동
        Login login = GameObject.Find("Canvas/Authentication Panel").GetComponent<Login>();
        login.SignupFailure();
        string msg = "ID : 4-6 영숫자    닉네임 : 1-16\\n비밀번호 : 8-32 영숫자특수문자";
        if (data.message.Length > 0)
            msg = data.message[0];
        login.ShowWarnMessage(msg);
    }
    else if (path.Equals("auth/login")) // 로그인일 경우
    {
        string msg = "사용자 정보를 불러올 수 없습니다.";
        if (data.message.Length>0)
            msg = data.message[0];

        Login login = GameObject.Find("Canvas/Authentication Panel").GetComponent<Login>();
        login.LoginFailure();
        login.ShowWarnMessage(msg);
    }
}

 

  • 통신에 성공한 경우, 회원가입이면 바로 로그인창으로 보내고, 로그인이면 리턴받은 토큰을 저장합니다.
  • 이 토큰이 없으면 모든 http 통신을 할 수 없습니다.
  • 로그인이 성공했으면 Get 호출로 유저 데이터를 받아서 저장합니다.
else // 통신 성공
{

    if (path.Equals("user")) // 회원가입일 경우
    {
        // 로그인 화면으로 이동
        Login login = GameObject.Find("Canvas/Authentication Panel").GetComponent<Login>();
        login.ShowLoginPanel();
    }
    else if (path.Equals("auth/login")) // 로그인일 경우
    {
        UserData data = JsonUtility.FromJson<UserData>(postRequest.downloadHandler.text);

        //액세스 토큰으로 id, pw 받아오는 get 보내야함
        UserInfo.GetInstance().SetToken(data.accessToken);

        GameObject.Find("Canvas/Authentication Panel").GetComponent<Login>().ShowConnecting();
        GetCall("user/profile");
    }
}

 

  • 유저 데이터는 JSON으로 변환하고 JSON에서 파싱하기 위해 serializable을 해줘야 합니다.
[System.Serializable]
class UserData
{
    public string accessToken;
    public string id;
    public string nickname;
    public string error;
    public string[] message;
}

 

  • 전체 post 함수
더보기
// POST
// url + path = uri
// dictionary로 값 전달
IEnumerator POST(string path, Dictionary<string, string> postParam)
{
    // Dictionary를 직접 JSON 문자열로 변환
    StringBuilder jsonDataBuilder = new StringBuilder("{");
    foreach (var item in postParam)
    {
        jsonDataBuilder.Append($"\"{item.Key}\":\"{item.Value}\",");
    }
    if (jsonDataBuilder.Length > 1) // 마지막 쉼표를 제거하기 위함
    {
        jsonDataBuilder.Remove(jsonDataBuilder.Length - 1, 1);
    }
    jsonDataBuilder.Append("}");
    string jsonData = jsonDataBuilder.ToString();

    byte[] jsonToSend = new UTF8Encoding().GetBytes(jsonData);
    UnityWebRequest postRequest = new UnityWebRequest(url + path, "POST");
    postRequest.uploadHandler = (UploadHandler)new UploadHandlerRaw(jsonToSend);
    postRequest.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); 
    postRequest.SetRequestHeader("Content-Type", "application/json");

    yield return postRequest.SendWebRequest();

    if (postRequest.result != UnityWebRequest.Result.Success)
    {
        //Debug.LogError("error" + postRequest.error);
        //Debug.Log("result" + postRequest.result);
        UserData data = JsonUtility.FromJson<UserData>(postRequest.downloadHandler.text);

        if (path.Equals("user")) // 회원가입일 경우
        {
            // 로그인 화면으로 이동
            Login login = GameObject.Find("Canvas/Authentication Panel").GetComponent<Login>();
            login.SignupFailure();
            string msg = "ID : 4-6 영숫자    닉네임 : 1-16\n비밀번호 : 8-32 영숫자특수문자";
            if (data.message.Length > 0)
                msg = data.message[0];
            login.ShowWarnMessage(msg);
        }
        else if (path.Equals("auth/login")) // 로그인일 경우
        {
            string msg = "사용자 정보를 불러올 수 없습니다.";
            if (data.message.Length>0)
                msg = data.message[0];

            Login login = GameObject.Find("Canvas/Authentication Panel").GetComponent<Login>();
            login.LoginFailure();
            login.ShowWarnMessage(msg);
        }
    }
    else // 통신 성공
    {

        if (path.Equals("user")) // 회원가입일 경우
        {
            // 로그인 화면으로 이동
            Login login = GameObject.Find("Canvas/Authentication Panel").GetComponent<Login>();
            login.ShowLoginPanel();
        }
        else if (path.Equals("auth/login")) // 로그인일 경우
        {
            UserData data = JsonUtility.FromJson<UserData>(postRequest.downloadHandler.text);

            //액세스 토큰으로 id, pw 받아오는 get 보내야함
            UserInfo.GetInstance().SetToken(data.accessToken);

            GameObject.Find("Canvas/Authentication Panel").GetComponent<Login>().ShowConnecting();
            GetCall("user/profile");
        }
    }
}
728x90
반응형

'공부 > 유니티' 카테고리의 다른 글

디스코드 자체 게임젬  (0) 2024.08.21
유니티 Find 정리  (1) 2024.04.30
미로게임5 (22.6.15)  (0) 2024.04.22
미로게임4 (22.6.7)  (0) 2024.04.22
미로게임3 (22.6.2)  (0) 2024.04.22