iOS ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์ถ• ๋ฐ ์ œ๊ณต ๊ธฐ๋Šฅ

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” Plarium Krasnodar ์ŠคํŠœ๋””์˜ค๊ฐ€ CI/CD๋ฅผ ๋””๋ฒ„๊น…ํ•˜๋Š” ๊ณผ์ •์—์„œ ์Œ“์•„์˜จ iOS ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์กฐ๋ฆฝํ•˜๊ณ  ์‚ฌ์šฉ์ž์—๊ฒŒ ์ „๋‹ฌํ•œ ๊ฒฝํ—˜์„ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.

iOS ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์ถ• ๋ฐ ์ œ๊ณต ๊ธฐ๋Šฅ

ํ›ˆ๋ จ

์–ด๋–ค ์‹์œผ๋กœ๋“  Apple ์žฅ์น˜์šฉ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์— ์ฐธ์—ฌํ•˜๋Š” ๋ชจ๋“  ์‚ฌ๋žŒ์€ ์ด๋ฏธ ๋…ผ์Ÿ์˜ ์—ฌ์ง€๊ฐ€ ์žˆ๋Š” ์ธํ”„๋ผ์˜ ํŽธ๋ฆฌํ•จ์„ ๋†’์ด ํ‰๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž ํ”„๋กœํ•„ ๋ฉ”๋‰ด๋ถ€ํ„ฐ ๋””๋ฒ„๊ทธ ๋ฐ ๋นŒ๋“œ ๋„๊ตฌ๊นŒ์ง€ ๋ชจ๋“  ๊ณณ์—์„œ ์–ด๋ ค์›€์ด ๋ฐœ๊ฒฌ๋ฉ๋‹ˆ๋‹ค.

์ธํ„ฐ๋„ท์—๋Š” "๊ธฐ๋ณธ"์— ๊ด€ํ•œ ๊ธฐ์‚ฌ๊ฐ€ ๋งŽ์ด ์žˆ์œผ๋ฏ€๋กœ ์ฃผ์š” ๋‚ด์šฉ์„ ๊ฐ•์กฐํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์„ฑ๊ณต์ ์œผ๋กœ ๋นŒ๋“œํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์‚ฌํ•ญ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ๊ฐœ๋ฐœ์ž ๊ณ„์ •;
  • ๋นŒ๋“œ ์„œ๋ฒ„ ์—ญํ• ์„ ํ•˜๋Š” macOS ๊ธฐ๋ฐ˜ ์žฅ์น˜
  • ์ƒ์„ฑ๋จ ๊ฐœ๋ฐœ์ž ์ธ์ฆ์„œ, ์ด๋Š” ์‹ ์ฒญ์„œ์— ์„œ๋ช…ํ•˜๋Š” ๋ฐ ์ถ”๊ฐ€๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ๊ณ ์œ ํ•œ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ID (์™€์ผ๋“œ์นด๋“œ ID๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ด€๋ จ ๋„๋ฉ”์ธ, ํ‘ธ์‹œ ์•Œ๋ฆผ, Apple ๋กœ๊ทธ์ธ ๋“ฑ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋งŽ์€ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ๋ฒˆ๋“ค ์‹๋ณ„์ž์˜ ์ค‘์š”์„ฑ์— ์œ ์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.)
  • ํ”„๋กœํ•„ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ์„œ๋ช….

๊ฐœ๋ฐœ์ž ์ธ์ฆ์„œ๋Š” ๋ชจ๋“  macOS ์žฅ์น˜์—์„œ ํ‚ค์ฒด์ธ์„ ํ†ตํ•ด ์ƒ์„ฑ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ธ์ฆ์„œ ์œ ํ˜•์€ ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ™˜๊ฒฝ(๊ฐœ๋ฐœ, QA, ์Šคํ…Œ์ด์ง•, ํ”„๋กœ๋•์…˜)์— ๋”ฐ๋ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ช… ํ”„๋กœํ•„ ์œ ํ˜•๋„ ๋‹ฌ๋ผ์ง€๋ฉฐ(๊ฐœ๋ฐœ ๋˜๋Š” ๋ฐฐํฌ)๋„ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

์ฃผ์š” ํ”„๋กœํ•„ ์œ ํ˜•:

  • ๊ฐœ๋ฐœ - ๊ฐœ๋ฐœํŒ€์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ช…์„ ์œ„ํ•ด ๊ฐœ๋ฐœ ์ธ์ฆ์„œ๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค(์œ ํ˜• ์ด๋ฆ„ iPhone ๊ฐœ๋ฐœ์ž: XXXXX).
  • ์ž„์‹œ - ํ…Œ์ŠคํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ช… ๋ฐ QA ๋ถ€์„œ์˜ ๋‚ด๋ถ€ ํ™•์ธ์„ ์œ„ํ•ด ๊ฐœ๋ฐœ์ž์˜ ๋ฐฐํฌ ์ธ์ฆ์„œ๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค(์œ ํ˜• ์ด๋ฆ„ iPhone ๋ฐฐํฌ: XXXXX).
  • App Store - TestFlight๋ฅผ ํ†ตํ•œ ์™ธ๋ถ€ ํ…Œ์ŠคํŠธ ๋ฐ App Store์— ์—…๋กœ๋“œ๋ฅผ ์œ„ํ•œ ๋ฆด๋ฆฌ์Šค ๋นŒ๋“œ, ๊ฐœ๋ฐœ์ž์˜ ๋ฐฐํฌ ์ธ์ฆ์„œ๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ ๋ฐ ์ž„์‹œ ํ”„๋กœํ•„์„ ์ƒ์„ฑํ•  ๋•Œ์—๋„ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ์žฅ์น˜ ๋ชฉ๋ก, ์‚ฌ์šฉ์ž์˜ ์•ก์„ธ์Šค๋ฅผ ์ถ”๊ฐ€๋กœ ์ œํ•œํ•  ์ˆ˜ ์žˆ๋Š” ๋นŒ๋“œ๋ฅผ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋น„๊ณต๊ฐœ ๋ฒ ํƒ€ ํ…Œ์ŠคํŠธ ์ค‘ ์•ก์„ธ์Šค ์ œ์–ด๋Š” ๋‚˜์ค‘์— ์„ค๋ช…ํ•  TestFlight์—์„œ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ App Store ํ”„๋กœํ•„์—๋Š” ์žฅ์น˜ ๋ชฉ๋ก์ด ์—†์Šต๋‹ˆ๋‹ค.

๋ช…ํ™•์„ฑ์„ ์œ„ํ•ด ๊ฐœ๋ฐœ์ž ํ”„๋กœํ•„์„ ์•„๋ž˜ ํ‘œ ํ˜•์‹์œผ๋กœ ์ œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์กฐ๋ฆฝ์— ํ•„์š”ํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜์™€ ํ•ด๋‹น ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์–ด๋””์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š”์ง€ ๋” ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

iOS ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์ถ• ๋ฐ ์ œ๊ณต ๊ธฐ๋Šฅ

์กฐ๋ฆฝ

ํ”„๋กœ์ ํŠธ ๋ฐ ํ™˜๊ฒฝ๋ณ„๋กœ ์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ๋” ์‰ฝ๊ฒŒ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ”„๋กœํ•„ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ${ProjectName}_${Instance}, ์ฆ‰ ํ”„๋กœ์ ํŠธ ์ด๋ฆ„ + ์ธ์Šคํ„ด์Šค(์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋‹ค๋ฆ„: Dev, QA, GD, Staging, Live ๋“ฑ)

๋นŒ๋“œ ์„œ๋ฒ„๋กœ ๊ฐ€์ ธ์˜ค๋ฉด ํ”„๋กœํ•„ ์ด๋ฆ„์ด ๊ณ ์œ  ID๋กœ ๋ณ€๊ฒฝ๋˜๊ณ  ํ•ด๋‹น ํด๋”๋กœ ์ด๋™๋ฉ๋‹ˆ๋‹ค. /Users/$Username/Library/MobileDevice/Provisioning Profiles (์–ด๋”” $Username ๋นŒ๋“œ ์„œ๋ฒ„์˜ ์‚ฌ์šฉ์ž ๊ณ„์ • ์ด๋ฆ„์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค).

*.ipa ํŒŒ์ผ์„ ๋นŒ๋“œํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ๋ ˆ๊ฑฐ์‹œ(PackageApplication)์™€ ์ตœ์‹ (XcAchive ์ƒ์„ฑ ๋ฐ ๋‚ด๋ณด๋‚ด๊ธฐ๋ฅผ ํ†ตํ•ด)์˜ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ ๋ฒ„์ „ 8.3๋ถ€ํ„ฐ ์•ฑ ํŒŒ์ผ ํŒจํ‚ค์ง• ๋ชจ๋“ˆ์ด Xcode ๋ฐฐํฌํŒ์—์„œ ์ œ๊ฑฐ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ด์ „ Xcode(๋ฒ„์ „ 8.2 ์ดํ•˜)์˜ ๋ชจ๋“ˆ์„ ๋‹ค์Œ ํด๋”์— ๋ณต์‚ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/

๊ทธ๋Ÿฐ ๋‹ค์Œ ๋‹ค์Œ ๋ช…๋ น์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

chmod +x /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/*

๋‹ค์Œ์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ *.app ํŒŒ์ผ์„ ์ˆ˜์ง‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

xcodebuild 
-workspace $ProjectDir/$ProjectName.xcworkspace 
-scheme $SchemeName 
-sdk iphoneos 
build 
-configuration Release 
-derivedDataPath build 
CODE_SIGN_IDENTITY=โ€$DevAccNameโ€
PROVISIONING_PROFILE=โ€$ProfileIdโ€
DEPLOYMENT_POSTPROCESSING=YES 
SKIP_INSTALL=YES 
ENABLE_BITCODE=NO

์žฅ์†Œ :

-workspace โ€” ํ”„๋กœ์ ํŠธ ํŒŒ์ผ์˜ ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค.

-scheme โ€” ํ”„๋กœ์ ํŠธ์— ์ง€์ •๋œ ์‚ฌ์šฉ๋œ ๊ตฌ์„ฑํ‘œ.

-derivedDataPath โ€” ์กฐ๋ฆฝ๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜(*.app)์„ ๋‹ค์šด๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•œ ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค.

CODE_SIGN_IDENTITY โ€” ํ‚ค์ฒด์ธ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐœ๋ฐœ์ž ๊ณ„์ •์˜ ์ด๋ฆ„(iPhone ๊ฐœ๋ฐœ์ž: XXXX XXXXXXX, ๊ด„ํ˜ธ ์•ˆ์— TeamID ์—†์Œ).

iOS ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์ถ• ๋ฐ ์ œ๊ณต ๊ธฐ๋Šฅ

PROVISIONING_PROFILE โ€” ๋‹ค์Œ ๋ช…๋ น์œผ๋กœ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ช…์šฉ ํ”„๋กœํ•„ ID์ž…๋‹ˆ๋‹ค.

cd "/Users/$Username/Library/MobileDevice/Provisioning Profiles/" && find *.mobileprovision -type f | xargs grep -li ">${ProjectName}_${Instance}<" | sed -e 's/.mobileprovision//'

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ถ”๊ฐ€ ํ”„๋กœํ•„(์˜ˆ: ํ‘ธ์‹œ ์•Œ๋ฆผ์šฉ)์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๋Œ€์‹  PROVISIONING_PROFILE ๋‚˜ํƒ€๋‚ด๋‹ค:

APP_PROFILE=โ€$AppProfileโ€ 
EXTENSION_PROFILE=โ€$ExtProfileโ€ 

๋‹ค์Œ์œผ๋กœ ๊ฒฐ๊ณผ *.app ํŒŒ์ผ์„ *.ipa๋กœ ํŒจํ‚ค์ง•ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ช…๋ น์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

/usr/bin/xcrun --sdk iphoneos PackageApplication 
-v $(find "$ProjectDir/build/Build/Products/Release-iphoneos" -name "*.app") 
-o "$ProjectDir/$ProjectName_$Instance.ipa"

๊ทธ๋Ÿฌ๋‚˜ ์ด ๋ฐฉ๋ฒ•์€ Apple์˜ ๊ด€์ ์—์„œ ๋ณผ ๋•Œ ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์•„์นด์ด๋ธŒ์—์„œ ๋‚ด๋ณด๋‚ด์–ด *.ipa๋ฅผ ์–ป๋Š” ๊ฒƒ์ด ๊ด€๋ จ๋ฉ๋‹ˆ๋‹ค.

๋จผ์ € ๋‹ค์Œ ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ์•„์นด์ด๋ธŒ๋ฅผ ์ˆ˜์ง‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

xcodebuild 
-workspace $ProjectDir/$ProjectName.xcworkspace 
-scheme $SchemeName 
-sdk iphoneos 
-configuration Release 
archive 
-archivePath $ProjectDir/build/$ProjectName.xcarchive 
CODE_SIGN_IDENTITY=โ€$DevAccNameโ€ 
PROVISIONING_PROFILE=โ€$ProfileIdโ€
ENABLE_BITCODE=NO 
SYNCHRONOUS_SYMBOL_PROCESSING=FALSE

์ฐจ์ด์ ์€ ์กฐ๋ฆฝ๋ฐฉ๋ฒ•๊ณผ ์˜ต์…˜์— ์žˆ์Šต๋‹ˆ๋‹ค SYNCHRONOUS_SYMBOL_PROCESSING, ๋นŒ๋“œ ์‹œ ๊ธฐํ˜ธ ์–ธ๋กœ๋“œ๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์œผ๋กœ ๋‚ด๋ณด๋‚ด๊ธฐ ์„ค์ •์ด ํฌํ•จ๋œ ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ExportSettings="$ProjectDir/exportOptions.plist"

cat << EOF > $ExportSettings
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>compileBitcode</key>
<false/>
<key>uploadBitcode</key>
<false/>
<key>uploadSymbols</key>
<false/>
<key>method</key>
<string>$Method</string>
<key>provisioningProfiles</key>
<dict>
<key>$BundleID</key>
<string>$ProfileId</string>
</dict>
<key>signingCertificate</key>
<string>$DevAccName</string>
<key>signingStyle</key>
<string>manual</string>
<key>stripSwiftSymbols</key>
<true/>
<key>teamID</key>
<string>$TeamID</string>
<key>thinning</key>
<string><none></string>
</dict>
</plist>
EOF

์žฅ์†Œ :

$Method โ€” ์ „๋‹ฌ ๋ฐฉ๋ฒ•์€ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ์„œ๋ช… ํ”„๋กœํ•„ ์œ ํ˜•์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, Development์˜ ๊ฒฝ์šฐ ๊ฐ’์€ development์ด๊ณ  Ad Hoc์˜ ๊ฒฝ์šฐ ์ž„์‹œ, App Store์˜ ๊ฒฝ์šฐ app-store์ž…๋‹ˆ๋‹ค.

$BundleID โ€” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค์ •์— ์ง€์ •๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ID์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ๋ช…๋ น์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

defaults read $ProjectDir/Info CFBundleIdentifier

$DevAccName ะธ $ProfileId โ€” ์ด์ „์— ์‚ฌ์šฉ๋œ ๊ฐœ๋ฐœ์ž ์ด๋ฆ„ ๋ฐ ์„œ๋ช… ํ”„๋กœํ•„ ID ์„ค์ •์€ ๋‚ด๋ณด๋‚ด๊ธฐ ์„ค์ •์˜ ๊ฐ’๊ณผ ์ผ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

$TeamID โ€” ๊ฐœ๋ฐœ์ž ์ด๋ฆ„ ๋’ค ๊ด„ํ˜ธ ์•ˆ์˜ XNUMX์ž๋ฆฌ ID, ์˜ˆ: iPhone ๊ฐœ๋ฐœ์ž: โ€ฆโ€ฆ (XXXXXXXXX); ํ‚ค์ฒด์ธ์—์„œ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์œผ๋กœ ๋‚ด๋ณด๋‚ด๊ธฐ ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•„์š”ํ•œ *.ipa ํŒŒ์ผ์„ ์–ป์Šต๋‹ˆ๋‹ค.

xcodebuild 
-exportArchive 
-archivePath $ProjectDir/build/$ProjectName.xcarchive 
-exportPath $ProjectDir 
-exportOptionsPlist $ExportSettings

๋ฐฐ๋‹ฌ

์ด์ œ ์ˆ˜์ง‘๋œ ํŒŒ์ผ์„ ์ตœ์ข… ์‚ฌ์šฉ์ž์—๊ฒŒ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์žฅ์น˜์— ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

HockeyApp, AppBlade ๋“ฑ ๊ฐœ๋ฐœ ๋ฐ Ad Hoc ๋นŒ๋“œ ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ ๋งŽ์€ ์„œ๋น„์Šค๊ฐ€ ์žˆ์ง€๋งŒ ์ด ๊ธฐ์‚ฌ์—์„œ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ ๋…๋ฆฝ ์‹คํ–‰ํ˜• ์„œ๋ฒ„์— ๋Œ€ํ•ด ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

iOS์šฉ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค์น˜๋Š” 2๋‹จ๊ณ„๋กœ ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค.

  1. ํ•ญ๋ชฉ ์„œ๋น„์Šค๋ฅผ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค์น˜ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค.
  2. HTTPS๋ฅผ ํ†ตํ•ด ๋งค๋‹ˆํŽ˜์ŠคํŠธ์— ์ง€์ •๋œ ์ •๋ณด์— ๋”ฐ๋ผ *.ipa ํŒŒ์ผ์„ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๋จผ์ € ๋‹ค์Œ ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ์„ค์น˜ ๋งค๋‹ˆํŽ˜์ŠคํŠธ(ํŒŒ์ผ ํ˜•์‹ *.plist)๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

cat << EOF > $manifest
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string>$ipaUrl</string>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>$BundleID</string>
<key>bundle-version</key>
<string>$AppVersion</string>
<key>kind</key>
<string>software</string>
<key>title</key>
<string>$ProjectName_$Instance</string>
<key>subtitle</key>
<string>$Instance</string>
</dict>
</dict>
</array>
</dict>
</plist>
EOF

๋ณด์‹œ๋‹ค์‹œํ”ผ ๋งค๋‹ˆํŽ˜์ŠคํŠธ์—๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์ถ•๊ณผ ๊ด€๋ จ๋œ ๊ฑฐ์˜ ๋ชจ๋“  ๋งค๊ฐœ ๋ณ€์ˆ˜๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฒ„์ „($AppVersion)๋Š” ๋‹ค์Œ ๋ช…๋ น์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

defaults read $ProjectDir/Info CFBundleVersion

๋งค๊ฐœ ๋ณ€์ˆ˜ $ipaUrl *.ipa ํŒŒ์ผ์„ ๋‹ค์šด๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š” ์ง์ ‘ ๋งํฌ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. iOS XNUMX ๋ฒ„์ „๋ถ€ํ„ฐ๋Š” HTTPS๋ฅผ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿ ๋ฒˆ์งธ ๋ฒ„์ „์—์„œ๋Š” ๋งค๋‹ˆํŽ˜์ŠคํŠธ์˜ ํ˜•์‹์ด ์•ฝ๊ฐ„ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์•„์ด์ฝ˜์— ๋Œ€ํ•œ ์„ค์ •์ด ํฌํ•จ๋œ ๋ธ”๋ก์ž…๋‹ˆ๋‹ค.

<images>
   <image>...</image>
</images>

๋”ฐ๋ผ์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์„ค์น˜ํ•˜๋ ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋งํฌ๊ฐ€ ํฌํ•จ๋œ ๊ฐ„๋‹จํ•œ HTML ํŽ˜์ด์ง€์ด๋ฉด ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.

itms-services://?action=download-manifest&url=https://$ServerUrl/$ProjectName/$Instance/iOS/$AppVersion/manifest.plist

๊ฐœ๋ฐœ ๋ฐ ํ…Œ์ŠคํŠธ ๋ถ€์„œ์˜ ์š”๊ตฌ์— ๋งž์ถฐ Plarium์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์ž์ฒด ๋นŒ๋“œ ์„ค์น˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

  • ์ž์œจ์„ฑ๊ณผ ๋…๋ฆฝ์„ฑ,
  • ๋™์ ์œผ๋กœ ์ƒ์„ฑ๋œ "์ž„์‹œ" ๋งํฌ๋ฅผ ํ†ตํ•œ ์•ก์„ธ์Šค ์ œ์–ด ์ค‘์•™ํ™” ๋ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ณด์•ˆ ์„ค์น˜,
  • ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๊ธฐ๋Šฅ(์ฆ‰, ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๊ฐœ๋ฐœ ํŒ€์ด ๋ˆ„๋ฝ๋œ ๊ธฐ๋Šฅ์„ ๊ธฐ์กด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Œ)

ํ…Œ์ŠคํŠธ

์ด์ œ ์šฐ๋ฆฌ๋Š” ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ถœ์‹œ ์ „ ํ…Œ์ŠคํŠธ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. TestFlight.

๋‹ค์šด๋กœ๋“œ๋ฅผ ์œ„ํ•œ ํ•„์ˆ˜ ์กฐ๊ฑด์€ App Store ์„œ๋ช… ํ”„๋กœํ•„ ์œ ํ˜•๊ณผ ์ƒ์„ฑ๋œ API ํ‚ค์˜ ์กด์žฌ ์—ฌ๋ถ€์ž…๋‹ˆ๋‹ค.

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋‹ค์šด๋กœ๋“œํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Xcode(์ฃผ์ตœ์ž)๋ฅผ ํ†ตํ•ด,
  • ์•Œํˆด์„ ํ†ตํ•ด,
  • ์ด์ „ ๋ฒ„์ „์˜ Xcode(ํ˜„์žฌ Transporter)์šฉ Application Loader๋ฅผ ํ†ตํ•ด.

์ž๋™ ๋‹ค์šด๋กœ๋“œ์—๋Š” ๋‘ ๊ฐ€์ง€ ์ธ์ฆ ๋ฐฉ๋ฒ•์ด ์žˆ๋Š” altool์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

  • ์•ฑ๋ณ„ ๋น„๋ฐ€๋ฒˆํ˜ธ,
  • API ํ‚ค.

API Key๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋‹ค์šด๋กœ๋“œํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•ฉ๋‹ˆ๋‹ค.

API ํ‚ค๋ฅผ ์–ป์œผ๋ ค๋ฉด ๋‹ค์Œ์œผ๋กœ ์ด๋™ํ•˜์„ธ์š”. ๋งํฌ ๊ทธ๋ฆฌ๊ณ  ํ‚ค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. *.p8 ํ˜•์‹์˜ ํ‚ค ์ž์ฒด ์™ธ์—๋„ IssuerID์™€ KeyID๋ผ๋Š” ๋‘ ๊ฐ€์ง€ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

iOS ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์ถ• ๋ฐ ์ œ๊ณต ๊ธฐ๋Šฅ

๊ทธ๋Ÿฐ ๋‹ค์Œ ๋‹ค์šด๋กœ๋“œํ•œ ํ‚ค๋ฅผ ๋นŒ๋“œ ์„œ๋ฒ„๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

mkdir -p ~/.appstoreconnect/private_keys
mv ~/Downloads/AuthKey_${KeyID}.p8 ~/.appstoreconnect/private_keys/

TestFlight์— ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์—…๋กœ๋“œํ•˜๊ธฐ ์ „์— ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

xcrun altool 
--validate-app 
-t ios 
-f $(find "$ProjectDir" -name "*.ipa") 
--apiKey โ€œ$KeyIDโ€ 
--apiIssuer โ€œ$IssuerIDโ€ 

ะ“ะดะต apiKey ะธ apiIssuer API ํ‚ค ์ƒ์„ฑ ํŽ˜์ด์ง€์˜ ํ•„๋“œ ๊ฐ’์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์œผ๋กœ ๊ฒ€์ฆ์— ์„ฑ๊ณตํ•˜๋ฉด ๋‹ค์Œ ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. --upload-app ๋™์ผํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ XNUMX~XNUMX์ผ ์ด๋‚ด์— Apple์—์„œ ํ…Œ์ŠคํŠธํ•œ ํ›„ ์™ธ๋ถ€ ํ…Œ์Šคํ„ฐ์—๊ฒŒ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ์„ค์น˜ ๋งํฌ๊ฐ€ ์ด๋ฉ”์ผ๋กœ ์ „์†ก๋ฉ๋‹ˆ๋‹ค.

altool์„ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋‹ค์šด๋กœ๋“œํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ App-Specific Password๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์•ฑ๋ณ„ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์–ป์œผ๋ ค๋ฉด ๋‹ค์Œ์œผ๋กœ ์ด๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งํฌ ๋ณด์•ˆ ์„น์…˜์—์„œ ์ƒ์„ฑํ•˜์„ธ์š”.

iOS ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์ถ• ๋ฐ ์ œ๊ณต ๊ธฐ๋Šฅ

๋‹ค์Œ์œผ๋กœ ์ด ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ‚ค์ฒด์ธ์— ๋นŒ๋“œ ์„œ๋ฒ„ ๊ธฐ๋ก์„ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Xcode ๋ฒ„์ „ 11๋ถ€ํ„ฐ๋Š” ๋‹ค์Œ ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

xcrun altool --store-password-in-keychain-item "Altool" -u "$DeveloperName" -p $AppPswd

์žฅ์†Œ :

$DeveloperName โ€” Apple ์„œ๋น„์Šค์— ๋กœ๊ทธ์ธํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” iOS ๊ฐœ๋ฐœ์ž ๊ณ„์ •์˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.

$AppPswd โ€” ์ƒ์„ฑ๋œ ์•ฑ๋ณ„ ๋น„๋ฐ€๋ฒˆํ˜ธ.

๋‹ค์Œ์œผ๋กœ asc-provider ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๊ณ  ๋‹ค์Œ ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ€์ ธ์˜ค๊ธฐ ์„ฑ๊ณต์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

xcrun altool --list-providers -u "$DeveloperName" -p "@keychain:Altool"

์šฐ๋ฆฌ๋Š” ์ถœ๋ ฅ์„ ์–ป์Šต๋‹ˆ๋‹ค:

Provider listing:
- Long Name - - Short Name -
XXXXXXX        XXXXXXXXX

๋ณด์‹œ๋‹ค์‹œํ”ผ, ํ•„์ˆ˜ ์งง์€ ์ด๋ฆ„ ๊ฐ’(asc-provider)์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ตฌ์ถ•ํ•  ๋•Œ ์‚ฌ์šฉํ•œ $TeamID ๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค.

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฒ€์ฆํ•˜๊ณ  TestFlight์— ๋กœ๋“œํ•˜๋ ค๋ฉด ๋‹ค์Œ ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

xcrun altool 
--(validate|upload)-app   
-f $(find "$ProjectDir" -name "*.ipa") 
-u "$DeveloperName" 
-p "@keychain:Altool" 

๋งค๊ฐœ๋ณ€์ˆ˜ ๊ฐ’์œผ๋กœ -p ๋‹น์‹ ์€ ๊ฐ€์น˜๋ฅผ ์ทจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค $AppPswd ์•”ํ˜ธํ™”๋˜์ง€ ์•Š์€(๋ช…์‹œ์ ) ํ˜•์‹์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์ด๋ฏธ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด ์„ฑ๋Šฅ ๊ด€์ ์—์„œ ๋ณผ ๋•Œ altool ์ธ์ฆ์—๋Š” API ํ‚ค๋ฅผ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹์Šต๋‹ˆ๋‹ค. Xcode์˜ ๋‹ค๋ฅธ ๋ฒ„์ „์—๋Š” ํŠน์ • ๋ฌธ์ œ(ํ‚ค์ฒด์ธ์ด "๋ณด์ด์ง€ ์•Š์Œ", ์—…๋กœ๋“œ ์ค‘ ์ธ์ฆ ์˜ค๋ฅ˜ ๋“ฑ)๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ ๊ทธ๊ฒŒ ์ „๋ถ€์ž…๋‹ˆ๋‹ค. ๊ด€๋ จ๋œ ๋ชจ๋“  ๋ถ„๋“ค์ด App Store์—์„œ ์„ฑ๊ณต์ ์ธ ๋นŒ๋“œ์™€ ๋ฌธ์ œ ์—†๋Š” ์ถœ์‹œ๋ฅผ ๊ธฐ์›ํ•ฉ๋‹ˆ๋‹ค.

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€