brunch

매거진 Delightroom

You can make anything
by writing

C.S.Lewis

by Chris Lee Nov 29. 2018

Fastlane으로 손쉬운 다중 빌드 iOS 앱 배포

하나의 앱이 무료와 유료버전으로 나누어진 경우, 같은 코드 베이스에서 무료 버전과 프로(유료) 버전 두 가지의 빌드를 만들게 된다. 두 빌드를 별도의 코드 베이스로 관리할 수도 있지만, 동일한 수정사항을 두 군데에 적용해야 하는 등 애로사항이 많다.


한때 알라 미는 무료 버전, 유료버전 그리고 일본 앱스토어 버전이 존재했다. 빌드가 3가지가 되면서 효율적인 빌드 및 배포 방법에 대한 고민을 하였고, 그 해결책은 Fastlane이었다. 이 글에서는 Fastlane으로 다중 빌드 배포 프로세스를 손쉽게 만드는 방법을 소개하려 한다.


Fastlane이란?


Fastlane은 iOS와 Android 앱을 빌드, 배포하는 귀찮고도 번거로운 프로세스를 커맨드 라인 명령행 한 줄로 가능하게 해주는 대단히 유용한 오픈소스 프로젝트이다.


iOS 앱스토어에 업로드하는 예를 들어보자. Fastfile (Fastlane 명령어 실행 시 참조하는 ruby 언어로 쓰인 명세)에 다음과 같이 release라는 lane을 만든다.


lane :release do
  sync_code_signing(type: "appstore")  # see code signing guide for more information
  build_app(scheme: "MyApp")
  upload_to_app_store                  # upload your app to App Store Connect
  slack(message: "Successfully uploaded a new App Store build")
end

그 후, 터미널에서

fastlane release 

를 실행하면, 짜잔! 코드사이닝, 빌드 그리고 업로드까지 그 자리에서 완료된다. Fastlane의 더 자세한 사용 방법과 기능 목록은 위 링크의 공식 문서를 참조 바란다.


빌드 변환 스크립트 만들기


가장 먼저 해야 할 일은, 프리 버전 코드-> 프로버전 코드의 자동화이다. 이 부분은 앱에 따라서, 빌드 분리 구현 방식에 따라서 달라질 수 있다.
알라미의 경우 프로버전과 프리 버전의 기능적 구분은 코드상에서 bundle_identifier를 체크하여 런타임에서 이루어진다. 그러므로 코드상에 조작을 가해줄 필요는 없고 bundle_identifier 만 교체해 주면 된다. 추가로 앱 이름(bundle_name)을 바꿔주는 것도 잊지 말아야 한다.

다음 shell script가 그 작업을 해준다.


#!/bin/sh
# Replace bundle identifier to pro version identifier
sed -i '.bak' s/droom.sleepIfUCanFree/droom.sleepIfUCan/g
../Alarmy.xcodeproj/project.pbxproj
sed -i '.bak' s/droomalarmy/droomalarmypro/g ../Alarmy/Info.plist
# Replace bundle names
for dir in ../Alarmy/Resources/Localizable/*
do
    file="${dir}/InfoPlist.strings"
    if [ -e "${file}" ]
    then
        echo "${file}"
        sed -i '.bak' s/\"Alarmy\"/\"Alarmy\ Pro\"/g "${file}"
        sed -i '.bak' s/\"알라미\"/\"알라미\ 프로\"/g "${file}"
    fi
done
# Replace Resources
cp -fR ../Scripts/pro_resources/* ../


여기서 자세한 shell script의 설명은 하지 않겠다. sed 명령어가 많이 등장하는데 모르는 분들을 위해 간단히 설명하면, 문자열에서 패턴을 찾아 치환이나 삭제를 해주는 터미널 유틸리티이다. 우리의 경우 프로젝트 파일들에서 'droom.sleepIfUCanFree’라는 문자열을 'droom.sleepIfUCan’로 치환하는 데 사용한다.


lane 만들기


다음으로는 release 버전을 업로드하는 lane을 만들어준다.
Fastlane 공식 문서에 따라서, Fastlane을 설치하고 "init" 하면 Fastfile 이 만들어지는데, 그 파일에다가 lane을 추가하면 된다. 이 부분 역시 앱마다 어떤 옵션으로 릴리즈를 해야 하는지가 다를 테니, 참고만 하기 바란다. 알라미의 경우 match를 이용해서 certificates들을 받아오고 gym을 이용해 빌드, deliver를 이용해 업로드한다. 경우에 따라선, snapshot 으로 스크린숏을 자동으로 찍어 올리거나, slack을 이용해 완료 시 Slack 메시지를 받을 수도 있을 것이다.

desc "Deploy a new version to the App Store"
lane :release do
    match(type: "appstore", git_url: "https://github.com/******")
    gym(scheme: "Alarmy") # Build your app - more options available
    deliver(force: false)
end


dotenv 설정하기


Fastlane은 dotenv를 통해 환경변수를 설정할 수 있다. 우리는 이 환경변수들을 Fastlane에 프리와 프로 중 어떤 빌드를 할 것인지 알려주는 데 사용할 것이다.


다음 내용으로. env.free라는 파일을 fastlane 폴더 내에 만들어준다.

APP_EDITION=free 
APP_IDENTIFIER=droom.sleepIfUCanFree
METADATA_DIR_NAME=metadata 

APP_EDITION 은 ‘free’, 'pro’의 값을 가지고 어떤 버전인지 구분하기 위해 사용한다.

APP_IDENTIFIER는 fastlane 사용 시 빌드하고 업로드하는 app_identifier로써 사용되는 값이다.

METADATA_DIR_NAME 은 deliver를 통해 앱스토어에 각종 메타데이터 (앱 이름, 설명, 릴리즈 노트, 저작권 정보 등등) 업로드할 때, 어떤 디렉터리에 저장된 메타 테이터를 쓸 것인지 지정해 주기 위해 넣어주었다.


이 상태에서 -efree라는 옵션을 붙여서 Fastlane 명령을 실행하게 되면 (예: fastlane release -efree) Fastlane은 위 세 환경변수들이 설정된 채로 실행된다. 나중에 Fastfile 혹은 다른 설정 파일에서 ENV ['APP_EDITION'] (값은 당연하게도 free가 들어있겠다)과 같이 환경변수를 사용할 수 있다.


같은 형식으로 프로버전 업로드를 위한. env.pro 파일을 fastlane 폴더 내에 만들어준다.

APP_EDITION=pro
APP_IDENTIFIER=droom.sleepIfUCan
METADATA_DIR_NAME=metadata_pro 

미묘하게 다른 APP_IDENTIFIER와 METADATA_DIR_NAME을 가짐을 확인할 수 있다.


Environment variable 활용하기


드디어 앞서 입력된 environment variable들을 빌드 선택에 활용할 때가 왔다.


Appfile - 앱 관련 변수 지정


Appfile은 Fastlane에서 전반적으로 사용되는 앱 관련 유용한 정보(bundle_identifier나 apple_id)들을 담아두는 파일이다. fastlane 폴더 내에 Appfile을 다음과 같이 작성한다.


app_identifier "droom.sleepIfUCan"
apple_id "dev@delightroom.com"
team_id "***"

(team_id 에는 자신의(혹은 팀의) 애플 개발자 계정에 부여된 팀 아이디를 적어준다)


우리는 여기서 app_identifier와 apple_id를 environment variable들로부터 입력받아야 한다. environment variable을 쓰고자 하는 곳에 ENV ['변수 이름']를 넣어준다. Appfile 은 다음과 같이 바뀌게 된다.


app_identifier ENV['APP_IDENTIFIER']
apple_id ENV['FASTLANE_USER']
team_id "***" 


앞으로 fastlane에서 app_identifier나 apple_id가 사용될 때, 환경변수로 넣어준 값이 사용되게 된다.


Deliverfile - 메타데이터 관련 행동 및 변수 지정


다음으로 Deliverfile을 작성한다. 이 파일은 앱스토어에 업로드되는 메타데이터의 값과 종류, 기타 업로드 행동을 정의하는 데 사용된다.


screenshots_path "./fastlane/screenshots"
metadata_path "./fastlane/" + ENV['METADATA_DIR_NAME'] 


여기서 ENV['METADATA_DIR_NAME'] 은 앞서 dotenv 파일에서 지정해 준 대로, free의 경우 metadata, 그리고 pro버전의 경우 metadata_pro 폴더를 가리키게 된다. 프로버전을 위한 메타데이터는 metadata_pro 에 넣어두고 쓰면 된다.


Fastfile


마지막으로 모든 마법이 일어나는 Fastfile을 손댈 차례이다.

맨 처음에 정의해주었던, 릴리즈를 위한 lane은 다음과 같다.


desc "Deploy a new version to the App Store"
lane :release do
    match(type: "appstore", git_url: "https://github.com/******") 
    gym(scheme: "Alarmy") # Build your app - more options available
    deliver(force: false)
end


bundle_identifier는 Appfile에 의해, metadata_path는 Deliverfile에 의해 이미 free버전에서 pro버전으로의 변화가 적용되었다. 이들은 fastlane에서 사용하는 내부적인 변수들이다. release lane에서는 변수가 아니라 실제 빌드가 되는 프로젝트 파일에 변화를 준다. 이를 위해 아까 만들어 놓았던 shell script를 사용한다. 만약 다른 방식으로 변화를 주었다면 shell script를 쓰는 부분을 해당 방식으로 교체하면 된다. gym을 실행하기 직전에, APP_EDITION이 pro인지, okoshiteme인지에 따라 적절한 shell script를 실행하는 로직을 추가한다.


desc "Deploy a new version to the App Store"
lane :release do
    match(type: "appstore", git_url: "https://github.com/******")    
    if ENV['APP_EDITION'] == 'pro'
      # pro 일때
      puts 'Convert to Pro version'
      sh('../Scripts/pro.sh')
    elsif ENV['APP_EDITION'] == 'okoshiteme'
      # okoshiteme 일때
      puts 'Convert to Okoshiteme version'
      sh('../Scripts/okoshitemize.sh')
    end    
    gym(scheme: "Alarmy") # Build your app - more options available
    deliver(force: false)
end


다른 여러 가지 빌드 종류에도 연속적인 if문을 써서 적용할 수 있다. 위 예시에서는 pro 이외에도 okoshiteme라는 제 3의 빌드를 추가로 커버한다.

그 후 gym을 통해 실제 빌드를 진행하고, deliver를 통해 업로드해준다.


이제 필요한 모든 것을 해준 듯 하지만 그렇지 않다. 이대로 fastlane의 작업이 끝나고 나면 프로버전으로 바꾸기 pro.sh 스크립트가 돌고 난 흔적이 그대로 남아있게 된다.


after_all do |lane|
    if ENV['APP_EDITION'] == 'pro' || ENV['APP_EDITION'] == 'okoshiteme'
      puts 'Discard all local changes'
      sh('git reset --hard && git clean ../ -f')
    end
    slack(
      message: "Successfully deployed new App Update."
    )
end


after_all hook은 어떤 lane이든 성공리에 끝나면 블록 내의 코드를 실행시켜준다. 만약 프리가 아닌 다른 빌드를 진행하였다면 git 명령어를 사용하여 모든 로컬 변화들을 취소해준다. (주의할 점은 저장되지 않은 다른 변화들 역시 취소해 버릴 수 있기 때문에, fastlane사용 전에 마지막 변화를 잘 commit 해 두어야 한다.)


사용법


모든 설정이 완료되었다. 이제 free 버전 빌드를 위해서는 다음을,


fastlane release -efree 


pro 버전 빌드를 위해서는


fastlane release -epro 


를 실행시켜 주기만 하면 된다.


마치며


이 글에서 소개한 다중 빌드 배포 방식은 우리 생산성에 적지 않은 도움이 되었다. 릴리즈 때마다 수동으로 빌드 관련 세팅을 바꾸느라 들이던 시간이 아껴지고, 실수하거나 헷갈릴 염려도 없었다. 배포 프로세스에서 정신적 부하가 없어졌다. 또한 한번 자동화된 프로세스이므로 더 큰 자동화 프로세스의 일부로 포함시키기 용이했다. 예를 들면 CI(Continuous Integration) 파이프라인에 적절하게 fastlane 명령어들을 사용하여 프리, 프로, 일본어 버전을 모두 빌드해버리는 것도 가능했다.


처음에 나는 Fastlane이 주는 간편함에 이끌렸다. 하지만 Fastlane 진정한 잠재력은 확장성과 자유도에 있다. 다중 빌드 배포는 하나의 비교적 간단한 예시에 불과하다. 이 글이 Fastlane으로 커스터마이즈 된 빌드 프로세스를 만들 생각을 했던 분들에게 조금이나마 도움이 되었으면 좋겠다.


프리 버전과 프로버전을 동시에 유지, 관리하는 서로 다른 수 없이 많은 방법이 있을 수 있다. 혹시 더 좋은 방법을 사용 중이거나 아이디어가 있으면 코멘트에 공유를 부탁드린다.


We’re hiring!


컴퓨터가 해줄 수 있는것을 사람이 하고있다고? 자동화, 효율화를 매우 중요하게 여기는 딜라이트룸에 관심이 있다면 아래 링크를 클릭!


지원 가능 포지션 확인하기 >> We're hiring!

매거진의 이전글 내가 하는 일은 Product에 어떤 의미가 있을까?

작품 선택

키워드 선택 0 / 3 0

댓글여부

afliean
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari