UE4 모바일게임출시를위해궁금하실바로그것들! 신광섭 Developer Relations Lead/Programmer Epic Games Korea 목차 왜이컨텐츠가쿠킹되고포함되지? 앱사이즈줄여보기 4.11 기준으로다시살펴보는패치시스템 알아두면유용한소소한팁들 1
왜이컨텐츠가쿠킹되고포함되지? 앱사이즈줄여보기 패키지크기줄이기 패키지크기줄이기 문서로시작하세요. https://docs.unrealengine.com/latest/kor/engine/performance/reducingpackag esize/index.html 프로젝트세팅 에 패키징 항목에 Create compressed cooked packages 옵션을통해서쿠킹컨텐츠압축하기 2
패키지크기줄이기 사용하지않은플러그인제거 Paper2D SpeedTree Importer Script Generator Plugin UObject Example Plugin ArchVis Character ChracterAI Leap Motion Controller Plugin Epic Survey Experimental HTML5 Networking Plugin Cable Component Custom Mesh Component Procedural Mesh Compnent Gear VR Oculus Input Oculus Library Oculus Rift Steam VR 패키지크기줄이기 사용하지않는모듈제거 일부모듈들을빌드에서뺄수있는옵션있음 Unreal Match 3 예제로참고가능 IOSEngine.ini 에 [/Script/BuildSettings.BuildSettings] bcompileapex=false bcompilebox2d=false bcompileicu=true bcompilesimplygon=false bcompileleanandmeanue=true bincludeado=true bcompilerecast=true bcompilespeedtree=false bcompilephysxvehicle=false bcompilefreetype=true bcompileforsize=false bcompilecef3=false 3
패키지크기줄이기 패키지블랙리스트 플랫폼별로포함하지않을어셋 / 폴더설정가능 기본으로포함되는원치않는 / 필요하지않은리소스들제거 4.11 에서는이와관련한편리한옵션추가됨 문서에추가적으로아래항목들도제거가능../../../Engine/Content/EditorBlueprintResources/../../../Engine/Content/EditorLandscapeResources/../../../Engine/Content/EditorMaterials/../../../Engine/Content/EngineDebugMaterials/../../../Engine/Content/EngineMaterials/AntiAliasedTextMaterialTranslucent.uasset../../../Engine/Content/Tutorial/../../../Engine/Content/EditorMeshes/ 패키지크기줄이기 최대다이내믹포인트라이트개수 Used Shared Dynamic Point Light Shaders 가 true 일경우는 Max Dynamic Point Lights 가큰의미가없음 퍼포먼스를위해서변수를 false 로두면큰의미가 전체쉐이더개수에따라최종패키지사이즈차이가크게날수있으니최소한으로하시는것을적극추천 4
패키지크기줄이기 원하지않는컨텐츠가쿠킹되어서포함된다? 기본으로참조 / 포함되는컨텐츠들이있음 Slate 를고려한기본으로추가되는컨텐츠폴더들 BaseEditor.ini 에 [UI] +ContentDirectories=/Game/UI +ContentDirectories=/Game/Widget +ContentDirectories=/Game/Widgets +ContentDirectories=/Engine/MobileResources 패키지크기줄이기 기본으로쿠킹되서포함되는맵과게임오브젝트확인 프로젝트세팅 에 맵 & 모드 에설정된값들 5
패키지크기줄이기 이런과정들만거쳐도 OBB 사이즈가 43.1 MB -> 13.4 MB 여기서조금더하드코어하게줄여본다면? 출시버전에엔진디폴트어셋들교체도고려가능 조금더패키지크기줄이기 출시버전에엔진디폴트어셋들교체하기 엔진에서기본적으로사용할수있는기본어셋들이존재 BaseEngine.ini 에설정되어있음 void UEngine::InitializeObjectReferences() 에서로딩해서세팅을함 줄여볼수있는것은? 내부적으로게임에서사용하는폰트가있을테니 TinyFontName=/Engine/EngineFonts/RobotoTiny.RobotoTiny SmallFontName=/Engine/EngineFonts/Roboto.Roboto MediumFontName=/Engine/EngineFonts/Roboto.Roboto LargeFontName=/Engine/EngineFonts/Roboto.Roboto SubtitleFontName=/Engine/EngineFonts/Roboto.Roboto 6
조금더패키지크기줄이기 출시버전에엔진디폴트어셋들교체하기 모든머티리얼이제대로세팅되어있다면 DefaultMaterialName=/Engine/EngineMaterials/WorldGridMaterial.WorldGridM aterial 변경도고려가능 특별한 material 로 busedasspecialenginematerial 가 true 로되어있어서모든 primitive type 에대한쉐이더를전부가지고있기때문에크다 쿠킹하면 3.62MB 정도 4.11 기준으로다시살펴보면패치시스템 7
모바일게임앱출시 - 컨텐츠 3D 게임은일반적으로컨텐츠가많고, 크기때문에구글의최대 APK 사이즈안에다넣기는사실불가능 작년 9 월쯤구글이 APK 최대사이즈 100 MB 로상향! ios 는사실제한이없지만 Wifi 없이다운로드가능하려면 100 MB 밑으로해야함 100MB 이상의앱을위해구글에서는 OBB 파일지원 UE4 도기본으로안드로이드패키징시에모든게임컨텐츠는 OBB 로빼서자동으로만들어줌 하지만한국의모바일게임은빠른컨텐츠업데이트가필요한사실상온라인게임처럼운영하기에플랫폼지원시스템을쓰기는어려움 그래서개별컨텐츠서버를두고, 서비스중 컨텐츠서버사용한 UE4 모바일게임 초기 IPA/APK 만들기 게임에서사용하는컨텐츠들이포함된 Pak 파일만들기 만들어진컨텐츠파일을다운로드에용의하도록나누기 다운로드를위해서나누어진파일을다운로드하고복구하기 8
기본배포버전만들기 UE4 의컨텐츠다운로드시스템은 UE4 네이티브엔진레벨에서동작 즉, 엔진이구동되어야다운로드기능사용가능 엔진구동을위한최소컨텐츠 + 다운로드관련정보를보여줄최소컨텐츠필요 기본배포버전만들기 - 안드로이드 배포를위한최소한의컨텐츠들만포함한 APK 를만드는것필요 1. 컨텐츠다운로드를위한기본맵만들기 컨텐츠다운로드에대한메시지보여주는 UMG 맵블루프린트에서는그 UMG 를생성해서추가만 9
기본배포버전만들기 - 안드로이드 2. OBB 파일을따로만들지않고, APK 에포함하기 3. 컨텐츠다운로드맵과기본컨텐츠만쿠킹해서포함한 APK 만들기 RunUAT.bat 배치파일을이용해서한번에쿠킹에서 APK 까지만들면편함 C:\Program Files\Unreal Engine\4.11\Engine\Build\BatchFiles\RunUAT.bat" BuildCookRun -project= D:\UnrealProjects\Match3\Match3.uproject" -clientconfig=development -platform=android_etc1 -targetplatform=android -cookflavor=etc1 -cook -map=patch -stage -package -cmdline=patch -compressed -pak -build 기본배포버전만들기 - IOS 1. IOS 의경우 IPA 에기본으로컨텐츠들포함해서만들기때문에컨텐츠다운로드용기본맵만쿠킹패키징하면됨예 ) C:\Program Files\Unreal Engine\4.11\Engine\Build\BatchFiles\RunUAT.bat" BuildCookRun -project= D:\UnrealProjects\Match3\Match3.uproject" -clientconfig=development -platform=ios -targetplatform=ios -cook -map=patch -stage -package -cmdline=patch -compressed -pak -build 10
다운로드컨텐츠만들기 - 안드로이드 1. 기본배포버전에포함된컨텐츠를제외한다운로드컨텐츠만들기필요 - DLC 기능사용 DLC 기능? 배포버전설정해서쿠킹하면거기서쿠킹된컨텐츠들을기억해두고, DLC 쿠킹시에설정했던배포버전을사용해서쿠킹하면배포버전에서쿠킹된컨텐츠들은제외되는기능 다운로드컨텐츠만들기 - 안드로이드 2. 베이스버전쿠킹하기예 ) C:\Program Files\Unreal Engine\4.11\Engine\Build\BatchFiles\RunUAT.bat" BuildCookRun -project= D:\UnrealProjects\Match3\Match3.uproject" -clientconfig=development -platform=android_etc1 -targetplatform=android -cookflavor=etc1 -cook -map=patch -cmdline=patch -build -unversionedcookedcontent -createreleaseversion=releasecontent 쿠킹이완료되면게임프로젝트폴더 \Releases\ReleaseContent\Android_ETC1 폴더안에쿠킹된정보를기억해둔 AssetRegistry.bin 생성됨 11
다운로드컨텐츠만들기 - 안드로이드 3. 다운로드컨텐츠만들기 배포버전을제외한실제게임에필요한컨텐츠들을모두포함한다운로드컨텐츠제작필요예 ) C:\Program Files\Unreal Engine\4.11\Engine\Build\BatchFiles\RunUAT.bat BuildCookRun -project= D:\UnrealProjects\Match3\Match3.uproject" -clientconfig=development -platform=android_etc1 -targetplatform=android -cookflavor=etc1 -build -cook -map=gamelevel+main -unversionedcookedcontent -BasedOnReleaseVersion=ReleaseContent -DLCName=DownloadContent -stage -compressed -pak -cmdline=main Map 부분에포함되어야하는모든맵을넣여야하는데맵이추가될때마다 comandline 에추가하는것은좋지못함 다운로드컨텐츠만들기 - 안드로이드 3. 다운로드컨텐츠만들기 ( 계속 ) -MAPINISECTION= 파라미터를사용하면좀더편함예 ) C:\Program Files\Unreal Engine\4.11\Engine\Build\BatchFiles\RunUAT.bat BuildCookRun -project= D:\UnrealProjects\Match3\Match3.uproject" -clientconfig=development -platform=android_etc1 -targetplatform=android -cookflavor=etc1 -build cook -MAPINISECTION=DownloadContent -unversionedcookedcontent -BasedOnReleaseVersion=ReleaseContent -DLCName=DownloadContent -stage -compressed -pak -cmdline=main DefaultEditor.ini 에 [DownloadConent] +Map=Main +Map=GameLevel 이렇게설정하면 DownloadConent 에설정됨맵들을전부쿠킹 12
다운로드컨텐츠만들기 - 안드로이드 3. 다운로드컨텐츠만들기 ( 계속 ) 이렇게쿠킹이완료되면배포해야하는컨텐츠들이전부포함됨하나의큰 Pak 파일이생성됨 예 ) Match3\Plugins\DownloadContent\Saved\StagedBuilds\Android_ETC 1\UnrealMatch3\Content\Paks\UnrealMatch3-Android_ETC1.pak 다운로드컨텐츠만들기 - IOS 1. 베이스버전쿠킹하기예 ) C:\Program Files\Unreal Engine\4.11\Engine\Build\BatchFiles\RunUAT.bat" BuildCookRun -project= D:\UnrealProjects\Match3\Match3.uproject" -clientconfig=development -platform=ios -targetplatform=ios -cook -map=patch -cmdline=patch -build -unversionedcookedcontent -createreleaseversion=releasecontent 2. 다운로드컨텐츠만들기예 ) C:\Program Files\Unreal Engine\4.11\Engine\Build\BatchFiles\RunUAT.bat BuildCookRun -project= D:\UnrealProjects\Match3\Match3.uproject" -clientconfig=development -platform=ios -targetplatform=ios -build cook -MAPINISECTION=DownloadContent -unversionedcookedcontent -BasedOnReleaseVersion=ReleaseContent -DLCName=DownloadContent -stage -compressed -pak -cmdline=main 13
다운로드컨텐츠만들기 이제안드로이드 /IOS 별로만들어진 Pak 파일을 UE4 컨텐츠배포시스템에서사용할수있도록작업필요 사용되는툴과모듈은 BuildPatchTool 과 BuildPatchServices BuildPatchTool 파일을다운로드에편하도록청크로나누어주고, 복구를위한 manifest 파일을만드는툴 BuildPatchServices BuildPatchTool 로만들어진 manifest 를이용해서청크들을다운로드하고, 원본파일을복구해주시는모듈 다운로드컨텐츠만들기 BuildPatchTool 사용법 위치 Engine\Binaries\Win64\BuildPatchTool.exe 예 ) Engine\Binaries\Win64\BuildPatchTool.exe -BuildRoot= D:\UnrealProjects\Match3 \Plugins\DownloadContent\Saved\StagedBuilds\Android_ETC1" -CloudDir= D:\Unr ealprojects\match3\clouddir" -AppID=0 -AppName= MatchGame" -BuildVersion= M atchgame-1" -AppLaunch=".\Engine\Binaries\Win64\UE4Editor.exe" -AppArgs="" -c ustomint="chunkid=1 BuildVersion 을비교해서새로운컨텐츠인지비교하기때문에 BuildVersion 은중요 생성되는것은 14
컨텐츠다운로드하기 BuildPatchServices 사용법 안드로이드환경 사용을위해필요한작업들살펴보면 1. BuildPatchTool로만들어진 manifest 파일과청크폴더를웹호스팅서버에올 려둔다. 2. BuildPatchServices에서사용하기위해 Manifest 파일을다운로드한다. 3. BuildPatchServices에청크폴더를다운로드할웹서버주소를세팅 4. 다운로드된청크및임시파일을저장할폴더와복구된원본파일이놓여질 경로를 BuildPatchServices에세팅 5. BuildPatchServices에다운로드요청 컨텐츠다운로드하기 BuildPatchServices 에서사용하기위해 Manifest 파일을다운로드한다. HTTP 모듈을사용해서 Manifest 파일다운로드필요 단, 추후에컨텐츠가변경되어패치가있을수도있고, 그렇게되면다운로드받아야하는 manifest 파일의이름이변경될수있음 그래서간단한 manifest 파일정보를가지고있는파일을올리거나알맞은 manifest 파일을리턴해주는웹프로그램필요 15
컨텐츠다운로드하기 예 ) manifest 파일정보를가지고있는간단한 json 파일을사용한다면? 그럼실제 BuildPatchServices 을사용하기위해서 Build.cs 에필요한모듈들은 PublicDependencyModuleNames.AddRange( new string[] "HTTP", "Json", "OnlineSubsystem", "BuildPatchServices", "PakFile", ); 컨텐츠다운로드하기 예 ) manifest 파일정보를가지고있는간단한 json 파일을사용한다면? ( 계속 ) TSharedRef<class IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest(); HttpRequest->OnProcessRequestComplete().BindRaw(this, &FPatchManager::GetManifestFileU RL_HttpRequestComplete); HttpRequest->SetURL(TEXT(http://patch.myserver.com/downloadcontent.json)); HttpRequest->SetVerb(TEXT("GET")); HttpRequest->ProcessRequest(); 16
컨텐츠다운로드하기 예 ) manifest 파일정보를가지고있는간단한 json 파일을사용한다면?( 계속 ) downloadcontent.json 는아주단순하게 manifest": http://patch.myserver.com/matchgamematchgame-1.manifest" 컨텐츠다운로드하기 예 ) manifest 파일정보를가지고있는간단한 json 파일을사용한다면?( 계속 ) FPatchManager::GetManifestFileURL_HttpRequestComplete (FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bsucceeded) 함수에서는 ResponseStr = HttpResponse->GetContentAsString(); if (EHttpResponseCodes::IsOk(HttpResponse->GetResponseCode())) TSharedPtr<FJsonObject> JSONManifestObject; TSharedRef<TJsonReader<TCHAR>> Reader = TJsonReaderFactory<TCHAR>::Create(ResponseStr); // Attempt to deserialize JSON if (FJsonSerializer::Deserialize(Reader, JSONManifestObject)!JSONManifestObject.IsValid()) 17
컨텐츠다운로드하기 // Attempt to deserialize JSON if (FJsonSerializer::Deserialize(Reader, JSONManifestObject)!JSONManifestObject.IsValid()) // Get the values map TMap<FString, TSharedPtr<FJsonValue>>& JsonValueMap = JSONManifestObject->Values; TSharedPtr< FJsonValue > JsonAppNameString = JsonValueMap.FindRef(TEXT("manifest")); if (JsonAppNameString.IsValid()) FString ManifestFile = JsonAppNameString->AsString(); // Create the Http request and add to pending request list TSharedRef<class IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest(); HttpRequest->OnProcessRequestComplete().BindRaw(this, &FPatchManager::CompareManifestFiles_HttpRequestC omplete); HttpRequest->SetURL(ManifestFile); HttpRequest->SetVerb(TEXT("GET")); HttpRequest->ProcessRequest(); 컨텐츠다운로드하기 CompareManifestFiles_HttpRequestComplete 함수에서는 IBuildPatchServicesModule* BuildPatchServices = &FModuleManager::LoadModuleChecked<IBuildPatchServicesModule>(TEXT("BuildPatchServices")); if (bsucceeded && HttpResponse.IsValid()) ResponseStr = HttpResponse->GetContentAsString(); if (EHttpResponseCodes::IsOk(HttpResponse->GetResponseCode())) InstallManifest = BuildPatchServices->MakeManifestFromJSON(ResponseStr); FString DownloadedManifestFileName = FPaths::Combine(*GExternalFilePath, TEXT("Patches"), TEXT("DownloadedManifest.manifest")); CurrentManifest = BuildPatchServices- >LoadManifestFromFile(DownloadedManifestFileName); if (!CurrentManifest.IsValid() (InstallManifest->GetVersionString()!= CurrentManifest- >GetVersionString())) // download new content 18
컨텐츠다운로드하기 // download new content IBuildPatchServicesModule* BuildPatchServices = &FModuleManager::LoadModuleChecked<IBuildPatchServicesModule>(TEXT("BuildPatchServices")); IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); StageDir = FPaths::Combine(*GExternalFilePath, TEXT("Patches"), TEXT("Staged")); InstallDir = FPaths::Combine(*GExternalFilePath, TEXT("Patches"), TEXT("Installed")); BuildPatchServices->SetCloudDirectory(CloudURL); BuildPatchServices->SetStagingDirectory(StageDir); if (CurrentManifest.IsValid()) BuildInstaller = BuildPatchServices->StartBuildInstall(CurrentManifest, InstallManifest, InstallDir, FBuildPatchBoolManifestDelegate::CreateRaw(this, &FPatchManager::OnDownloadCompleted)); else BuildInstaller = BuildPatchServices->StartBuildInstall(nullptr, InstallManifest, InstallDir, FBuildPatchBoolManifestDelegate::CreateRaw(this, &FPatchManager::OnDownloadCompleted)); 컨텐츠다운로드하기 void FPatchManager::OnDownloadCompleted(bool bsuccess, IBuildManifestRef Manifest) FString DownloadedManifestFileName = FPaths::Combine(*GExternalFilePath, TEXT("Patches"), TEXT("DownloadedManifest.manifest")); BuildPatchServices->SaveManifestToFile(DownloadedManifestFileName, Manifest); // Pak 파일찾기 // Pak 파일찾고, Mount 하기 19
컨텐츠다운로드하기 void FPatchManager::OnDownloadCompleted(bool bsuccess, IBuildManifestRef Manifest) // Pak 파일찾기 class FPakSearchVisitor : public IPlatformFile::FDirectoryVisitor TArray<FString>& FoundPakFiles; public: FPakSearchVisitor(TArray<FString>& InFoundPakFiles) : FoundPakFiles(InFoundPakFiles) virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bisdirectory) if (bisdirectory == false) FString Filename(FilenameOrDirectory); if (FPaths::GetExtension(Filename) == TEXT("pak")) FoundPakFiles.Add(Filename); // Add Filename to Manifest IFileManager::Get().SetTimeStamp(*Filename, FDateTime::UtcNow()); return true; ; 컨텐츠다운로드하기 void FPatchManager::OnDownloadCompleted(bool bsuccess, IBuildManifestRef Manifest) // Pak 파일찾고, Mount 하기 TArray<FString> FoundPaks; IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); FPakSearchVisitor Visitor(FoundPaks); PlatformFile.IterateDirectoryRecursively(*InstallDir, Visitor); for (const auto& PakPath : FoundPaks) FPakPlatformFile* PakPlatformFile = static_cast<fpakplatformfile*>(fplatformfilemanager::get().findplatformfile(text("pakfile"))); PakPlatformFile->Mount(*PakPath, 1, TEXT("../../../Match3/")); // 메인게임레벨로딩 20
패치시스템정리 최소한의컨텐츠다운로드를위한배포용 APK/IPA 만들기 실제게임컨텐츠를모두포함한 pak 파일만들기 DLC 기능사용으로초기배포컨텐츠제거해서생성 BuildPatchTool 을사용해서 pak 파일을다운로드할수있게가공 BuildPatchServices 를사용해서 BuildPatchTool 로만들어진 manifest 와청크파일다운로드하고, pak 파일복구해서 mount! 패치시스템정리 생각해볼이슈들 4.11 전의엔진버전사용하시는경우같은리소스재쿠킹시에쿠킹된컨텐츠바이너리가매번바뀌는이슈가많을수있음 4.11 업데이트추천드리고, 경우에따라서는어셋다시저장해야해결됨 Compressed 옵션으로 Pak 파일을압축할경우실제어셋이많이바뀌지않았어도만들어진 Pak 파일은전에 Pak 많은바이너리차이가날수있고, 이로인해다운로드받는청크가많아질수있음 Pak 파일을여러개로나누어만들어서다운로드받고, Mount 를여러개하는것도현재로서는이이슈를우회할방법 내부적으로개선할수있는점이있을지고민중 마지막으로, 설명드린초기배포버전과다운로드컨텐츠제작법을 GUI 기반으로쉽게할수있는기능개발예정! 21
알아두면유용한소소한팁들 Android 6.0 runtime permission 처리예제 안드로이드에서 Target SDK 를 23(6.0) 으로할경우 runtime permission 사용이필요 UE4 에지원추가방법은? 1. Engine\Build\Android\Java\src\com\epicgames\ue4\ GameActivity.java 에 runtime permission 관련처리추가필요 http://stackoverflow.com/questions/36098366/android-marshmallowpermissions/36098423#36098423 참조 첨부된 GameActivity.java.txt 파일참조. 2. Engine\Build\Android\Java\src\com\google\android\vending\expansion\downloader\impl\DownloadNotific ation.java 수정필요 현재구글에서제공한 APK Expansion Zip Library 를사용하고있는데아직 6.0 호환버전을지원하고있지않음 그래서어차피컨테츠다운로드를사용하는거면 setlatesteventinfo 를코멘트처리해서임시로우회 3. 최신 android.support.v4 로교체 C:\NVPACK\android-sdk-windows\SDK Manager.exe 실행후에 Extras 항목에 Android Support Library 를최신버전받기 C:\NVPACK\android-sdk-windows\extras\android\support\v4\android-support-v4.jar 파일을 Engine/Build/Android/Java/libs/android-support-v4.jar 덮어쓰기 4. 안드로이드 6.0 은 org.apache.http 가제거되었기에따로추가필요 C:\NVPACK\android-sdk-windows\platforms\android-23\optional\org.apache.http.legacy.jar 을 Engine/Build/Android/Java/libs/org.apache.http.legacy.jar 복사추가 22
안드로이드비디오녹화 디버깅이나기타목적을위해서안드로이드화면을녹화가능 PC 에디바이스연결후콘솔창에아래명령어입력 adb shell screenrecord /sdcard/ 파일이름.mp4 --bit-rate 8000000 디바이스에서벌어지는내용을 180 초까지녹화할수있음 언제든지콘솔창에서 Ctrl + C 를누르면비디오녹화중지됨 비디오는일반적으로폰의루트디렉토리에서찾을수있음 감사합니다! Q/A Q: 다운로드시스템을썼을때스트리밍레벨의경우제대로로딩이안된다. 테스트해본결과스트리밍레벨도로딩이잘되는것으로확인이됩니다. 대신에 Engine 에있는컨텐츠를사용할경우이컨텐츠들은현재추천드린방식의다운로드받아서프로젝트경로로 Mount 하는경우에는포함이되지않기때문에그리소스들이나오지않아서제대로로딩이안된다고보일수있을것같습니다.( 예를들어대표적인것이 /Engine/EngineSky/BP_Sky_Sphere.uasset) 그래서그런컨텐츠들을복사해서게임의컨텐츠폴더에옮겨서사용하시면될것같습니다. Q: 4.10 에서는패치관련한실행파일이나모듈이 (BuildPatchTool.exe 와 BuildPatchServices 모듈 ) 런쳐버전에는없다. 네. 4.10 에런쳐버전에서느는해당툴이나모듈이없습니다. 그래서 github 에서풀소스코드다운로드후에빌드해서만드셔야하는데요. 대신에 4.11 에는런쳐버전에도포함이되어있고, 컨텐츠다시쿠킹시에변경되지않은컨텐츠가변경되는이슈들이많이해결되었으므로 4.11 로업그레이드해서사용하시기를추천드립니다. 23