[Unity & AWS] Unity에서 AWS에 파일 업로드/다운로드

박민주·2022년 2월 17일
2

Unity

목록 보기
20/40
post-thumbnail

유니티에서 서버에 파일을 업로드하고, 다운로드 받는 과정을 정리했다
일주일 넘게 R&D 한 부분이지만, 실제 적용하기 위해서는 수정해야 할 부분이 많다

업로드 시 유니티 .asset 파일을 바로 보내지 않고, Json으로 변환하여 서버에 업로드한다
다운로드 시 유니티에서는 Json 문자열을 다운로드해서 다시 .asset으로 변환하는 과정을 거쳤다

처음에는 Unity에서 S3로 바로 업로드 해보려고 했는데,
S3는 클라우드 역할을 한다는 점과 서버를 거치지 않을 경우 보안 관련 문제, 정보 동기화 문제 등이 있어
Unity에서 파일에 대한 정보를 EC2 웹서버에 전송하고, 웹서버에서 해당 파일을 S3로 업로드 하도록 했다

전체 과정을 정리하자면 다음과 같다.

1. Unity에서는 EC2 웹서버로 업로드

2. EC2 웹서버에서 S3에 업로드

3. S3에서 생성된 파일의 URL 정보를 가져옴 (수동)

4. Unity에서 해당 URL을 통해 파일을 다운로드

아직 한계점이 존재해서 이후 시도해보아야 할 부분은 다음과 같다.
1. 파일을 전송할 때 원하는 정보 끼워넣기
이 예제에서는 .asset 파일 데이터만 업로드하기 때문에
해당 파일에 대한 부가정보(파일 이름, 생성일 등)을 같이 전송하지는 못하고 있다.
-> 해당 .asset 에 대한 정보를 추가해주고 싶다.
2. S3에 업로드된 파일의 URL을 수동으로 가져오는 과정 생략하기
지금은 S3에 업로드된 파일의 URL을 수동으로 입력해주어야 다운로드 할 수 있다.
-> S3에 업로드된 파일에 대한 URL 주소를 직접 입력하지 않아도 다운받을 수 있게 하고 싶다.

진행했던 STEP별로 전체 과정을 정리해보았다.
상세한 과정이 생략된 경우 참고한 링크를 함께 남겼다.

STEP 1. 서버 생성

1. AWS EC2 인스턴스 생성

2. IAM 사용자 만들기

STEP 2. 서버 접속

1. Ubuntu에서 EC2 SSH 클라이언트 접속

2. EC2에 웹서버 구축

  • 게임 유저에 대한 정보를 관리하고 유니티에서 파일 업로드 시 EC2를 통해 업로드하기 위함

STEP 3. 웹서버 구축

1. 웹서버 구축에 필요한 것들 설치

  • Node js 설치
  • Node Package Manager: NPM 설치
  • Express 설치
  • Express generator 설치
  • Nodemon 설치
  • node project 생성
  • 참고: https://blog-han.tistory.com/58

2. 주의할 점

  • 반드시 root 계정 로그인
  • 서버 접속 후 설치해야 함 (Ubuntu에서 상단에 ip가 local host가 아닌 서버 ip인지 확인)

STEP 4. AWS S3 버킷 생성

1. S3 버킷 생성하기

2. 버킷 정책 생성/편집

  • IAM 사용자에 'S3FullAccess' 권한 주어야 함
  • 퍼블릭 액세스 차단 비활성화해야 편집 가능

STEP 5. Unity에서 EC2 웹서버를 통해 S3 로 업로드

전체 과정을 요약하면 다음과 같다

  1. 서버에 node 프로젝트 생성 및 upload 라우터 추가
  2. 유니티에서 해당 upload 라우터 경로로 UnityWebRequest 사용해서 POST 요청
  3. 웹서버에서 POST 요청을 받고, body의 내용을 S3로 업로드

EC2와 S3 연동하는 부분에서의 핵심은 다음과 같다

  1. aws-sdk 사용 (설치해주어야 함)
  2. Unity에서 업로드한 데이터를 JSON.stringify() 해서 S3에 txt로 저장

STEP 6. S3에서 Unity로 다운로드

  1. S3 콘솔에서 업로드 된 파일을 확인
  2. 해당 파일의 URL 복사
  3. Unity에서 다운로드할 URL 입력
    - 어떤 형태든 public IEnumerator DownLoadGet(string URL) 이 함수의 매개변수로 등록되도록

정상 동작 확인

  • 위 내용이 정상적으로 동작하는지 확인하려면 다음과 같은 과정을 거치면 된다.
    나의 경우 서버 IP주소를 고정시켜놓지 않아서 매번 접속 시마다 업로드 주소가 달라진다.

1. Ubuntu에서 AWS EC2 접속

2. 웹서버 on
- node 프로젝트로 들어가서 node app.js 입력

- 서버 상태 확인

3. 유니티에서 업로드 시도
- index.js에서 res.send() 해준 내용을 받아 출력하게 해두었음

- 업로드 성공하면 서버 로그에 다음과 같이 출력됨

4. S3에서 업로드 된 파일 확인

5. S3에서 해당 파일의 URL을 복사해서 유니티에서 다운받을 파일의 URL로 지정
- 지금은 S3에 존재하는 파일들의 URL을 수동으로 입력해주어야 함
- public IEnumerator DownLoadGet(string URL) 의 매개변수로 지정해주면 됨
.
6. 지정해준 경로에 생성된 파일 확인

  • 'SongItemJson.txt'이 서버로부터 다운로드 받은 파일이며, 이를 통해 'SongItemFromS3.asset' 파일이 생성되도록 처리했음

전체 코드

1. AWS EC2 웹서버 - node-project/routes/index.js


var express = require('express');
var router = express.Router();
const path = require("path");
// AWS, S3 관련 변수
let AWS = require("aws-sdk");
let s3 = new AWS.S3({
        accessKeyId: '{Access Key}',
        secretAccessKey: '{Secret Access Key}'
});

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

// S3 bucket name 
const bucket_name = "s3-bucket-name"; 
let songItem = "";

router.post('/upload', function(req, res){

        songItem = req.body;
        // req.body를 바로 업로드하면 에러남. string으로 변환해야 함.
        const text = JSON.stringify(songItem);
        const params = {
            Bucket: bucket_name,
            Key: 'songItem_' + Date.now().toString(),
            Body: text
        }

        s3.upload(params, (err, data) => {
            if(err) {
                console.log('s3.upload -> error:' + err);
                res.send(err);
            }else {
                console.log('s3.upload -> uploaded');
                res.send('Uploaded! : ' + req.body);
            }

        });
});

router.get('/upload', function(req, res){
		// 접속 시 가장 최근에 받은 songItem 데이터를 페이지에 출력
        res.send(songItem);
        // 서버 로그 출력
        console.log('---------get upload---------');

});

module.exports = router;

2. Unity 코드

  • 업로드/다운로드에 필수적이지 않은 코드는 Custom 표시해둠
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using RhythmGameStarter;
using UnityEditor;

public class AWSTest : MonoBehaviour
{
    public SongItem uploadSongItem;
    public Button btn_upload;
    public Button btn_download;

    private string jsonFile;

    // 서버로부터 받은 파일을 저장할 경로
    string filePath_downloadText = "Assets/Resources/SongItemJson.txt";
    
    // ------------------------- Custom --------------------------------
    // 서버에서 받은 데이터를 통해 만들어진 songItem 파일이 저장될 위치
    string filePath_songItem = "Assets/Resources/SongItemFromS3.asset"; 
    private SongItem downloadSongItem;
    // -----------------------------------------------------------------
    
    void Start()
    {
    	// ------------------------- Custom --------------------------------
        // 에셋 파일을 Json 형태로 업로드하기 위해 Json으로 변환
        jsonFile = JsonUtility.ToJson(uploadSongItem);
		// -----------------------------------------------------------------
        
        btn_upload.onClick.AddListener(() =>
        {
            StartCoroutine(Upload("http://{EC2 인스턴스 퍼블릭 IPv4 주소}:9000/upload", jsonFile));
        });

        btn_download.onClick.AddListener(() =>
        { 
            StartCoroutine(DownLoadGet("{S3에 업로드된 파일의 URL}"));
        });
    }

	// AWS EC2에 업로드를 위한 함수
    IEnumerator Upload(string URL, string jsonfile)
    {
        // 웹서버로 Post 요청을 보냄
        using (UnityWebRequest request = UnityWebRequest.Post(URL, jsonfile))
        {
            byte[] jsonToSend = new System.Text.UTF8Encoding().GetBytes(jsonfile);
            request.uploadHandler = new UploadHandlerRaw(jsonToSend); // 업로드 핸들러
            request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); // 다운로드 핸들러
            // 헤더를 Json으로 설정
            request.SetRequestHeader("Content-Type", "application/json");

            yield return request.SendWebRequest();
            if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
            {
                Debug.Log(request.error);
            }
            else
            {
            	// 웹서버로부터 받은 응답 내용 출력
                Debug.Log(request.downloadHandler.text);
            }
        }
    }

	// AWS S3에서 다운로드를 위한 함수 
    public IEnumerator DownLoadGet(string URL)
    {
        UnityWebRequest request = UnityWebRequest.Get(URL);

        yield return request.SendWebRequest();
        // 에러 발생 시
        if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
        {
            Debug.Log(request.error);
        }
        else
        {
            // 서버에서 받아온 파일 생성
            File.WriteAllBytes(filePath_downloadText, request.downloadHandler.data);

			// ------------------------- Custom --------------------------------
            // 불러온 데이터를 저장할 songItem 에셋 파일 생성 
            AssetDatabase.CreateAsset(new SongItem(), filePath_songItem);

            // 위에서 미리 만들어둔 에셋 파일을 songItem으로 load함 
            downloadSongItem = (SongItem)AssetDatabase.LoadAssetAtPath(filePath_songItem, typeof(SongItem));
            if (downloadSongItem)
            {
                // Json 파일을 LiteSongItem으로 만듦
                var importedLiteSongItem = FindJsonFile();
                if (importedLiteSongItem == null)
                {
                    Debug.Log("importedLiteSongItem is null");
                }
                downloadSongItem.LoadNotesFrom(importedLiteSongItem);
            }
            else
            {
                // 파일이 경로에 없으면 로그 출력
                Debug.Log($"songItem is not in {filePath_songItem}");
            }
		// -----------------------------------------------------------------
        }
     }

	// ------------------------- Custom --------------------------------
    // SongItem 에셋 생성을 위한 함수
    // - Json을 LiteSongItem으로 변경해줌
    public LiteSongItem FindJsonFile()
    {
        // ... (생략)
    }
    // -----------------------------------------------------------------
}

Tip. Postman 사용하기

  • upload 라우터를 추가한 후에 유니티를 통해 업로드를 시도해봤는데 오류가 난 경우,,
    해당 라우터가 POST 요청을 정상적으로 받지 못하는건지, 유니티 코드가 잘못된건지 알 수가 없을 때 확인하기에 유용하다

번외편. MySQL 셋팅하기

유저 정보를 EC2에서 관리하기 위해 MySQL에 관한 설정도 미리 해주었다.
workbench를 연결해두면 테이블 만들기에 수월하다
없으면 ubuntu 에서 이러고 있어야 함..

1. 서버에 MySQL 설치

- AWS EC2 인바운드 규칙 편집해서 3306 열어줘야 함
- 서버에서도 포트 열어줘야 함
- Mysql 사용자 권한 설정해주어야 함
- 참고 : https://technote.kr/32

2. MySQL workbench 연결

3. workbench 연결 실패뜨면 확인해볼 것

- 해당 username이 MySQL 에서 권한을 가지고 있는지
- Hostname이 AWS 서버의 퍼블릭 IP주소인지
- AWS 서버에서 인바운드 규칙에서 MySQL 3306 port 가 열려있는지

profile
Game Programmer

0개의 댓글