2024년 8월 06일 화요일 개발일지 / 계정 관리와 저장 및 불러오기

2024. 8. 6. 21:16나의 개발자 기록/본 캠프 개발 일지

2024년 8월 06일 화요일


What I did today : Authentication and Cloud Save ( 계정 관리 시스템과 저장 및 불러오기 )

 

 

오늘은 UGS, Unity Gaming Services에 대해서 좀 더 알아보려고 이것저것 찾아보았습니다. 일단 저희가 필요한 부분이 회원가입이 가능하고, 로그인이 되어야 하는 부분인데, 이걸 관리할 수 있게 해주는 서비스가 Player Authentication 서비스라고 하고, 추가적으로 저장 및 불러오기는 Cloud Save 서비스로 해결 가능하다고 합니다. 그래서 어제 필요한 패키지들을 다운로드하고 계정 연동을 했었기 때문에, 어떻게 만드는지에 대해서 오늘 유튜브와 공식 문서를 통해서 해결해 보았습니다.

 

https://docs.unity.com/ugs/en-us/manual/authentication/manual/platform-signin-custom-id

 

Custom ID sign-in

AccountsAuthenticationTutorialsCustom ID English日本語 (日本)한국어(대한민국)中文(中国) English日本語 (日本)한국어(대한민국)中文(中国) Custom ID sign-in#Minimum SDK version: 3.1.0This article guides you through the f

docs.unity.com

https://www.youtube.com/watch?v=OHWo_xCdqHQ

 

일단 계정 관리의 경우, 플랫폼을 통한 로그인도 가능하고, 커스텀 ID를 통해서 로그인 시스템을 만들 수 있었습니다. 처음에는 공식 문서로 초반 튜토리얼 부분을 참고하고, 어떤 외국인 유튜버의 튜토리얼 영상도 참고해서 기본적인 시스템을 만들어 보았습니다.

 

 

처음에는 계정을 연동한 유니티 Cloud에서 계정 로그인 방식을 어떻게 할 건지, 제공 방식을 정해야 하는 데, 저는 영상에서 알려주던 함수인 SignInAnonymouslyAsync()를 사용해 보니까 그냥 어떤 ID 값을 받아서 로그인하는 방식이라... 제가 생각하는 거랑은 거리가 멀어 보였습니다. 제가 생각하는 로그인 방식은 아이디와 비밀번호가 있고, 회원 가입도 가능한 일반적인 로그인 시스템을 생각했기 때문에, 해당 사이트에서 Custom ID라고 직접 만들어서 사용할 수 있는 거를 위의 이미지처럼 추가해야지만 관련 코드를 쓸 수 있었습니다.

 

 

이다음에는 유니티에서 필요한 UI를 만들고, 그 UI에 대한 정보들을 받아올 LoginManager를 만들었습니다.

 

async Task OnLoginButtonClicked()
{
    string username = usernameInput.text;
    string password = passwordInput.text;

    if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
    {
        DisplayError("Username or password is empty");
        return;
    }

    try
    {
        await AuthenticationService.Instance.SignInWithUsernamePasswordAsync(username, password); // 사용자 정의 ID로 로그인
        Debug.Log("Logged in successfully");
        loginPanel.SetActive(false);
        SceneManager.LoadScene("KSLScene(Main)");
    }
    catch (RequestFailedException e)
    {
        DisplayError($"Login failed: {e.Message}");
    }
}

async Task OnSignUpButtonClicked()
{
    string username = usernameInput.text;
    string password = passwordInput.text;

    if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
    {
        DisplayError("Username or password is empty");
        return;
    }

    try
    {
        await AuthenticationService.Instance.SignUpWithUsernamePasswordAsync(username, password);
    }
    catch (RequestFailedException e)
    {
        DisplayError($"Sign up failed: {e.Message}");
    }
}

 

공식 문서와 유튜브를 통해서 기본적인 틀을 만들고, AuthenticationSevice 안에 어떤 함수가 있는지 보니까, Sign In 하고, Sign Up에 대한 아이디와 패스워드를 받는 함수가 있었습니다. 그래서 이걸 토대로 회원 가입을 하고, 로그인을 할 수 있게 됩니다.

 

https://docs.unity.com/ugs/en-us/manual/cloud-save/manual/get-started

 

Get started

Attention: The Digital Services Act (DSA) requires Unity to notify our customers’ end users if Unity takes an action that impacts those end users under the DSA. To comply with this requirement, if you use Unity Gaming Services (UGS) products that rely on

docs.unity.com

 

https://www.youtube.com/watch?v=STuIobcdKzk

 

근데 해당 정보를 보기 위해서는 Cloud Save도 같이 사용해줘야 하므로 저장 및 불러오기 시스템도 해당 유튜브에서 확인할 수 있었고, 공식 문서에도 자세하게 나와 있어서 생각한 것보다는 크게 어렵진 않았던 것 같습니다.

 

async void Start()
{
    await UnityServices.InitializeAsync();
    loginButton.onClick.AddListener(async () => await OnLoginButtonClicked());
    signUpButton.onClick.AddListener(async () => await OnSignUpButtonClicked());
}

async Task OnLoginButtonClicked()
{
    string username = usernameInput.text;
    string password = passwordInput.text;

    if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
    {
        DisplayError("Username or password is empty");
        return;
    }

    try
    {
        await AuthenticationService.Instance.SignInWithUsernamePasswordAsync(username, password); // 사용자 정의 ID로 로그인
        Debug.Log("Logged in successfully");
        loginPanel.SetActive(false);
        await SaveData("LastLogin", DateTime.Now.ToString());
        string lastLogin = await LoadData("LastLogin");
        Debug.Log("Last Login: " + lastLogin);
        SceneManager.LoadScene("KSLScene(Main)");
    }
    catch (RequestFailedException e)
    {
        DisplayError($"Login failed: {e.Message}");
    }
}

async Task OnSignUpButtonClicked()
{
    string username = usernameInput.text;
    string password = passwordInput.text;

    if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
    {
        DisplayError("Username or password is empty");
        return;
    }

    try
    {
        await AuthenticationService.Instance.SignUpWithUsernamePasswordAsync(username, password);
    }
    catch (RequestFailedException e)
    {
        DisplayError($"Sign up failed: {e.Message}");
    }
}

async Task SaveData(string key, string value)
{
    try
    {
        var data = new Dictionary<string, object> { { key, value } };
        await CloudSaveService.Instance.Data.ForceSaveAsync(data);
        Debug.Log("Data saved");
    }
    catch (Exception e)
    {
        DisplayError($"Failed to save data: {e.Message}");
    }
}

async Task<string> LoadData(string key)
{
    try
    {
        var keys = new HashSet<string> { key };
        var data = await CloudSaveService.Instance.Data.LoadAsync(keys);
        if (data.ContainsKey(key))
        {
            return data[key].ToString();
        }
        else
        {
            DisplayError("Key not found");
            return null;
        }
    }
    catch (Exception e)
    {
        DisplayError($"Failed to load data: {e.Message}");
        return null;
    }
}

 

위의 코드대로 한다면 처음에 입력한 정보를 회원가입을 통해서 이미지에 있는 Player ID 값이 생기게 되고, 저장불러오기를 통해서 Default 데이터에 해당 정보의 키와 값을 통한 데이터가 저장됩니다. 아마도 이 저장과 불러오기 기능을 게임 매니저에서 활용하여 게임에 필요한 모든 정보를 담은 함수를 만든 다음에, 게임 종료나 멈췄을 때, 저장되는 함수를 불러오고, 로그인할 때 불러오기 함수를 불러오면 해당 계정에 대한 정보들이 저장된 데이터를 불러오는 것도 되지 않을까 생각이 듭니다. 일단 이 부분은 다른 팀원분들이 맡으신 부분이 완성이 돼야 저도 진행이 가능하기 때문에 여기까지만 작성하고, 팀원분들에게 공유해 드린 다음에, 저는 TIL을 쓰면서 오늘 하루를 마무리했습니다.  


면접 질문 대비 : 인터페이스란 무엇인가요?

 

public interface IShape
{
    double Area();
}

public class Circle : IShape
{
    public double Radius { get; set; }

    public double Area()
    {
        return Math.PI * Radius * Radius;
    }
}

public class Rectangle : IShape
{
    public double Width { get; set; }
    public double Height { get; set; }

    public double Area()
    {
        return Width * Height;
    }
}

 

인터페이스는 클래스가 구현해야 하는 메서드속성을 정의하는 계약(Contract)입니다. 인터페이스 자체는 구현을 제공하지 않으며, 다중 상속을 대체하는 방법으로 사용됩니다. 인터페이스를 통해 클래스는 기능을 정의하고, 이를 다양한 방식으로 구현할 수 있습니다.