원래 계획은 Playfab을 사용하여 게임 데이터를 Playfab TitleData에 넣으려고 했습니다.
그런데 Playfab에서 넣을수 있는 노드의 양이 적고 전체 크기 역시 제한적이기 때문에 결론적으로는 넣을수 없었습니다.
그래서 방법을 찾아보다 Azure에 넣어야 겠다고 생각했고 여기서 보안적인 이슈 때문에 요청이 있을때만 잠깐 경로를 열었다가 이외에는 닫는 방법을 생각하여 Azure Blob Storage 의 SAS를 사용하기로 하였습니다.
일단 Aure Portal 에 로그인합니다.
구독은 비용관련된 내용이기 때문에 구독을 등록 하셔야 대부분의 기능을 이용 하실 수 있습니다.
우선 리소스 그룹을 만들어 줍니다.
서비스할 영역은 한국이기 때문에 Korea Central(서울)로 만들어 줍니다.
리소스 그룹 이름은 자신이 원하는 적당한 것으로 해줍니다.
이제 리소스 내부에 스토리지 계정을 만들어 줍니다.
리소스 그룹은 프로젝트의 리소스를 관리하는 단위이고
그 내부에 스토리지 계정은 스토리지 관리를 하는 단위입니다.
이제 만들어진 스토리지 계정에 들어가서 Container를 만들어야 합니다.
Blob Service가 있는데 여기서 컨테이너를 만들 수 있습니다.
상단에 컨테이너 추가가 있습니다.
컨테이너 이름이 중요하기 때문에 이름을 잘 정해두고 액세스 수준은 프라이빗입니다.
여기서 실제로 파일을 업로드 할 수 있습니다.
올린 파일이 실제적인 Blob이 됩니다.
계층구조는 다음과 같습니다.
리소스 그룹 > 스토리지 계정 > 컨테이너 > 블롭(파일)
여기서 액세스 수준을 private으로 둔 이유는 public 이라면 주소만 있으면 누구나 다 읽을 수 있기 때문에 보안 수준이 많이 떨어지게 됩니다.
따라서 우리가 필요할 때만 엑세스 할 수 있도록 private으로 둡니다.
이제 SAS(설명) 를 만들어 줍니다.
SAS는 공유용으로 제한된 엑세스 권한을 원하는 시간만큼 부여하는 기능 입니다.
제가 원하는 것 특정 폴더(컨테이너)내부에 있는 모든 파일(Blob)을 공유 하는 것 입니다.
그래서 컨테이너 SAS가 필요 합니다.
보안 수준때문에 클라에서 SAS를 만들수 없고 서버에서 만들어야 하기 때문에 Azure Function으로 만들어 Playfab에서 호출해 줄 것 입니다.
string storageAccountName = Environment.GetEnvironmentVariable("AZURE_STORAGE_ACCOUNT_NAME");
string storageAccountKey = Environment.GetEnvironmentVariable("AZURE_STORAGE_ACCOUNT_KEY");
string containerName = args["ContainerName"];
try
{
string blobServiceEndpoint = $"https://{storageAccountName}.blob.core.windows.net";
StorageSharedKeyCredential credential = new StorageSharedKeyCredential(storageAccountName, storageAccountKey);
BlobServiceClient blobServiceClient = new BlobServiceClient(new Uri(blobServiceEndpoint), credential);
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);
if (!await containerClient.ExistsAsync())
{
return new NotFoundObjectResult("Container Not Found");
}
//SAS 생성
BlobSasBuilder sasBuilder = new BlobSasBuilder
{
BlobContainerName = containerName,
Resource = "c",
ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(30)
};
sasBuilder.SetPermissions(BlobContainerSasPermissions.List | BlobContainerSasPermissions.Read);
string sasToken = sasBuilder.ToSasQueryParameters(credential).ToString();
string sasUrl = $"{containerClient.Uri}?{sasToken}";
return new OkObjectResult(new { sasUrl = sasUrl });
}
catch (Exception ex)
{
return new ObjectResult(new { statusCode = 500, errorMessage=ex.Message });
}
전체적인 흐름은 파라미터로 받은 컨테이너 이름을 기준으로 BlobContainerClient를 만들고 이것이 실제로 있다면 컨테이너에 대한 SAS를 만들어 반환시킨다 입니다.
Resource "c"의 의미는 컨테이너라는 명시 입니다.
ExpiresOn은 SAS의 유효 시간입니다.
여기서는 30분간 유효 하도록 처리 하였습니다.
이제 결과값을 sasUrl 이란 이름으로 리턴하였습니다.
그리고 권한을 List와 Read로 줍니다.
List는 컨테이너이기 때문에 내부에 파일이 뭐가 있는지 리스팅 할 수 있어야 하기 때문에 리스트 권한을 받아야 합니다.
마지막으로 환경변수로 추가가 필요한것이 AZURE_STORAGE_ACCOUNT_NAME, AZURE_STORAGE_ACCOUT_KEY 입니다.
NAME은 스토리지 계정 이름입니다. 스토리지 계정을 만들때 할당 하였던 이름을 적어주면 됩니다.
ACCOUNT_KEY 는 Azure Portal에서 스토리지 계정에 들어가면 좌측메뉴에 액세스키가 있습니다.
Key1 이나 Key2나 아무거나 사용하면 됩니다.
이제 이것을 만들어진 Azure Function 환경변수로 등록합니다.
환경변수까지 추가해 준다면 이제 Azure Function은 정상동작하게 됩니다.
이제 클라이언트 사이드 입니다.
UnityWebRequest listRequest = UnityWebRequest.Get($"{url}&restype=container&comp=list");
UnityWebRequestAsyncOperation operation = listRequest.SendWebRequest();
while (!operation.isDone)
await Task.Yield();
if (listRequest.result == UnityWebRequest.Result.Success)
{
XmlDocument xmlDoc = new XmlDocument();
string xmlString = listRequest.downloadHandler.text;
xmlString = xmlString.Substring(1);
xmlDoc.LoadXml(xmlString);
XmlNodeList blobNodes = xmlDoc.GetElementsByTagName("Blob");
foreach (XmlNode blobNode in blobNodes)
{
string blobName = blobNode["Name"].InnerText;
string filePath = $"{Application.persistentDataPath}\\sheet\\{dataVersion}\\{blobName}";
if (File.Exists(filePath))
{
string md5Value = blobNode.SelectSingleNode("Properties").SelectSingleNode("Content-MD5").InnerText;
if (CheckMD5(filePath, md5Value))
{
continue;
}
}
string blobUrl = $"{url.Split('?')[0]}/{blobName}{url.Substring(url.IndexOf('?'))}";
await DownloadBlob(blobUrl, blobName);
}
}
else
{
EMLog.Log("ERROR ERROR ERROR ERROR ");
}
컨테이너에 대한 SAS URL을 받아오는 것이기 때문에 리턴받는 URL이 1개 입니다.
이제 뒤에 &restype=container&comp=list 를 붙여서 호출해 주게 되면 xml을 리턴받게 되는데 이것이 blob들의 리스팅입니다.
xml은 다음과 같은 형태로 오게 됩니다.
ServiceEndPoint에서는 내가 사용한 스토리지 주소이고 ContainerName에는 컨테이너 이름이 적혀져 있습니다.
Blobs 노드 하위로 Blob노드가 반복되면서 파일들의 내용이 적혀 있습니다.
이제 개별적인 것들을 다운로드 하려면 다음과 같습니다.
private async Task DownloadBlob(string url, string fileName)
{
UnityWebRequest blobRequest = UnityWebRequest.Get(url);
UnityWebRequestAsyncOperation operation = blobRequest.SendWebRequest();
while (!operation.isDone)
await Task.Yield();
if (blobRequest.result == UnityWebRequest.Result.Success)
{
string filePath = $"{Application.persistentDataPath}\\sheet\\{dataVersion}\\{fileName}";
File.WriteAllText(filePath, blobRequest.downloadHandler.text);
}
}
해당 파일을 읽어서 persistentDataPath에 저장하는 부분 까지 입니다.
이로서 Playfab에서 부족한 데이터 저장 부분을 Azure Blob Storage의 도움을 받아서 처리하는 것을 확인 하였습니다.