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 |