뒤끝

[Unity]뒤끝 4 (서버 데이터 불러오기)

때류기 2024. 2. 29. 22:19

저번에는 뒤끝 서버와 연결해 유저 정보를 저장시키는 기능을 구현했었습니다.

이번 글에선 로그인 후 게임내 데이터를 불러오는 기능을 구현해 보도록 하겠습니다.

 

 

데이터 불러오기 구현

 

1. 엑셀을 켜 아래와 같이 작성 후 CSV 파일로 저장합니다.

 

 

 

 

2. 뒤끝 홈페이지에 접속, 로그인 후 차트 탭을 클릭합니다.

 

 

 

3. 상단의 차트 파일 업로드를 클릭후 1번에서 작성했던 CSV파일을 엽니다.

(CSV파일 첫번째 줄에는 대, 소문자 영문, _만 입력할 수 있습니다.)

 

 

 

 

4. 차트에 등록된 것을 보실 수 있습니다.

 

 

 

다음으로 저번까지 작성했던 유니티 파일을 엽니다.

저번과 이어서 작성을 해보겠습니다.

전체 코드는 최하단에 작성해놓았으니 참고하시면 되겠습니다.

 

5. BackendManager.cs에 아래와 같이 함수를 추가합니다.

해당 함수는 차트ID와 반복횟수, 서버 연결 후 실행할 함수를 받아 서버를 연결해주는 함수입니다.

이 함수를 사용하실 때는 Action<BackendReturnObj> onCompleted 에 차트를 받아 변수로 변환해주는 함수를 넣으시면 됩니다.

 /// <summary> 차트 ID와 반복 횟수, 연결이 됬을 경우 실행할 함수를 받아 뒤끝에서 ChartData를 받아오는 함수 </summary>
    public void GetChartData(string selectedProbabilityFileId, int maxRepeatCount = 10, Action<BackendReturnObject> onCompleted = null)
    {
        //로그인이 되어 있지 않을 경우
        if (!Backend.IsLogin)
        {
            Debug.LogError("서버에 로그인 되어있지 않습니다.");
            return;
        }

        //반복횟수를 초과했을 경우
        if (maxRepeatCount <= 0)
        {
            Debug.LogError("연결 실패");
            return;
        }

        //서버에서 Chart데이터를 불러온다.
        BackendReturnObject bro = Backend.Chart.GetOneChartAndSave(selectedProbabilityFileId);

        switch (ErrorCheck(bro))
        {
            case BackendState.Failure:
                Debug.LogError("연결 실패");
                break;

            case BackendState.Maintainance:
                Debug.LogError("서버 점검 중");
                break;

            case BackendState.Retry:
                Debug.LogWarning("연결 재시도");
                GetChartData(selectedProbabilityFileId, maxRepeatCount - 1, onCompleted);
                break;

            case BackendState.Success:
                Debug.Log("차트 정보 받기 성공");
                onCompleted?.Invoke(bro);
                break;
        }
    }

 

 

 

6. ChartDatabase.cs  스크립트를 생성후 아래와 같이 코드를 작성합니다.

이곳에선 서버의 데이터를 받아온 클래스를 넘겨받아 현재 클래스에 맞게 데이터를 변환시켜주는 작업을 합니다.

using BackEnd;
using LitJson;
using UnityEngine;

/// <summary>차트데이터를 저장하는 클래스</summary>
public class ChartDatabase
{
    int _int;
    float _float;
    string _string;
    bool _bool;


    public void LoadData()
    {
        BackendManager.Instance.GetChartData("110270", 10, LoadChartByServer);
    }


    /// <summary>서버에서 전달받은 데이터를 클래스에 맞게 변환해주는 함수</summary>
    private void LoadChartByServer(BackendReturnObject callback)
    {
        JsonData json = callback.FlattenRows();

        for (int i = 0; i < json.Count; i++)
        {
            //json[i]["엑셀 첫번째 줄 이름"]
            _int = int.Parse(json[i]["Int"].ToString());
            _float = float.Parse(json[i]["Float"].ToString());
            _string = json[i]["String"].ToString();
            _bool = bool.Parse(json[i]["Bool"].ToString());

            Debug.LogFormat("Int: {0}, Float: {1}, String: {2}, Bool: {3}", _int, _float, _string, _bool);

        }
        Debug.Log("정보 받아오기 성공!");
    }

}

 

 

GetCharData에 들어가는 변수중 첫번째 변수는 아래와 같이 파일ID를 입력하는 곳입니다.

해당 변수의 값과 같은 차트를 받아오는 역활을 합니다.

 

 

 

7. 이제 LoginManager.cs를 수정할 차례입니다.

빨간색 박스에 들어있는 코드를 추가하시면됩니다.

 

 

 

이제 모든 작업이 끝났습니다.! 

유니티를 실행하여 로그인을 하시면 Console창에 아래와 같이 뜨신다면 성공입니다.

저희가 작성했던 CSV파일과 같이 출력되는 것을 볼 수 있습니다.

 

 

 

이것으로 뒤끝 서버에서 데이터를 불러와 사용하는 방법에 대해 서술했습니다.

쓰면 쓸수록 참으로 편한 서비스인거같습니다.! 이것으로 글을 마치겠습니다.

 

 

 

전체 코드

 

LoginManager.cs

using BackEnd;
using UnityEngine;
using UnityEngine.UI;


public class LoginManager : MonoBehaviour
{
    [Tooltip("아이디 입력 필드")]
    [SerializeField] private InputField _idInput;

    [Tooltip("비밀번호 입력 필드")]
    [SerializeField] private InputField _passwordInput;


    [Tooltip("로그인 버튼")]
    [SerializeField] private Button _loginButton;

    [Tooltip("회원가입 버튼")]
    [SerializeField] private Button _signupButton;


    [Tooltip("로그인 성공 텍스트")]
    [SerializeField] private Text _loginText;



    void Start()
    {
        _loginText.gameObject.SetActive(false);

        //버튼 클릭 이벤트 추가
        _loginButton.onClick.AddListener(Login);
        _signupButton.onClick.AddListener(SignUp);
    }


    /// <summary>로그인 함수</summary>
    private void Login()
    {
        string id = _idInput.text;
        string pw = _passwordInput.text;

        Debug.Log("로그인을 요청합니다.");
        BackendReturnObject bro = Backend.BMember.CustomLogin(id, pw);

        if (bro.IsSuccess())
        {
            Debug.Log("로그인이 성공했습니다. : " + bro);
            HideLoginUI();
            UserDataUpdate();
        }
        else
        {
            Debug.LogError("로그인이 실패했습니다. : " + bro);
        }
    }


    /// <summary>회원가입 함수</summary>
    private void SignUp()
    {
        string id = _idInput.text;
        string pw = _passwordInput.text;

        Debug.Log("회원가입을 요청합니다.");     
        BackendReturnObject bro = Backend.BMember.CustomSignUp(id, pw);

        if (bro.IsSuccess())
        {
            Debug.Log("회원가입에 성공했습니다. : " + bro);
        }
        else
        {
            Debug.LogError("회원가입에 실패했습니다. : " + bro);
        }
    }


    /// <summary>로그인 성공시 로그인 관련 ui들을 비활성화 시키는 함수</summary>
    private void HideLoginUI()
    {
        _idInput.gameObject.SetActive(false);
        _passwordInput.gameObject.SetActive(false);
        _loginButton.gameObject.SetActive(false);
        _signupButton.gameObject.SetActive(false);

        _loginText.gameObject.SetActive(true);
    }


    private BackendGameData _backendGameData = new BackendGameData();
    private ChartDatabase _chartDatabase = new ChartDatabase();
    private void UserDataUpdate()
    {
        _backendGameData.SaveUserData(10);
        _chartDatabase.LoadData();
    }
}

 

 

BackendManager.cs

using BackEnd;
using System;
using UnityEngine;

public enum BackendState
{
    Failure,
    Maintainance,
    Retry,
    Success,
}


public class BackendManager : MonoBehaviour
{
    private static BackendManager instance;

    public static BackendManager Instance
    {
        get
        {
            if (!instance)
            {
                GameObject obj;
                obj = GameObject.Find(typeof(BackendManager).Name);
                if (!obj)
                {
                    obj = new GameObject(typeof(BackendManager).Name);
                    instance = obj.AddComponent<BackendManager>();
                }
                else
                {
                    instance = obj.GetComponent<BackendManager>();
                }
            }
            return instance;
        }
    }


    private void Awake()
    {
        DontDestroyOnLoad(this);

        //BackendReturnObject는 통신의 결과로 넘어오는 값을 저장하는 클래스
        //뒤끝 SKD를 이용하여 서버로 요청한 모든 기능은 BackendReturnObject클래스 형태로 리턴

        //SDK를 초기화 후 BackendReturnObject클래스 리턴
        BackendReturnObject bro = Backend.Initialize(true);

        //초기화가 성공했을 경우?
        if (bro.IsSuccess())
        {
            //성공일 경우 statusCode 204 Success
            Debug.LogFormat("초기화 성공: {0}", bro); 
        }
        else 
        {
            // 실패일 경우 statusCode 400대 에러 발생 
            Debug.LogFormat("초기화 실패: {0}", bro);
        }
    }


    private void Update()
    {
        //SDK가 초기화되어있을때
        if(Backend.IsInitialized)
        {
            //뒤끝 비동기 함수 사용 시, 메인쓰레드에서 콜백을 처리해주는 Dispatch을 실행
            Backend.AsyncPoll();
        }
    }


    /// <summary> 차트 ID와 반복 횟수, 연결이 됬을 경우 실행할 함수를 받아 서버 GameData란에 정보를 추가하는 함수 </summary>
    public void GameDataInsert(string selectedProbabilityFileId, int maxRepeatCount, Param param, Action<BackendReturnObject> onCompleted = null)
    {
        if (!Backend.IsLogin)
        {
            Debug.LogError("뒤끝에 로그인 되어있지 않습니다.");
            return;
        }

        if (maxRepeatCount <= 0)
        {
            Debug.LogErrorFormat("{0} 게임 정보를 추가하지 못했습니다.", selectedProbabilityFileId);
            return;
        }

        Backend.GameData.Insert(selectedProbabilityFileId, param, callback =>
        {
            switch (ErrorCheck(callback))
            {
                case BackendState.Failure:
                    Debug.LogError("연결 실패");
                    break;

                case BackendState.Maintainance:
                    Debug.LogError("서버 점검 중");
                    break;

                case BackendState.Retry:
                    Debug.LogWarning("연결 재시도");
                    GameDataInsert(selectedProbabilityFileId, maxRepeatCount - 1, param, onCompleted);
                    break;

                case BackendState.Success:
                    Debug.Log("정보 추가 성공");
                    onCompleted?.Invoke(callback);
                    break;
            }
        });
    }


    /// <summary> 차트 ID와 반복 횟수, 연결이 됬을 경우 실행할 함수를 받아 서버 GameData란에 정보를 추가하는 함수 </summary>
    public void GameDataUpdate(string selectedProbabilityFileId, string inDate, int maxRepeatCount, Param param, Action<BackendReturnObject> onCompleted = null)
    {
        if (!Backend.IsLogin)
        {
            Debug.LogError("뒤끝에 로그인 되어있지 않습니다.");
            return;
        }

        if (maxRepeatCount <= 0)
        {
            Debug.LogErrorFormat("{0} 게임 정보를 수정하지 못했습니다.", selectedProbabilityFileId);
            return;
        }

        Backend.GameData.UpdateV2(selectedProbabilityFileId, inDate, Backend.UserInDate, param, callback =>
        {
            switch (ErrorCheck(callback))
            {
                case BackendState.Failure:
                    Debug.LogError("연결 실패");
                    break;

                case BackendState.Maintainance:
                    Debug.LogError("서버 점검 중");
                    break;

                case BackendState.Retry:
                    Debug.LogWarning("연결 재시도");
                    GameDataUpdate(selectedProbabilityFileId, inDate, maxRepeatCount - 1, param, onCompleted);
                    break;

                case BackendState.Success:
                    Debug.Log("정보 수정 성공");
                    onCompleted?.Invoke(callback);
                    break;
            }

        });
    }


    /// <summary> 차트 ID와 반복 횟수, 연결이 됬을 경우 실행할 함수를 받아 뒤끝에서 ChartData를 받아오는 함수 </summary>
    public void GetChartData(string selectedProbabilityFileId, int maxRepeatCount = 10, Action<BackendReturnObject> onCompleted = null)
    {
        //로그인이 되어 있지 않을 경우
        if (!Backend.IsLogin)
        {
            Debug.LogError("서버에 로그인 되어있지 않습니다.");
            return;
        }

        //반복횟수를 초과했을 경우
        if (maxRepeatCount <= 0)
        {
            Debug.LogError("연결 실패");
            return;
        }

        //서버에서 Chart데이터를 불러온다.
        BackendReturnObject bro = Backend.Chart.GetOneChartAndSave(selectedProbabilityFileId);

        switch (ErrorCheck(bro))
        {
            case BackendState.Failure:
                Debug.LogError("연결 실패");
                break;

            case BackendState.Maintainance:
                Debug.LogError("서버 점검 중");
                break;

            case BackendState.Retry:
                Debug.LogWarning("연결 재시도");
                GetChartData(selectedProbabilityFileId, maxRepeatCount - 1, onCompleted);
                break;

            case BackendState.Success:
                Debug.Log("차트 정보 받기 성공");
                onCompleted?.Invoke(bro);
                break;
        }
    }


    /// <summary> 서버와 연결 상태를 체크하고 BackendState값을 반환하는 함수 </summary>
    public BackendState ErrorCheck(BackendReturnObject bro)
    {
        if (bro.IsSuccess())
        {
            Debug.Log("요청 성공!");
            return BackendState.Success;
        }
        else
        {
            if (bro.IsClientRequestFailError()) // 클라이언트의 일시적인 네트워크 끊김 시
            {
                Debug.LogError("일시적인 네트워크 끊김");
                return BackendState.Retry;
            }
            else if (bro.IsServerError()) // 서버의 이상 발생 시
            {
                Debug.LogError("서버 이상 발생");
                return BackendState.Retry;
            }
            else if (bro.IsMaintenanceError()) // 서버 상태가 '점검'일 시
            {
                //점검 팝업창 + 로그인 화면으로 보내기
                Debug.Log("게임 점검중");
                return BackendState.Maintainance;
            }
            else if (bro.IsTooManyRequestError()) // 단기간에 많은 요청을 보낼 경우 발생하는 403 Forbbiden 발생 시
            {
                //단기간에 많은 요청을 보내면 발생합니다. 5분동안 뒤끝의 함수 요청을 중지해야합니다.  
                Debug.LogError("단기간에 많은 요청을 보냈습니다. 5분간 사용 불가");
                return BackendState.Failure;
            }
            else if (bro.IsBadAccessTokenError())
            {
                bool isRefreshSuccess = RefreshTheBackendToken(3); // 최대 3번 리프레시 실행

                if (isRefreshSuccess)
                {
                    Debug.LogError("토큰 발급 성공");
                    return BackendState.Retry;
                }
                else
                {
                    Debug.LogError("토큰을 발급 받지 못했습니다.");
                    return BackendState.Failure;
                }
            }

            return BackendState.Retry;
        }
    }


    /// <summary> 뒤끝 토큰 재발급 함수 </summary>
    /// maxRepeatCount : 서버 연결 실패시 재 시도할 횟수
    public bool RefreshTheBackendToken(int maxRepeatCount)
    {
        //만약 반복 횟수가 끝났으면?
        if (maxRepeatCount <= 0)
        {
            Debug.Log("토큰 발급 실패");
            return false;
        }

        BackendReturnObject callback = Backend.BMember.RefreshTheBackendToken();

        if (callback.IsSuccess())
        {
            Debug.Log("토큰 발급 성공");
            return true;
        }
        else
        {
            if (callback.IsClientRequestFailError()) // 클라이언트의 일시적인 네트워크 끊김 시
            {
                return RefreshTheBackendToken(maxRepeatCount - 1);
            }
            else if (callback.IsServerError()) // 서버의 이상 발생 시
            {
                return RefreshTheBackendToken(maxRepeatCount - 1);
            }
            else if (callback.IsMaintenanceError()) // 서버 상태가 '점검'일 시
            {
                //점검 팝업창 + 로그인 화면으로 보내기
                return false;
            }
            else if (callback.IsTooManyRequestError()) // 단기간에 많은 요청을 보낼 경우 발생하는 403 Forbbiden 발생 시
            {
                //너무 많은 요청을 보내는 중
                return false;
            }
            else
            {
                //재시도를 해도 액세스토큰 재발급이 불가능한 경우
                //커스텀 로그인 혹은 페데레이션 로그인을 통해 수동 로그인을 진행해야합니다.  
                //중복 로그인일 경우 401 bad refreshToken 에러와 함께 발생할 수 있습니다.  
                Debug.Log("게임 접속에 문제가 발생했습니다. 로그인 화면으로 돌아갑니다\n" + callback.ToString());
                return false;
            }
        }
    }
}

 

 

ChartDatabase.cs

using BackEnd;
using LitJson;
using UnityEngine;

/// <summary>차트데이터를 저장하는 클래스</summary>
public class ChartDatabase
{
    int _int;
    float _float;
    string _string;
    bool _bool;


    public void LoadData()
    {
        BackendManager.Instance.GetChartData("110270", 10, LoadChartByServer);
    }


    /// <summary>서버에서 전달받은 데이터를 클래스에 맞게 변환해주는 함수</summary>
    private void LoadChartByServer(BackendReturnObject callback)
    {
        JsonData json = callback.FlattenRows();

        for (int i = 0; i < json.Count; i++)
        {
            //json[i]["엑셀 첫번째 줄 이름"]
            _int = int.Parse(json[i]["Int"].ToString());
            _float = float.Parse(json[i]["Float"].ToString());
            _string = json[i]["String"].ToString();
            _bool = bool.Parse(json[i]["Bool"].ToString());

            Debug.LogFormat("Int: {0}, Float: {1}, String: {2}, Bool: {3}", _int, _float, _string, _bool);

        }
        Debug.Log("정보 받아오기 성공!");
    }
}

'뒤끝' 카테고리의 다른 글

[Unity] 뒤끝 3 (데이터 저장)  (0) 2024.01.18
[Unity] 뒤끝 2 (로그인)  (0) 2024.01.16
[Unity] 뒤끝 1 (연동)  (0) 2024.01.14