개발노트/Taxi Game 3D

Devlog) Taxi Game 3D) 25) 서버 게시, 게임 빌드

username263 2024. 2. 20. 17:08
728x90
SMALL

몽고DB 아틀라스 설정

몽고DB 아틀라스에 접속하여 무료 클러스터(저장소?)를 추가하였습니다.
동시에 보안 설정에서 제 PC의 IP주소를 등록하였습니다.

 

MongoDB Compass를 이용하여 새로 추가된 클러스터에 잘 접속 되는지 확인해 보았고, 잘 접속 되었습니다.

 

다음으로 제가 만든 서버도 DB주소를 바꿔도 잘 되는지 확인해 보았습니다.
서버 프로젝트에서 appsettings.AzureDev.json 파일 추가한 후, DB 주소를 새로 만든 클러스터의 주소로 수정하였습니다.

 

서버를 실행했을 때 appsettings.AzureDev.json 파일을 이용하도록 만들기 위해 Properties/launchSettings.json 파일의 ASPNETCORE_ENVIRONMENT 수정하였습니다.

"environmentVariables": {
  "ASPNETCORE_ENVIRONMENT": "AzureDev"
}

 

appsettings.AzureDev.json 파일에는 민감한 정보가 있기 때문에 .gitignore 파일에 등록하여 깃허브에 업로드 되지 않도록 않도록 처리하였습니다.

.vs
TaxiGame3D.Server/bin
TaxiGame3D.Server/obj
TaxiGame3D.Server/appsettings.AzureDev.json

 

서버를 실행한 후, 파이썬 스크립트를 이용하여 모든 템플릿을 DB에 등록해보았습니다.
새로 추가한 클러스터에 아무 문제 없이 DB가 추가되었습니다.

 

Azure 설정

 

https://portal.azure.com/ 에 접속하여 무료 웹 앱을 새로 추가하였습니다.

 

Azure 웹 앱에서도 MongoDB Atlas에 잘 접속할 수 있도록 아웃바운드 IP주소를 등록하였습니다.

 

Azure 웹 앱에 배포된 서버는 appsettings.AzureDev.json 파일에 입력된 설정을 사용하도록 ASPNETCORE_ENVIRONMENT 설정하였습니다.

 

Visiual Studio 에서 서버 게시를 할 수 있도록 게시 프로필 생성하였습니다.

 

민감한 정보 때문에 .gitignore 파일에 게시 프로필 경로를 입력하여 깃허브에 업로드 되지 않도록 처리하였습니다.

.vs
TaxiGame3D.Server/bin
TaxiGame3D.Server/obj
TaxiGame3D.Server/appsettings.AzureDev.json
TaxiGame3D.Server/Properties/PublishProfiles
TaxiGame3D.Server/Properties/ServiceDependencies

 

Azure에 게시된 서버에서도 Swagger를 이용하여 테스트할 수 있도록 Program.cs 수정하였습니다.

//if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

 

Azure에 서버를 게시한 후 Swagger를 이용하여 서버와 DB가 잘 동작하는지 확인하였습니다.
/Template/Versions를 실행하여 미리 등록했던 템플릿 버전확인을 해 보았습니다.

 

클라이언트 수정/테스트

 

접속할 서버를 변경할 때마다 ClientManager 프리팹에서 서버 주소를 직접 수정하지 않도록 ClientSettings 스크립트 추가하였습니다.
ClientSettingsScriptableObject를 상속받아 .asset 파일로 만들 수 있도록 구현하였습니다.

[CreateAssetMenu(menuName = "TaxiGame/ClientSettings")]
public class ClientSettings : ScriptableObject
{
    [field: SerializeField]
    public string Enviroment
    {
        get;
        private set;
    }

    [field: SerializeField]
    public string ServerUri
    {
        get;
        private set;
    }
}

 

ClientSettings 타입의 AzureDev.asset, LocalDev.asset 파일을 추가하여 기존에 사용하던 로컬주소와 Azure 웹앱 주소를 등록하였습니다.

 

ClientManager 프리팹은 ClientSettings 파일을 이용하여 접속할 서버를 선택할 수 있도록 수정하였습니다.

public class ClientManager : MonoBehaviour
{
    [SerializeField]
    ClientSettings settings;

    public static ClientManager Instance
    {
        get;
        private set;
    }

    public string Enviroment => settings.Enviroment;

    public HttpContext Http
    {
        get;
        private set;
    }

    public AuthService AuthService
    {
        get;
        private set;
    }

    public UserService UserService
    {
        get;
        private set;
    }

    public TemplateService TemplateService
    {
        get;
        private set;
    }

    void Awake()
    {
        Instance = this;
        DontDestroyOnLoad(gameObject);

        Http = new(settings.ServerUri);
        AuthService = gameObject.AddComponent<AuthService>();
        UserService = gameObject.AddComponent<UserService>();
        TemplateService = gameObject.AddComponent<TemplateService>();
    }

    public static void CreateInstance()
    {
        if (Instance != null)
            return;
        Instantiate(Resources.Load(nameof(ClientManager)));
    }

    [ContextMenu("Reset Template Versions")]
    void ResetTemplateVersions() => TemplateService.ResetTemplateVersions(Enviroment);

    [ContextMenu("Reset Saved Auth")]
    void ResetSavedAuth() => AuthService.ResetSavedAuth();
}

 

Azure에 게시했던 서버에 잘 접속 되는지 테스트해봤습니다.
자동 로그인 실패 후 로그인 UI가 출력되지 않는 현상 발생하여 로그를 추가하여 원인을 찾아 보았습니다.
제 PC에서 실행한 서버와 통신하였을 때는 Exception이 발생해도 결과값을 잘 반환하였지만 Azure에 게시했던 서버와 통신하였을 때는 Exception이 발생하면 결과값을 반환하지 못 했습니다.
로그를 계속 입력하여 확인했을 때 UniTask 에서 다르게 처리하는 것 같다고 의심이 되었지만 더 자세히 확인하지 않고, HttpContext 스크립트에서 서버에 요청하는 구간은 try~catch로 감싸줘서 어떤 문제가 발생하여도 결과값은 반환하도록 수정하였습니다.

public async UniTask<HttpStatusCode> Get(string subUri)
{
    using (var req = UnityWebRequest.Get($"{BaseUri}{subUri}"))
    {
        try
        {
            foreach (var h in Headers)
                req.SetRequestHeader(h.Key, h.Value);
            await req.SendWebRequest();
        }
        catch (Exception e)
        {
            Debug.LogException(e);
        }
        return (HttpStatusCode)req.responseCode;
    }
}

public async UniTask<(HttpStatusCode, T)> Get<T>(string subUri)
{
    using (var req = UnityWebRequest.Get($"{BaseUri}{subUri}"))
    {
        try
        {
            foreach (var h in Headers)
                req.SetRequestHeader(h.Key, h.Value);
            await req.SendWebRequest();
        }
        catch (Exception e)
        {
            Debug.LogException(e);
        }
        return (
            (HttpStatusCode)req.responseCode,
            FromJsonSafety<T>(req.downloadHandler.text)
        );
    }
}

public async UniTask<HttpStatusCode> Post(string subUri, object data)
{
    using (var req = UnityWebRequest.Post($"{BaseUri}{subUri}", ToJsonSafety(data), "application/json"))
    {
        try
        {
            foreach (var h in Headers)
                req.SetRequestHeader(h.Key, h.Value);
            await req.SendWebRequest();
        }
        catch (Exception e)
        {
            Debug.LogException(e);
        }
        return (HttpStatusCode)req.responseCode;
    }
}

public async UniTask<(HttpStatusCode, T)> Post<T>(string subUri, object data)
{
    using (var req = UnityWebRequest.Post($"{BaseUri}{subUri}", ToJsonSafety(data), "application/json"))
    {
        try
        {
            foreach (var h in Headers)
                req.SetRequestHeader(h.Key, h.Value);
            await req.SendWebRequest();
        }
        catch (Exception e)
        {
            Debug.LogException(e);
        }
        return (
            (HttpStatusCode)req.responseCode,
            FromJsonSafety<T>(req.downloadHandler.text)
        );
    }
}

public async UniTask<HttpStatusCode> Put(string subUri, object data)
{
    using (var req = UnityWebRequest.Put($"{BaseUri}{subUri}", ToJsonSafety(data)))
    {
        try
        {
            foreach (var h in Headers)
                req.SetRequestHeader(h.Key, h.Value);
            req.SetRequestHeader("Content-Type", "application/json");
            await req.SendWebRequest();
        }
        catch (Exception e)
        {
            Debug.LogException(e);
        }
        return (HttpStatusCode)req.responseCode;
    }
}

public async UniTask<(HttpStatusCode, T)> Put<T>(string subUri, object data)
{
    using (var req = UnityWebRequest.Put($"{BaseUri}{subUri}", ToJsonSafety(data)))
    {
        try
        {
            foreach (var h in Headers)
                req.SetRequestHeader(h.Key, h.Value);
            req.SetRequestHeader("Content-Type", "application/json");
            await req.SendWebRequest();
        }
        catch (Exception e)
        {
            Debug.LogException(e);
        }
        return (
            (HttpStatusCode)req.responseCode,
            FromJsonSafety<T>(req.downloadHandler.text)
        );
    }
}

public async UniTask<HttpStatusCode> Delete(string subUri)
{
    using (var req = UnityWebRequest.Delete($"{BaseUri}{subUri}"))
    {
        try
        {
            foreach (var h in Headers)
                req.SetRequestHeader(h.Key, h.Value);
            await req.SendWebRequest();
        }
        catch (Exception e)
        {
            Debug.LogException(e);
        }
        return (HttpStatusCode)req.responseCode;
    }
}

public async UniTask<(HttpStatusCode, T)> Delete<T>(string subUri)
{
    using (var req = UnityWebRequest.Delete($"{BaseUri}{subUri}"))
    {
        try
        {
            foreach (var h in Headers)
                req.SetRequestHeader(h.Key, h.Value);
            await req.SendWebRequest();
        }
        catch (Exception e)
        {
            Debug.LogException(e);
        }
        return (
            (HttpStatusCode)req.responseCode,
            FromJsonSafety<T>(req.downloadHandler.text)
        );
    }
}

 

클라이언트를 빌드하기 전에 Azure 웹 앱은 무료버전으로 만들 경우 30분 이상 통신하지 않으면 슬립모드가 되기 때문에 최소 비용을 지불하도록 요금제를 변경했던 경험이 떠올랐습니다.
그래서 40분 정도 대기했다가 게임을 실행해보았고, 처음 실행할 때 응답속도가 엄청 느리다는 것 외에는 큰 문제 없어 다른 처리를 하지 않아 안드로이드로 빌드 하였습니다.

 

빌드 후 테스트를 해보니 화면 터치를 해도 자동차가 이동하지 않았습니다.
InputSystem을 적용할 때 마우스 입력만 추가하였기 때문인 것 같습니다.
원래 모바일에서도 잘 돌아가면 프로젝트를 마무리 할 계획이었지만 입력 문제와 그 밖에 빌드 후 발생하는 다른 문제들도 찾아서 수정한 후 프로젝트를 마무리하겠습니다.

깃 허브 저장소 : taxi-game-3d-unity

728x90
LIST