๋ชจ๋ฐ”์ผ CICD ๊ฒฝํ—˜: ๋‹ค์–‘ํ•œ ๋ชจ๋ฐ”์ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ํ•˜๋‚˜์˜ fastlane ํ‘œ์ค€

๋ชจ๋ฐ”์ผ CICD ๊ฒฝํ—˜: ๋‹ค์–‘ํ•œ ๋ชจ๋ฐ”์ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ํ•˜๋‚˜์˜ fastlane ํ‘œ์ค€
fastlane์„ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋ฐ”์ผ ์•ฑ์˜ ์ง€์†์ ์ธ ํ†ตํ•ฉ๊ณผ ์ „๋‹ฌ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๋ชจ๋ฐ”์ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— CI/CD๋ฅผ ๊ตฌํ˜„ํ•œ ๋ฐฉ๋ฒ•, ๊ทธ ๋ชฉํ‘œ์— ๋„๋‹ฌํ•œ ๋ฐฉ๋ฒ• ๋ฐ ์ตœ์ข… ๊ฒฐ๊ณผ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๋„๊ตฌ์˜ ๋„คํŠธ์›Œํฌ์—๋Š” ์ด๋ฏธ ์ถฉ๋ถ„ํ•œ ์ž๋ฃŒ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ์ฒ˜์Œ์—๋Š” ๋ถ€์กฑํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์˜๋„์ ์œผ๋กœ ๋„๊ตฌ์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์„ค๋ช…ํ•˜์ง€ ์•Š๊ณ  ๋‹น์‹œ์— ๊ฐ€์ง€๊ณ  ์žˆ๋˜ ๋‚ด์šฉ๋งŒ ์–ธ๊ธ‰ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด ๊ธฐ์‚ฌ๋Š” ๋‘ ๋ถ€๋ถ„์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

  • ์‚ฌ๋‚ด ๋ชจ๋ฐ”์ผ CI/CD ๋“ฑ์žฅ ๋ฐฐ๊ฒฝ
  • N ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์šฉ CI/CD ์ถœ์‹œ๋ฅผ ์œ„ํ•œ ๊ธฐ์ˆ  ์†”๋ฃจ์…˜

์ฒซ ๋ฒˆ์งธ ๋ถ€๋ถ„์€ ์˜›๋‚ ์— ๋Œ€ํ•œ ํ–ฅ์ˆ˜๋ฅผ ๋” ๋งŽ์ด ๋‹ด๊ณ  ์žˆ๊ณ , ๋‘ ๋ฒˆ์งธ ๋ถ€๋ถ„์€ ์ž์‹ ์—๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝํ—˜์ž…๋‹ˆ๋‹ค.

์—ญ์‚ฌ์ ์œผ๋กœ ๊ทธ๋žฌ๋‹ค.

2015 ์—ฐ๋„

์šฐ๋ฆฌ๋Š” ๋ฐฉ๊ธˆ ๋ชจ๋ฐ”์ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ–ˆ์ง€๋งŒ ์ง€์†์ ์ธ ํ†ตํ•ฉ, DevOps ๋ฐ ๊ธฐํƒ€ ์ตœ์‹  ์œ ํ–‰์— ๋Œ€ํ•ด ์•„๋ฌด๊ฒƒ๋„ ๋ชฐ๋ž์Šต๋‹ˆ๋‹ค. ๊ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์—…๋ฐ์ดํŠธ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์ž์‹ ์˜ ์ปดํ“จํ„ฐ์—์„œ ์ง์ ‘ ์ถœ์‹œํ–ˆ์Šต๋‹ˆ๋‹ค. Android์˜ ๊ฒฝ์šฐ ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ์กฐ๋ฆฝ, ์„œ๋ช… .apk Google ๊ฐœ๋ฐœ์ž ์ฝ˜์†”์— ์—…๋กœ๋“œํ•œ ๋‹ค์Œ iOS์˜ ๊ฒฝ์šฐ Xcode๋ฅผ ํ†ตํ•œ ๋ฐฐํฌ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฆ๊ฑฐ์šด ์ €๋…์„ ๋ณด๋ƒˆ์Šต๋‹ˆ๋‹ค. ์•„์นด์ด๋ธŒ๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜๋ ค๋Š” ์‹œ๋„๋Š” ์ข…์ข… ์˜ค๋ฅ˜๋กœ ๋๋‚˜์„œ ๋‹ค์‹œ ์‹œ๋„ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ€์žฅ ๋ฐœ์ „๋œ ๊ฐœ๋ฐœ์ž๋Š” ํ•œ ๋‹ฌ์— ์—ฌ๋Ÿฌ ๋ฒˆ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ถœ์‹œํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค.

2016 ์—ฐ๋„

์šฐ๋ฆฌ๋Š” ์„ฑ์žฅํ–ˆ๊ณ , ๋ฆด๋ฆฌ์Šค๋ฅผ ์œ„ํ•ด ํ•˜๋ฃจ ์ข…์ผ ๊ฐœ๋ฐœ์ž๋ฅผ ํ•ด๋ฐฉ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ด๋ฏธ ์ƒ๊ฐํ•˜๊ณ  ์žˆ์—ˆ๊ณ  ๋‘ ๋ฒˆ์งธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๋„ ๋“ฑ์žฅํ•˜์—ฌ ์šฐ๋ฆฌ๋ฅผ ์ž๋™ํ™”์ชฝ์œผ๋กœ ๋” ๋ฐ€์–ด ๋ถ™์˜€์Šต๋‹ˆ๋‹ค. ๊ฐ™์€ ํ•ด์— ์šฐ๋ฆฌ๋Š” ์ฒ˜์Œ์œผ๋กœ Jenkins๋ฅผ ์„ค์น˜ํ•˜๊ณ  fastlane์˜ ๋ฌธ์„œ์— ํ‘œ์‹œ๋œ ๊ฒƒ๊ณผ ๋งค์šฐ ์œ ์‚ฌํ•œ ๋ฌด์„œ์šด ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋งŽ์ด ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

$ xcodebuild clean archive -archivePath build/MyApp 
    -scheme MyApp

$ xcodebuild -exportArchive 
                        -exportFormat ipa 
                        -archivePath "build/MyApp.xcarchive" 
                        -exportPath "build/MyApp.ipa" 
                        -exportProvisioningProfile "ProvisioningProfileName"

$ cd /Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/

$ ./altool โ€”upload-app 
-f {abs path to your project}/build/{release scheme}.ipa  
-u "[email protected]" 
-p "PASS_APPLE_ID"

๋ถˆํ–‰ํ•˜๊ฒŒ๋„ ์ง€๊ธˆ๊นŒ์ง€๋Š” ์šฐ๋ฆฌ ๊ฐœ๋ฐœ์ž๋“ค๋งŒ์ด ์ด๋Ÿฌํ•œ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€, ์™œ ์ด ๋์—†๋Š” ํ‚ค ์Šคํƒ์ด ํ•„์š”ํ•œ์ง€ ์•Œ๊ณ  ์žˆ์—ˆ์œผ๋ฉฐ, ๋ฌธ์ œ๊ฐ€ ๋‹ค์‹œ ๋ฐœ์ƒํ•˜๋ฉด ๋กœ๊ทธ ๋ถ„์„์„ ์œ„ํ•ด "๋ฉ‹์ง„ ์ €๋…"์„ ๋ณด๋ƒˆ์Šต๋‹ˆ๋‹ค.

2017 ์—ฐ๋„

์˜ฌํ•ด ์šฐ๋ฆฌ๋Š” fastlane๊ณผ ๊ฐ™์€ ๊ฒƒ์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ๋งŒํผ ์ •๋ณด๊ฐ€ ๋งŽ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์‹œ์ž‘ ๋ฐฉ๋ฒ•, ์‚ฌ์šฉ ๋ฐฉ๋ฒ•. ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋‹น์‹œ ๋„๊ตฌ ์ž์ฒด๋Š” ์—ฌ์ „ํžˆ ์กฐ์žกํ–ˆ์Šต๋‹ˆ๋‹ค. ๋Š์ž„์—†๋Š” ์˜ค๋ฅ˜๋Š” ์šฐ๋ฆฌ๋ฅผ ์‹ค๋ง์‹œ์ผฐ๊ณ  ๊ทธ๋“ค์ด ์•ฝ์†ํ•œ ๋งˆ๋ฒ• ๊ฐ™์€ ์ž๋™ํ™”๋ฅผ ๋ฏฟ๊ธฐ ์–ด๋ ค์› ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ fastlane ์ฝ”์–ด์— ํฌํ•จ๋œ ์ฃผ์š” ์œ ํ‹ธ๋ฆฌํ‹ฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. gym ะธ pilot, ์šฐ๋ฆฌ๋Š” ๊ทธ๊ฒƒ์„ ์‹œ์ž‘ํ•  ์ˆ˜์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ์˜ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์•ฝ๊ฐ„ ๊ฐœ์„ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

$ fastlane gym  โ€”-workspace "Example.xcworkspace" 
                --scheme "AppName" 
                โ€”-buildlog_path "/tmp" 
                -โ€”clean

ํ•„์š”ํ•œ ๋ชจ๋“  ๋งค๊ฐœ ๋ณ€์ˆ˜๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ์„ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. xcodebuild, ๋‹ค์Œ์„ ํ‘œ์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. gym ์–ด๋””์— ๋ฌด์—‡์ด ์žˆ๋Š”์ง€ ๋…๋ฆฝ์ ์œผ๋กœ ์ดํ•ดํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ข€ ๋” ์„ธ๋ถ€์ ์ธ ์กฐ์ •์„ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ‚ค๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. xcodebuild, ํ‚ค ์ด๋ฆ„๋งŒ ๋” ๋ช…ํ™•ํ•ด์กŒ์Šต๋‹ˆ๋‹ค.

์ด๋ฒˆ์—๋Š” gym๊ณผ ๋‚ด์žฅ๋œ xcpretty ํฌ๋งทํ„ฐ ๋•๋ถ„์— ๋นŒ๋“œ ๋กœ๊ทธ๋ฅผ ํ›จ์”ฌ ๋” ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๊นจ์ง„ ์–ด์…ˆ๋ธ”๋ฆฌ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๋ฐ ๋“œ๋Š” ์‹œ๊ฐ„์ด ์ ˆ์•ฝ๋˜๊ธฐ ์‹œ์ž‘ํ–ˆ์œผ๋ฉฐ ๋•Œ๋กœ๋Š” ๋ฆด๋ฆฌ์Šค ํŒ€์ด ์Šค์Šค๋กœ ์•Œ์•„๋‚ผ ์ˆ˜๋„ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋ถˆํ–‰ํ•˜๊ฒŒ๋„ ์กฐ๋ฆฝ ์†๋„ ์ธก์ • xcodebuild ะธ gym ์šฐ๋ฆฌ๋Š” ๊ทธ๋ ‡๊ฒŒ ํ•˜์ง€ ์•Š์•˜์ง€๋งŒ ๋ฌธ์„œ๋ฅผ ์‹ ๋ขฐํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์†๋„๊ฐ€ ์ตœ๋Œ€ 30% ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ๋‹จ์ผ ํ”„๋กœ์„ธ์Šค

2018๋…„๊ณผ ํ˜„์žฌ

2018๋…„์—๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ตฌ์ถ•ํ•˜๊ณ  ์ถœ์‹œํ•˜๋Š” ํ”„๋กœ์„ธ์Šค๊ฐ€ Jenkins๋กœ ์™„์ „ํžˆ ์ด์ „๋˜์—ˆ๊ณ  ๊ฐœ๋ฐœ์ž๋Š” ๋จธ์‹ ์—์„œ ๋ฆด๋ฆฌ์Šค๋ฅผ ์ค‘๋‹จํ–ˆ์œผ๋ฉฐ ๋ฆด๋ฆฌ์Šค ํŒ€๋งŒ์ด ๋ฆด๋ฆฌ์Šค ๊ถŒํ•œ์„ ๊ฐ€์กŒ์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ์ด๋ฏธ ํ…Œ์ŠคํŠธ ๋ฐ ์ •์  ๋ถ„์„ ์‹คํ–‰์„ ๊ฐœ์„ ํ•˜๊ณ  ์‹ถ์—ˆ๊ณ  ์Šคํฌ๋ฆฝํŠธ๋„ ๊ณ„์†ํ•ด์„œ ์„ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๊ณ  ๋ณ€ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹น์‹œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด 10๊ฐœ ์ •๋„ ์žˆ์—ˆ๋Š”๋ฐ, ํ”Œ๋žซํผ์ด 20๊ฐœ ์žˆ๋‹ค๋Š” ์ ์„ ๊ณ ๋ คํ•˜๋ฉด '์‚ด์•„์žˆ๋Š”' ์Šคํฌ๋ฆฝํŠธ๊ฐ€ XNUMX๊ฐœ ์ •๋„ ๋œ๋‹ค.

์Šคํฌ๋ฆฝํŠธ์— ์ƒˆ๋กœ์šด ๋‹จ๊ณ„๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์„ ๋•Œ๋งˆ๋‹ค ๋ชจ๋“  ์…ธ ์Šคํฌ๋ฆฝํŠธ์— ํ•ด๋‹น ๋ถ€๋ถ„์„ ๋ณต์‚ฌํ•˜์—ฌ ๋ถ™์—ฌ๋„ฃ์–ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ์šฐ๋ฆฌ๋Š” ์ข€ ๋” ์‹ ์ค‘ํ•˜๊ฒŒ ์ž‘์—…ํ•  ์ˆ˜ ์žˆ์—ˆ์ง€๋งŒ ๊ทธ๋Ÿฌํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ์ข…์ข… ์˜คํƒ€๋กœ ๋๋‚˜์„œ ๋ฆด๋ฆฌ์Šค ํŒ€์ด ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  ์–ด๋–ค ๋˜‘๋˜‘ํ•œ ์‚ฌ๋žŒ์ด ์ด ๋ช…๋ น์„ ์ถ”๊ฐ€ํ–ˆ๋Š”์ง€, ์‹ค์ œ๋กœ ๋ฌด์—‡์„ ํ•˜๋Š”์ง€ ์•Œ์•„๋‚ด๋Š” ๋ฐ ์ €๋… ์‹œ๊ฐ„์ด ๊ฑธ๋ ธ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ํ•œ ํ”Œ๋žซํผ์˜ ์–ด์…ˆ๋ธ”๋ฆฌ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ ์–ด๋„ ๋‹ค์†Œ ์œ ์‚ฌํ•˜๋‹ค๊ณ  ๋งํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋“ค์€ ํ™•์‹คํžˆ ๊ฐ™์€ ์ผ์„ ํ–ˆ์ง€๋งŒ.

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

2018๋…„ ์—ฌ๋ฆ„, ์šฐ๋ฆฌ๋Š” ๋‹ค์‹œ ํ•œ ๋ฒˆ ์—ฌ์ „ํžˆ ๋ฐœ์ „ ์ค‘์ธ fastlane์„ ๋ฐ”๋ผ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

์ž‘์—… #1: ๋ชจ๋“  ์Šคํฌ๋ฆฝํŠธ ๋‹จ๊ณ„๋ฅผ ์š”์•ฝํ•˜๊ณ  Fastfile์— ๋‹ค์‹œ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

์‹œ์ž‘ํ–ˆ์„ ๋•Œ ์Šคํฌ๋ฆฝํŠธ๋Š” Jenkins์˜ ํ•˜๋‚˜์˜ ์‰˜ ์Šคํฌ๋ฆฝํŠธ์— ์žˆ๋Š” ๋ชจ๋“  ๋‹จ๊ณ„์™€ ๋ชฉ๋ฐœ๋กœ ๊ตฌ์„ฑ๋œ ์‹ ๋ฐœ์ฒ˜๋Ÿผ ๋ณด์˜€์Šต๋‹ˆ๋‹ค. ์•„์ง ํŒŒ์ดํ”„๋ผ์ธ๊ณผ ๋‹จ๊ณ„๋ณ„ ๋ถ„ํ• ๋กœ ์ „ํ™˜ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ํ˜„์žฌ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒƒ์„ ์‚ดํŽด๋ณด๊ณ  CI/CD ์„ค๋ช…์— ๋งž๋Š” 4๊ฐ€์ง€ ๋‹จ๊ณ„๋ฅผ ์‹๋ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ๋นŒ๋“œ - ์ข…์†์„ฑ ์„ค์น˜, ์•„์นด์ด๋ธŒ ์กฐ๋ฆฝ,
  • ํ…Œ์ŠคํŠธ โ€” ๊ฐœ๋ฐœ์ž ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์‹คํ–‰, ์ ์šฉ ๋ฒ”์œ„ ๊ณ„์‚ฐ,
  • sonar - ๋ชจ๋“  ๋ฆฐํ„ฐ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  SonarQube์— ๋ณด๊ณ ์„œ๋ฅผ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
  • ๋ฐฐํฌ โ€” ์•„ํ‹ฐํŒฉํŠธ๋ฅผ ์•ŒํŒŒ(TestFlight)๋กœ ๋ณด๋ƒ…๋‹ˆ๋‹ค.

์„ธ๋ถ€ ์‚ฌํ•ญ์„ ๋‹ค๋ฃจ์ง€ ์•Š๊ณ  ์ž‘์—…์— ์‚ฌ์šฉ๋˜๋Š” ํ‚ค๋ฅผ ์ƒ๋žตํ•˜๋ฉด ๋‹ค์Œ Fastfile์„ ์–ป๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

default_platform(:ios)

platform :ios do
  before_all do
    unlock
  end

  desc "Build stage"
  lane :build do
    match
    prepare_build
    gym
  end

  desc "Prepare build stage: carthage and cocoapods"
  lane :prepare_build do
    pathCartfile = ""
    Dir.chdir("..") do
      pathCartfile = File.join(Dir.pwd, "/Cartfile")
    end
    if File.exist?(pathCartfile)
      carthage
    end
    pathPodfile = ""
    Dir.chdir("..") do
      pathPodfile = File.join(Dir.pwd, "/Podfile")
    end
    if File.exist?(pathPodfile)
      cocoapods
    end
  end

  desc "Test stage"
  lane :test do
    scan
    xcov
  end

  desc "Sonar stage (after run test!)"
  lane :run_sonar do
    slather
    lizard
    swiftlint
    sonar
  end

  desc "Deploy to testflight stage"
  lane :deploy do
    pilot
  end

  desc "Unlock keychain"
  private_lane :unlock do
    pass = ENV['KEYCHAIN_PASSWORD']
    unlock_keychain(
      password: pass
    )
  end
end

์‹ค์ œ๋กœ ์šฐ๋ฆฌ์˜ ์ฒซ ๋ฒˆ์งธ Fastfile์€ ์—ฌ์ „ํžˆ โ€‹โ€‹ํ•„์š”ํ•œ ๋ชฉ๋ฐœ๊ณผ ๋Œ€์ฒดํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ์ˆ˜๋ฅผ ๊ณ ๋ คํ•  ๋•Œ ๊ดด๋ฌผ ๊ฐ™์€ ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค.

lane :build do
carthage(
  command: "update",
  use_binaries: false,
  platform: "ios",
  cache_builds: true)
cocoapods(
  clean: true,
    podfile: "./Podfile",
    use_bundle_exec: false)

gym(
  workspace: "MyApp.xcworkspace",
  configuration: "Release",
  scheme: "MyApp",
  clean: true,
  output_directory: "/build",
  output_name: "my-app.ipa")
end 

lane :deploy do
 pilot(
  username: "[email protected]",
  app_identifier: "com.example.app",
  dev_portal_team_id: "TEAM_ID_NUMBER_DEV",
  team_id: "ITS_TEAM_ID")
end

์œ„์˜ ์˜ˆ์—์„œ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜ ์ค‘ ์ผ๋ถ€๋งŒ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋นŒ๋“œ ๋งค๊ฐœ๋ณ€์ˆ˜(์Šคํ‚ค๋งˆ, ๊ตฌ์„ฑ, ํ”„๋กœ๋น„์ €๋‹ ํ”„๋กœํ•„ ์ด๋ฆ„) ๋ฐ ๋ฐฐํฌ ๋งค๊ฐœ๋ณ€์ˆ˜(๊ฐœ๋ฐœ์ž ๊ณ„์ •์˜ Apple ID, ๋น„๋ฐ€๋ฒˆํ˜ธ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ID ๋“ฑ)์ž…๋‹ˆ๋‹ค. ์—. ์ฒซ ๋ฒˆ์งธ ๊ทผ์‚ฌ์น˜๋กœ ์šฐ๋ฆฌ๋Š” ์ด๋Ÿฌํ•œ ๋ชจ๋“  ํ‚ค๋ฅผ ํŠน์ˆ˜ ํŒŒ์ผ์— ๋„ฃ์Šต๋‹ˆ๋‹ค. Gymfile, Matchfile ะธ Appfile.

์ด์ œ Jenkins์—์„œ๋Š” ๋ณด๊ธฐ๋ฅผ ํ๋ฆฌ๊ฒŒ ํ•˜์ง€ ์•Š๊ณ  ๋ˆˆ์œผ๋กœ ์‰ฝ๊ฒŒ ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ์งง์€ ๋ช…๋ น์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

# fastlane ios <lane_name>

$ fastlane ios build
$ fastlane ios test
$ fastlane ios run_sonar
$ fastlane ios deploy

๋งŒ์„ธ, ์šฐ๋ฆฌ๋Š” ํ›Œ๋ฅญํ•ด์š”

๋ฌด์—‡์„ ์–ป์—ˆ๋‚˜์š”? ๋ชจ๋“  ๋‹จ๊ณ„์— ๋Œ€ํ•œ ๋ช…๋ น์„ ์ง€์›๋‹ˆ๋‹ค. fastlane ํŒŒ์ผ์— ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌ๋œ ์Šคํฌ๋ฆฝํŠธ ์ •๋ฆฌ. ๊ธฐ๋ปํ•˜๋ฉด์„œ ์šฐ๋ฆฌ๋Š” ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ๋‹ฌ๋ ค๊ฐ€์„œ ์ €์žฅ์†Œ์— ํ•„์š”ํ•œ ๋ชจ๋“  ๊ฒƒ์„ ์ถ”๊ฐ€ํ•ด ๋‹ฌ๋ผ๊ณ  ์š”์ฒญํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ๋Š” ๊ฐ™์€ ์–ด๋ ค์›€์— ์ง๋ฉดํ•˜๊ฒŒ ๋  ๊ฒƒ์ด๋ผ๋Š” ๊ฒƒ์„ ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด์„œ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ์‹์œผ๋กœ๋“  ์ž์‹ ์˜ ์‚ถ์„ ์‹œ์ž‘ํ•˜๋Š” 20๊ฐœ์˜ ์–ด์…ˆ๋ธ”๋ฆฌ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์—ฌ์ „ํžˆ ๋‚จ์•„ ์žˆ์„ ๊ฒƒ์ด๊ณ , ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋กœ ์ด๋™ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํŽธ์ง‘ํ•˜๊ธฐ๊ฐ€ ๋” ์–ด๋ ค์šธ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๊ฑฐ๊ธฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ผ๋ฐ˜์ ์œผ๋กœ ์ด๋Ÿฐ ์‹์œผ๋กœ ์šฐ๋ฆฌ์˜ ๊ณ ํ†ต์„ ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋ฐ”์ผ CICD ๊ฒฝํ—˜: ๋‹ค์–‘ํ•œ ๋ชจ๋ฐ”์ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ํ•˜๋‚˜์˜ fastlane ํ‘œ์ค€

์ž‘์—… #2: N๊ฐœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•œ ๋‹จ์ผ Fastfile ๊ฐ€์ ธ์˜ค๊ธฐ

์ด์ œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ์ด ๊ทธ๋ฆฌ ์–ด๋ ต์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜๊ณ  ์ง„ํ–‰ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋„ค, ์‚ฌ์‹ค ๊ทธ๋ ‡๊ฒŒ ํ•ด์„œ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋์–ด์š”. ๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ๊ฐ€ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•œ ์ˆœ๊ฐ„ ์šฐ๋ฆฌ๋Š” fastlane ์ž์ฒด์— ๋Œ€ํ•œ ์ „๋ฌธ ์ง€์‹๋„, fastlane์ด ์ž‘์„ฑ๋œ Ruby์— ๋Œ€ํ•œ ์ „๋ฌธ ์ง€์‹๋„, ๋„คํŠธ์›Œํฌ์— ๋Œ€ํ•œ ์œ ์šฉํ•œ ์˜ˆ์ œ๋„ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹น์‹œ fastlane์— ๋Œ€ํ•ด ์“ด ๋ชจ๋“  ์‚ฌ๋žŒ์€ ํ•˜๋‚˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•œ ์˜ˆ์ œ๋กœ ์ œํ•œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž ํ•œ ๋ช….

Fastlane์€ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์šฐ๋ฆฌ๋Š” ์ด๋ฏธ ํ‚ค์ฒด์ธ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์„ค์ •ํ•˜์—ฌ ์ด๋ฅผ ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค.

ENV['KEYCHAIN_PASSWORD']

์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ดํŽด๋ณธ ํ›„ ๊ณตํ†ต ๋ถ€๋ถ„์„ ์‹๋ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค.

#for build, test and deploy
APPLICATION_SCHEME_NAME=appScheme
APPLICATION_PROJECT_NAME=app.xcodeproj
APPLICATION_WORKSPACE_NAME=app.xcworkspace
APPLICATION_NAME=appName

OUTPUT_IPA_NAME=appName.ipa

#app info
APP_BUNDLE_IDENTIFIER=com.example.appName
[email protected]
TEAM_ID=ABCD1234
FASTLANE_ITC_TEAM_ID=123456789

์ด์ œ fastlane ํŒŒ์ผ์—์„œ ์ด๋Ÿฌํ•œ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์‹œ์ž‘ํ•˜๋ ค๋ฉด ํ•ด๋‹น ํ‚ค๋ฅผ ๊ฑฐ๊ธฐ์— ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋‚ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. Fastlane์€ ์ด์— ๋Œ€ํ•œ ์†”๋ฃจ์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค: dotenv๋ฅผ ํ†ตํ•ด ๋ณ€์ˆ˜ ๋กœ๋“œํ•˜๊ธฐ. ๋ฌธ์„œ์—๋Š” ๋‹ค์–‘ํ•œ ๋ชฉ์ ์œผ๋กœ ํ‚ค๋ฅผ ๋กœ๋“œํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•œ ๊ฒฝ์šฐ fastlane ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์—ฌ๋Ÿฌ ๊ตฌ์„ฑ ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๋ผ๊ณ  ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค. .env, .env.default, .env.development.

๊ทธ๋ฆฌ๊ณ  ์šฐ๋ฆฌ๋Š” ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์กฐ๊ธˆ ๋‹ค๋ฅด๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. fastlane ์Šคํฌ๋ฆฝํŠธ์™€ ๋ฉ”ํƒ€ ์ •๋ณด๊ฐ€ ์•„๋‹Œ ํŒŒ์ผ์— ์žˆ๋Š” ์ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ณ ์œ  ํ‚ค๋ฅผ ๊ฐœ๋ฐœ์ž ์ €์žฅ์†Œ์— ์ €์žฅํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. .env.appName.

๊ทธ๋“ค ์ž์‹  Fastfile, Appfile, Matchfile ะธ Gymfile, ๋ณ„๋„์˜ ์ €์žฅ์†Œ์— ์ˆจ๊ฒผ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์„œ๋น„์Šค์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ‚ค๊ฐ€ ํฌํ•จ๋œ ์ถ”๊ฐ€ ํŒŒ์ผ์ด ์ˆจ๊ฒจ์ ธ ์žˆ์Šต๋‹ˆ๋‹ค. .env.
์˜ˆ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค ์—ฌ๊ธฐ์—.

๋ชจ๋ฐ”์ผ CICD ๊ฒฝํ—˜: ๋‹ค์–‘ํ•œ ๋ชจ๋ฐ”์ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ํ•˜๋‚˜์˜ fastlane ํ‘œ์ค€

CI์—์„œ๋Š” ํ˜ธ์ถœ์ด ํฌ๊ฒŒ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜์œผ๋ฉฐ ํŠน์ • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•œ ๊ตฌ์„ฑ ํ‚ค๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

# fastlane ios <lane_name> --env appName

$ fastlane ios build --env appName
$ fastlane ios test --env appName
$ fastlane ios run_sonar --env appName
$ fastlane ios deploy --env appName

๋ช…๋ น์„ ์‹คํ–‰ํ•˜๊ธฐ ์ „์— ์Šคํฌ๋ฆฝํŠธ๋กœ ์ €์žฅ์†Œ๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋‹ค์ง€ ์ข‹์•„ ๋ณด์ด์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.

git clone [email protected]/FastlaneCICD.git fastlane_temp

cp ./fastlane_temp/fastlane/* ./fastlane/
cp ./fastlane_temp/fastlane/.env fastlane/.env

Fastlane์—๋Š” ๋‹ค์Œ์„ ํ†ตํ•ด Fastfile์„ ๋‹ค์šด๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š” ์†”๋ฃจ์…˜์ด ์žˆ์ง€๋งŒ ์ง€๊ธˆ์€ ์ด ์†”๋ฃจ์…˜์„ ๊ทธ๋Œ€๋กœ ๋‘ก๋‹ˆ๋‹ค. ๋™์ž‘ import_from_git, ๊ทธ๋Ÿฌ๋‚˜ Fastfile์—์„œ๋งŒ ์ž‘๋™ํ•˜๊ณ  ๋‹ค๋ฅธ ํŒŒ์ผ์—์„œ๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. "์ •๋ง ์•„๋ฆ„๋‹ต์Šต๋‹ˆ๋‹ค"๋ฅผ ์›ํ•œ๋‹ค๋ฉด ์ง์ ‘ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค action.

Android ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐ ReactNative์— ๋Œ€ํ•ด ์œ ์‚ฌํ•œ ์„ธํŠธ๊ฐ€ ๋งŒ๋“ค์–ด์กŒ์œผ๋ฉฐ ํŒŒ์ผ์€ ๋™์ผํ•œ ์ €์žฅ์†Œ์— ์žˆ์ง€๋งŒ ๋‹ค๋ฅธ ๋ถ„๊ธฐ์— ์žˆ์Šต๋‹ˆ๋‹ค. iOS, android ะธ react_native.

๋ฆด๋ฆฌ์Šค ํŒ€์ด ์ƒˆ๋กœ์šด ๋‹จ๊ณ„๋ฅผ ์ถ”๊ฐ€ํ•˜๋ ค๊ณ  ํ•  ๋•Œ ์Šคํฌ๋ฆฝํŠธ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ git์˜ MR์„ ํ†ตํ•ด ๊ธฐ๋ก๋˜๋ฏ€๋กœ ๋” ์ด์ƒ ์†์ƒ๋œ ์Šคํฌ๋ฆฝํŠธ์˜ ์›์ธ์„ ์ฐพ์„ ํ•„์š”๊ฐ€ ์—†์œผ๋ฉฐ ์ผ๋ฐ˜์ ์œผ๋กœ ์ด์ œ ์ด๋ฅผ ๊นจ๋ ค๊ณ  ์‹œ๋„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ๊ทธ๊ฒŒ ํ™•์‹คํ•ด

์ด์ „์—๋Š” ๋ชจ๋“  ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์œ ์ง€ ๊ด€๋ฆฌํ•˜๊ณ , ์—…๋ฐ์ดํŠธํ•˜๊ณ , ์—…๋ฐ์ดํŠธ์˜ ๋ชจ๋“  ๊ฒฐ๊ณผ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์„ ๋ณด๋ƒˆ์Šต๋‹ˆ๋‹ค. ๋ฆด๋ฆฌ์Šค ์˜ค๋ฅ˜์™€ ๋‹ค์šดํƒ€์ž„์˜ ์›์ธ์ด ๋’ค์ฃฝ๋ฐ•์ฃฝ๋œ ์‰˜ ์Šคํฌ๋ฆฝํŠธ์—์„œ ์ถ”์ ํ•˜๊ธฐ๊ฐ€ ๋„ˆ๋ฌด ์–ด๋ ค์šด ๋‹จ์ˆœํ•œ ์˜คํƒ€์˜€์„ ๋•Œ ๋งค์šฐ ์‹ค๋ง์Šค๋Ÿฌ์› ์Šต๋‹ˆ๋‹ค. ์ด์ œ ์ด๋Ÿฌํ•œ ์˜ค๋ฅ˜๊ฐ€ ์ตœ์†Œํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ๋ชจ๋“  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋™์‹œ์— ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ƒˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํ”„๋กœ์„ธ์Šค์— ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐ 15๋ถ„์ด ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค. CI์— ํ…œํ”Œ๋ฆฟ ํŒŒ์ดํ”„๋ผ์ธ์„ ์„ค์ •ํ•˜๊ณ  ๊ฐœ๋ฐœ์ž ์ €์žฅ์†Œ์— ํ‚ค๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

Android์šฉ Fastfile๊ณผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ช…์— ๋Œ€ํ•œ ์š”์ ์€ ์—ฌ์ „ํžˆ โ€‹โ€‹์„ค๋ช…๋˜์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ธฐ์‚ฌ๊ฐ€ ํฅ๋ฏธ๋กœ์šฐ๋ฉด ๊ณ„์† ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋Œ“๊ธ€์ด๋‚˜ ํ…”๋ ˆ๊ทธ๋žจ์—์„œ "์ด ๋ฌธ์ œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?"์— ๋Œ€ํ•œ ์งˆ๋ฌธ์ด๋‚˜ ์ œ์•ˆ์„ ๋ณด๊ณ  ๊ธฐ์˜๊ฒŒ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋ฐ”์‹œํ‚ค๋กœ๋ฐ”.

์ถœ์ฒ˜ : habr.com

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