...
본문 바로가기

iOS

빌드 환경 분리 - Xcode

이전까지는 빌드 환경에 대해서 딱히 고려하지 않았다.

필요성을 느낀 이유는 출시한 앱이 구글광고를 포함하고 있기 때문에, 개발환경에서는 해당 구글광고 유료 ID를 이용하면 안된다. 

( 예전에 실수로 개발하다가 계속 유료 ID쓰면서 테스트했더니 구글로부터 경고 먹었었다. ) 

그래서 개발할 때는 테스트 광고 ID를 사용하고, 출시할 때는 유료 ID로 변경해서 배포해야한다. 

이걸 매번 수동적으로 하자니 불편함이 있고, 또 실수로 깜빡하는 경우도 생긴다. 그러면 또 다시 아카이브, 제출...하는 귀찮음이 발생한다. 지금까지 참고 오다가, 이번에 자동배포를 Fastlane을 적용해보면서, 자동화에 대해 관심이 생겼고, 

광고ID도 자동으로 변경됐으면 하는 생각이 들었다. 

 

 

내가 기대하는 결과는 다음과 같다.

개발을 할때는 무료 광고 ID를 이용하고,

출시할때는 자동적으로 유료 ID로 알아서 바꿔주기를 기대한다.

 

 

적용하기에 앞서 우선 target, scheme, build configuration의 개념이 필요하다.

 

 


 

Target

Target은 파일 또는 앱과 같은 product로 빌드하기 위한 것이다. 

최상단 프로젝트를 선택하면, 아래와 같이 Target들을 볼 수 있다.

밑에 보면 +로 추가할 수도 있고, App이 될수도 있고, 라이브러리, 프레임워크, Extension 등이 될수 있다. 

그래서 우리는 하나의 target을 선택해서 빌드하고, 시뮬레이터 또는 디바이스에서 앱을 설치하여 볼 수 있다. 

 


Scheme

scheme은 무엇을 어떻게 빌드할지에 관한 정보가 담겨있다. 

xcode를 사용해본 사람들은 scheme의 이름을 잘 모르더라도, 항상 사용하고 있을 것이다. 

왜냐하면 우리가 빌드를 하기 위해서는 하나의 target을 선택해서 빌드를 하는데, 

이때 target만으로 빌드하는 것이 아니라, 

target이 선택된 scheme으로 빌드를 한다. 

 

상단의 EasyToDoList_debug가 scheme의 이름이고, 

눌러보면 아래와 같이 Edit, New, Manage 할수가 있다. 

 

New Scheme을 선택하면, target을 선택하라고 뜬다.

 

그래서 우리는 scheme을 통해서 특정 Target을 선택하고, 

어디에서 실행할지를 결정할 수 있고 ( 디바이스 또는 시뮬레이터 ) 

또한 어떻게 빌드할지도 결정할 수 있다.

이때, 사용되는게 build configuration이다. 

Edit Scheme을 선택하면 아래와 같이 다양한 정보들을 확인할 수 있는 창이 뜬다. 

 


Build Configuration

처음에 프로젝트를 만들 때 부터 Build Configuration에는 Debug, Release가 생성되어있다.

Build Configuration은 빌드를 할 때 각 configuration에 따라 설정된 값으로 빌드할 수 있게 해준다. 

즉, Scheme에서 Build Configuration을 선택할때, Debug를 선택했다면, 

Debug configuration에 따라 빌드를 하게 된다. 

 

 

그럼 어떻게 Debug, Release만으로 다르게 빌드할 수 있게 해줄까? 🤔

 

 

Target을 선택해서 Build Settings을 보면 선택한 Target에 대한 빌드 세팅들에 관한 정보를 볼 수 있다. 

각 정보들이, Build Configuration마다 분기되어있는걸 볼 수 있다

즉, 여기에 있는 값들을 토대로 각 Target마다 빌드설정을, Build Configuration에 의해 가능하게 해준다. 

그리고 중요한점은, 여기에 새팅되어있는 값들은 이름에서 알 수 있듯이, 빌드과정에서만 접근할 수 있는 값이다. 

그러므로, 코드에서는 직접적으로 값들을 접근할 수가 없다. 

 

 

여기까지 어느정도 개념이 잡혔다면, Build Configuration을 이용하여 Debug, Release configuration에 따라 각각 다른 값들을 넣어줌으로써, 우리가 원하는 기대값을 얻을 수 있다. 

 

이것을 구현하는 방법에는 여러가지가 있다고 한다. 

 


(1) 가장 간단한 방법

우선 가장 간단한 방법을 설명하면, 

Build Setting + Info.plist를 이용하여, 값을 가져오는 방법이다.

 

Build Settings 에 + 버튼을 누르면 Add User-Defined Setting이 있다. 

 

그걸 누르고 이름을 바꿔주면, 자동적으로 Build Configuration에 따라 분기된걸 볼 수 있다. 

( 참고로, Target의 Build Setting값은 프로젝트로부터 상속받게 된다.

프로젝트에 Build Setting값을 추가하면, 하위 Target들도 가질 수 있게 된다.

즉, 반대로 Target에만 Build Setting을 추가하면 다른 Target에서는 알수가 없다. )

 

 

그래서 나는 각 configuration마다 다르게 값을 부여했다. 

 

이제 여기에 저장된 값을 가져오기 위해서는 Info.plist를 이용한다. 

Info.plist에는 Key와 value로 이루어져있고, 

Info.plist에 있는 값들도 빌드타임때 업데이트 되어진다.  

Key는 build setting의 이름을 가리키고, value는 그에 속한 값을 의미한다. 

즉 Xcode는 현재 선택된 build configuration에 따른 build setting값들을 찾아서 Info.plist의 value에 새팅해준다. 

 

 

다음과 같이 Key에는 원하는 이름을 설정하고,

Value에는 Build Setting에 작성했던 Setting이름을 $ ( ) 으로 넣어준다. 

 

그렇게 되면 

이제, 코드에서는 Bundle 클래스로 접근하여, infoDictionary 프로퍼티로 접근해서 value를 빌드타임 때 얻을 수 있게된다. 

현재, EasyToDoList_debug 스킴은, Debug Configuration을 이용하고 있고,  

아래와 같이 코드를 작성하면,  Debug configuration에는 "debug용입니다"라고 작성했으니 이 값이 뜨면 오케이!

실행해보면 다음과 같이 잘 뜬다.

 

이번엔 Release configuratoin을 선택해서 실행해보면,  마찬가지로 "release용입니다"라고 떠야할 것이다. 

 

매우 잘뜬다!!

 

 

하지만 어떤 블로그에 의하면, 이러한 방법은 가장 쉬운 방법이지만, 반면에 위험이 있다고 한다.

다운로드된 앱으로부터 Info.plist를 추출하는 건 쉽고, 그로인해 정보들을 알 수 있다고 한다. 

그러므로, 중요한 API KEY인경우나 민감한 정보들은 위험할 수 있다.

 

 


 

(2) 복잡하지만 안전한 방법

 

그래서 약간의 복잡함(?)을 안고서 구현할 수 있는데, 

Build setting에 값을 저장하기보다는, 직접 코드로 구현하는 방법이다.

Build setting - > Info.plist 까지는 동일한데, 

이제, 코드에서 직접 분기처리해준다. 

 

큰 맥락은 다음과 같다. 

Build Setting에는 어떠한 값도 추가로 지정하지 않는다. 

Info.plist에서는 $(CONFIGURATION) 를 value로 가지는 어떤 Key를 만들어놓고,

이제 코드로, 접근하게 되면  Build Configuration이름을 얻을 수 있다.

즉! 간단하게 코드에서는 Build Configuration이름을 분기처리하여 내가 원하는 값들을 내보내주면 된다.

 

 

그럼 우선 Info.plist에 다음과 같이 Key value로 만들어준다. 

 

그런 다음 아래와 같이 enum타입으로 만든다음, 

case에는 Build Configuration의 lowerCased된 단어로 만들어준다. 

 

그런 다음 위에서 했던 것처럼 Bundle을 통해서 Info.plist의 값을 접근하여, 

value를 가져온다. 

그럼 선택된 Build Configuration의 이름이 가져올텐데, Debug, Release 둘 중하나로 가져오게 된다.

앞에는 대문자로 되어있으므로, 이 값을 lowercase()로 만든다음 Configuration을 초기화한다. 

import Foundation

enum Configuration: String  {
    case debug
    case release
    
    static var current: Configuration? {
        guard let rawValue = Bundle.main.infoDictionary?["Configuration"] as? String else {
            return nil
        }
        return  Configuration(rawValue: rawValue.lowercased())
    }
    
    static var googleId: String {
        switch current {
        case .debug:
            return "debug용입니다"
        case .release:
            return "release용입니다"
        default:
            return ""
        }
    }
}

 

그럼 이제 원하는 부분에 다음과 같이 코드를 작성하면 

현재 선택된 Build Configuration에 따라 바뀌게 된다. 

print(Configuration.googleId)

 

 

 

 


참고 

https://leechanho.tistory.com/48

 

[iOS & Xcode] Dev & Release 빌드 환경 분리

Xcode에서는 프로젝트를 생성할 때 기본적으로 debug와 release의 두가지 빌드 환경을 제공해줍니다. 이번 포스팅의 목표는, Rest API url, 외부 서비스의 API Key 등을 빌드 환경에 따라 동적이게 변경하

leechanho.tistory.com

https://cocoacasts.com/tips-and-tricks-managing-build-configurations-in-xocde

 

Managing Build Configurations in Xcode

A brand new Xcode project defines two build configurations, Debug and Release. Most projects define one or more additional build configurations for various reasons. This isn't new and it's a good practice to use build configurations to tailor a build to th

cocoacasts.com