공부/Unreal Engine 4

aos, ios 네이티브 빌드&패키징 vs 언리얼 모바일 패키징

Lero God 2023. 5. 23. 11:42

참고 : https://creazier.tistory.com/15310767

 

 

[AOS]

<네이티브 패키징 방식>
1. java 코드 작성
2. java 코드 바탕으로 class 파일 생성
3. class 파일 dex 파일로 변환
4. dex 파일과 사용중인 리소스들을 `apkbuilder`로 apk 파일 생성
5. zipalign`으로 apk 파일 binary aligning
6. 앱 사이닝 
(사이닝 = 구글 플레이스토어에 제출하기 위한 과정? apk에 키값이라고 불리는 문자열을 같이 포함하는 건가?)

7. `adb` 혹은 스토어를 통해 apk 다운로드 
(Android ADB (Android Debug Bridge)는 PC와 스마트 폰 간에 통신을 할 수 있는 명령어도 도구입니다. 안드로이드 개발자에게는 apk 설치, log 출력의  등의 개발에 많은 활동에서 adb를 거의 매일 사용하고 있습니다. 또한 디버깅 목적뿐 아니라 스마트 폰 화면을 PC로 미러링 할 수 있는 App에서도 adb를 사용합니다.)

8. apk의 sign 체크
9. 애플리케이션이 실행될 때 VM을 켜고 자신의 코드 실행

- Proguard
 https://developer.android.com/studio/build/shrink-code?hl=ko 

난독화: 클래스와 멤버 이름을 줄여 DEX 파일 크기를 줄입니다. 자세히 알아보려면 코드 난독화 방법에 관한 섹션을 참조하세요.
최적화: 코드를 검사하고 다시 작성하여 앱 DEX 파일의 크기를 더 줄입니다. 예를 들어 주어진 if/else 구문의 else {} 분기가 전혀 사용되지 않음을 R8에서 감지한 경우 R8이 else {} 분기 코드를 삭제합니다. 자세히 알아보려면 코드 최적화 섹션을 참조하세요

- JVM(Java Virtual Machine)
Java는 여러 플랫폼에서 동일하게 실행 가능한데,
JVM 위에서 `바이트 코드`가 실행되기 때문이다. JVM은 Java 측에서 OS, CPU 아키텍처마다 빌드한 실행 가능한 프로그램으로 제공된다.

- JRE(Java Runtime Environment)
JVM을 포함하며, 프로그램 실행 과정에 필요한 여러 라이브러리들을 포함하고 있다.

- JDK(Java Development Kit)
JRE와 동시에 Java 언어 컴파일을 위한 컴파일러 및 여러 툴들도 포함한 패키지이다.

- JNI(Java Native Interface)
C/C++로 작성된 네이티브 코드에서 JVM을 생성할 수도, JVM에서 실행된 Java 프로그램에서 Shared Library 로딩을 할 수도 있다.
네이티브 코드에서 `JNI_CreateJavaVM` 함수를 실행하면 그 함수를 실행한 쓰레드가 JVM의 메인 쓰레드가 된다.
만들어진 JVM은 `JNIEnv*`가 되어 포인터로 자유롭게 이용 가능하다. 어떤 클래스를 찾아서 그 클래스의 인스턴스를 만들수도, 만들어진
인스턴스의 메소드 혹은 클래스의 static 메소드를 실행할 수도 있다. Java에서 할 수 있는 일의 거의 전부를 할 수 있다고 생각하면 된다. 

- NDK(Native Development Kit)
NDK를 이용해 C++ 코드를 코드를 Shared Library로 빌드한다.
Shared Library를 빌드할 때는 C++ 코드가 그 라이브러리가 사용될 CPU(거의 보통은 ARM 아키텍처)에서 실행 가능한 어셈블리로 컴파일 되어야 한다.
NDK로 빌드하면 네이티브 코드가 원하는 CPU 아키텍처의 Intstruction Set으로 컴파일 되는 것을 믿고 쓸 수 있다. 
또한, 안드로이드 측의 여러 시스템들을 좀 더 편리하게 사용할 수 있게 하기 위한 라이브러리들이 포함되어 있다.

- 언리얼에서 c++ 코드를 안드로이드에서 실행하는 방식
그렇기 때문에 게임 엔진들(언리얼 엔진, 유니티 엔진, Cocos2d-x 등)은 NDK를 이용해 코드를 Shared Library로 빌드하고
안드로이드 애플리케이션에서 해당 Shared Library를 로딩하는 방식으로 게임을 실행한다.
Shared Library의 네이티브 코드에서는 하드웨어 가속을 받아 열심히 렌더링하여 최종 결과물 비트맵을 만든다.
만들어진 비트맵을 애플리케이션의 View에 입힌다. 그러면 우리가 보는 게임 화면이 완성된다.

- and, gradle
앞서 간략한 빌드 순서를 보면 알겠지만 이것 저것 귀찮은 짓을 많이 필요로 한다.
하지만 실제로 IDE를 통해 안드로이드 개발을 할 때는 빌드 버튼만 누르면 apk 파일이 툭 나온다.
그 뒤에는 `ant`나 `gradle`과 같은 빌드 툴들의 도움이 있다.
android studio 에서는 기본으로 gradle 을 사용하고 예전에는 ant 를 사용했었다. 지금은 거의 gradle 로 다 대체됨.
ant 나 gradle 이 하는 동작은 미리 안드로이드 SDK에 명세된 대로 이런 저런 작업을 한 뒤 apk 파일이 만들어주는 작업을 한다.
이런 저런 작업에는 앞서 제시한 순서인 Java 소스 컴파일, dex 파일 생성, aapt로 apk 생성, zipalign으로 aligning, apksigner로 signing이 포함된다.

- androidmanifest.xml
AndroidManifest.xml 파일은 시스템에게 애플리케이션의 몇 가지 꼭 필요한 정보를 제공하기 위해 필요합니다.
예를 들면 애플리케이션에서 사용하는 기능들에 대한 권한, 빌드하는데 사용된 SDK 버전, 코드에 존재하는 App 컴포넌트들(Activity, Service, Broadcast Receiver, Content Provider) 등이 있다. AndroidManifest.xml에 명시된 App 컴포넌트로는 아마 `activity` element가 가장 익숙할 것이다.
애플리케이션에 어떤 Activity가 존재하는지 알려주는 element이다. 
* 언리얼에서 AndroidManifest.xml 파일의 위치와 제어하는 방식 
프로젝트 세팅에서 제어 -https://docs.unrealengine.com/4.26/ko/SharingAndReleasing/Mobile/Android/AndroidManifestControl/
upl로 제어 - https://tigerfish.tistory.com/71

<언리얼에서 안드로이드 어플리케이션 패키징 방식>
언리얼 엔진에서는 `UAT(Unreal Automation Tool)`라는 것으로 이 과정들이 숨겨져있다.
여러가지 복잡한 인자들을 많이 요구하는데, UAT를 이용하여 리소스 쿠킹, 바이너리 빌드, 기기에 배포 등을 모두 할 수 있도록 만들었기
때문이다. 언리얼 에디터에서 패키징 혹은 프리뷰 런칭을 할 때도 내부적으로는 UAT를 통해 빌드하고 기기에 배포까지 한다. 
사실 그 모든 것이 내부적으로는 적절하게 프로젝트 파일들을 세팅한 뒤 `ant build`, `adb install apk-file` 명령어 같은 것을 실행하는 것이다.

그렇기 때문에 프로젝트 디렉토리의 Intermediate 파일들을 보면 전형적인 안드로이드 프로젝트의 디렉토리 구조를 갖고 있으며,
UAT 명령어의 실행 로그를 보면 도중에 ant 명령어 실행과 그 로그들을 확인할 수 있다. 안드로이드 프로젝트의 AndoridManifest.xml 파일을
보면 메인 Activity로 `GameActivity`라는 언리얼 엔진 측의 클래스를 사용하는 것을 확인할 수 있다. 

당연히 프로젝트에 생성한 C++ 클래스들에서 JNI를 사용할 수 있기 때문에 직접 Java의 코드들을 실행할 수도 있다.
이를 이용하여 Tapjoy와 같은 네이티브 기능을 사용하는 SDK를 사용할 수 있다.
물론, 그 역도 성립한다. 프로젝트의 C++ 코드들은 빌드를 통해 Shared Library `libUE4.so`로 만들어진다.
그리고 `GameActivity`에서는 `System.loadLibrary`로 `libUE4.so`를 로드함으로써 네이티브 코드들이 실행 가능해진다. (그래서 안드로이드 기기에서 게임 앱을 제어 할 수 있는 건가?)

* 언리얼에서 ThirdParty JAVA 클래스 사용하기
먼저 사용하려는 Java 클래스들을 컴파일하여 jar 파일로 만들어야 한다.
다음으로 이 jar 파일이 함께 패키징되도록 해야 한다. 슬프게도 언리얼 엔진의 빌드 프로세스는 굉장히 복잡하게 숨겨져있다.
가장 쉬운 방법은 사용하려는 jar 파일을  엔진 디렉토리의 안드로이드 템플릿 프로젝트의 `libs` 디렉토리에 넣어버리는 것이다.
이 방법의 문제점은 해당 엔진을 사용하는 모든 프로젝트에 영향을 미친다는 것이다. 

다른 방법으로는 4.13버전까지 기준 Plugin Language 기능을 이용하는 방법이 있다.
Plugin Language에 명세로 어떤 파일들을 Intermediate 프로젝트에 복사시키거나, 추가 권한 획득이나 App 컴포넌트 추가를 위한
AndroidManifest.xml 수정, GameActivity의 몇몇 라이프 사이클 메소드에 코드 추가 등이 가능하다. 그러나 굉장히 제한적인 수준이다. 

[iOS]

 https://medium.com/jinshine-%EA%B8%B0%EC%88%A0-%EB%B8%94%EB%A1%9C%EA%B7%B8/%EC%BD%94%EB%93%9C%EC%82%AC%EC%9D%B4%EB%8B%9D-%EC%9D%B8%EC%A6%9D%EC%84%9C-%ED%94%84%EB%A1%9C%EB%B9%84%EC%A0%80%EB%8B%9D-%ED%94%84%EB%A1%9C%ED%8C%8C%EC%9D%BC%EC%9D%B4%EB%9E%80-2bd2c652d00f

https://velog.io/@enebin777/Apple-iOS-%EC%95%B1-%EB%B0%B0%ED%8F%AC-%EA%B3%BC%EC%A0%95%EC%BD%94%EB%93%9C-%EC%82%AC%EC%9D%B4%EB%8B%9D-%ED%94%84%EB%A1%9C%EB%B9%84%EC%A0%80%EB%8B%9D-%ED%94%84%EB%A1%9C%ED%95%84

https://www.blueswt.com/123

https://engineering.linecorp.com/ko/blog/ios-code-signing

iOS 는 aos 처럼 jvm 에서 어플리케이션을 실행하는 형식이 아니라 완벽하게 네이티브 프로그램 형태로 실행된다.

1. 목적 파일 생성
2. 링킹
3. 패키징
4. 프로비전 파일 내장
6. 사이닝

- LLVM>

- Clang

- 패키징
패키징 작업은 필요한 파일들을 정해진 규칙에 맞게 디렉토리 구조로 만드는 작업이다.
실행 파일과 Info.plist 파일, 각종 이미지, 스토리보드 리소스 등을 적절한 디렉토리에 둔다.
필요한 경우 Frameworks 디렉토리 내에 사용 중인 프레임워크들을 둔다. 


- certificate 발급 받기

1. 키 만들기
인증서를 받기 위해서는 키체인 접근 앱에서 CSR(Certificate Signing Request)를 생성해야합니다.

2. 인증서 발급 요청
애플 인증서 발급 사이트에서 development, adhoc 등의 인증서를 발급 요청 할 수 있습니다.
발급 요청을 할 때 1번에서 생성한 CSR 이 필요합니다.

3. 다운로드
다운로드 후 키체인에 잘 적용됬는지 확인

애플에서 발급한 인증서를 이용해 사이닝을 한다.
인증서 종류에는 development, adhoc 등이 있다.

위의 과정을 거치게 되면 앱을 사인할 수 있는 권한이 생겼습니다.
즉, 애플에서 인증을 한 개발자가 된것입니다.
하지만 앱을 사인(sign)할 수 있도록 허락을 받은 상태이지, 기기가 또 나를(개발자)를 신뢰하고있는지를 알아야만 설치를 시켜줍니다!
지금 만든 애플인증서와 iOS기기를 연결시켜주어야하는데 이것을 프로비저닝 프로파일이라고 합니다.
Apple이 발급한 '개발자 인증서'로 코드 서명한 앱을 기기에 설치할 때는 '프로비저닝 프로파일(provisioning profile)'이 반드시 필요합니다. 
프로비저닝 프로파일에 명시된 기기에 프로비저닝 프로파일을 설치해야 Apple의 인증서로 코드 서명된 앱이 아닌 개발자가 직접 발급받은 인증서로 코드 서명된 앱을 기기에서 실행할 수 있기 때문입니다.

패키지 내에 있는, 프레임워크들까지 포함한 모든 파일들에 대해 특정 키로 연산한 결과값을
리스팅한다. 이로써 누군가 패키지 내 파일을 변조한 뒤 다시 배포하는 경우 값 대조를 통해 변조 여부를 알 수 있게 된다.

근데 사이닝 한다는게 대체 뭐지? 그냥 어플에 인증서 정보를 추가한다는 건가? 기기에 어플을 설치한다는 건가?

- appid
com.ProjectR.room3 처럼 appid 를 발급 받을 수 있다.
appid 를 발급받은 후에 애플에 등록해야 한다.


- device 추가
애플 사이트에서 디바이스를 추가해줘야한다.
근데 디바이스를 추가한다는게 뭐지? -> 테스트할 개인 기기 id를 추가하는 것
그러면 테스트할 다른 디바이스를 추가하고 싶을 때마다 프로비저닝 프로파일을 갱신해줘야 하나? -> 그렇다 ㅇㅇ
하지만 위 내용은 개발용 인증서를 생성했을 때 예이고,
배포용 인증서는 따로 기기를 추가하지 않다도 된다.
배포용 인증서인데 테스트 기기의 갯수를 제한하고 싶으면 ad-hoc 인증서를 사용한다.

- Provisioning Profile 생성 방법

애플 사이트에서 appid, device, certificate 정보를 입력 후 프로비저닝 파일 생성


- 프로비전 파일 내장
프로비저닝 프로파일은 app id, certificate, device정보를 가지고 있어, iOS기기 애플 인증서를 연결 해주는 역할

프로비저닝 프로필은 앱 제작에 관한 권한과 이용 기능을 표시한다. 4가지 종류가 있다.
Development Profile: 등록된 디바이스만 앱을 인스톨 할 수 있다. 허가받은 디바이스 아이디가 명시되어있으며 디버그 모드에서 실행된다.
App Store Profile: 판매를 위해 앱스토어에 앱을 등록할 수 있다.
In-house Distribution Profile: 엔터프라이즈 개발자 계정에서만 사용 가능하며 회 사 내 계정을 가진 아무나에게 배포가 가능하다.
Ad-hoc Profile: 정해진 대수만큼만 배포된다. 디바이스 아이디를 따로 명시하지 않는다.

패키지에 프로비전 파일이 임베디드 된다. 개발자 등록을 하고 애플 개발자 콘솔에서 만든 프로비전 파일을 패키지에
둠으로써 누가 만든 애플리케이션인지 알도록 하는 것이다. 간단하게 프로비전 파일을 복사하여 패키지 내에 위치시키는 것으로 된다. 

프로비저닝 프로파일은 .ipa 파일을 뜯으면 나온다. .ipa 파일의 압축을 풀면 Payload 디렉터리 안에 embedded.mobileprovision란 이름의 파일이 있는데 entitlement, expriation date, provisioned devices 등의 정보를 볼 수 있다.

앱에 사이닝을 하는 거고 프로비전 프로파일은 디바이스에 추가되는 것이다.

지금까지 해왔던 과정을 코드사이닝 작업이라고 볼 수 있습니다.
* Code Signing은 앱의 제작자가 서명한 개발자임을 확인하고 서명 후 내용의 변경이 없음을 보장하는 일종의 품질보증확인서다.