하나의 앱을 개발하다 보면 단순히 '개발'과 '출시'라는 두 가지 상황만 존재하지 않습니다. 실제 현업에서는 내부 테스트를 위한 개발(Development) 서버, QA팀이나 외부 테스터를 위한 스테이징(Staging) 서버, 그리고 실제 사용자가 사용하는 운영(Production) 서버 등 여러 환경을 동시에 관리해야 하는 경우가 비일비재합니다. 각 환경은 서로 다른 데이터베이스, API 엔드포인트, 그리고 외부 서비스 키를 사용하게 됩니다.
이러한 복잡성 속에서 iOS 개발자들이 가장 흔하게 마주치는 난관 중 하나는 바로 Firebase 설정입니다. 특히 푸시 알림을 위한 FCM(Firebase Cloud Messaging)을 사용한다면, 각 빌드 환경에 맞는 별도의 Firebase 프로젝트를 사용해야 합니다. 이는 즉, 환경마다 다른 Bundle Identifier를 가져야 하고, 그에 따라 고유한 GoogleService-Info.plist
파일을 필요로 한다는 의미입니다. 많은 개발자들이 이 파일을 수동으로 교체하거나, 주석 처리하는 방식으로 번거롭게 관리하곤 합니다. 하지만 이런 방식은 실수를 유발하기 쉽고, 팀 단위 협업에서는 큰 혼란을 야기할 수 있습니다.
이 글에서는 Xcode의 빌드 설정(Build Configurations)과 스킴(Schemes)을 활용하여 개발, 스테이징, 운영 환경을 체계적으로 분리하고, 빌드 시점에 각 환경에 맞는 GoogleService-Info.plist
파일이 자동으로 적용되도록 설정하는 전문가적인 방법을 상세히 다룹니다. 이 가이드를 끝까지 따라오시면, 더 이상 .plist
파일 때문에 고통받는 일 없이, 버튼 클릭 한 번으로 원하는 환경의 앱을 빌드하고 실행할 수 있게 될 것입니다.
1. 기본 개념 이해하기: Configurations와 Schemes
본격적인 설정에 앞서 Xcode의 핵심 개념인 빌드 설정(Build Configurations)과 스킴(Schemes)에 대한 이해가 필요합니다. 이 둘의 관계를 정확히 알아야 전체 프로세스를 효과적으로 제어할 수 있습니다.
빌드 설정 (Build Configurations)
빌드 설정은 특정 타겟을 빌드할 때 사용되는 '설정 값들의 묶음'입니다. Xcode 프로젝트를 처음 생성하면 기본적으로 Debug
와 Release
두 가지 설정이 제공됩니다.
- Debug: 개발 과정에서 사용되는 설정입니다. 디버깅 심볼이 포함되고, 코드 최적화 수준이 낮아 변수 값을 확인하거나 브레이크포인트를 사용하는 데 용이합니다.
- Release: 앱 스토어에 배포하거나 사용자에게 전달할 때 사용되는 설정입니다. 코드가 최적화되어 실행 속도가 빠르고, 앱 용량이 작아지며, 디버깅 정보는 포함되지 않습니다.
우리는 이 기본 설정에 더해 'development', 'staging', 'production'과 같은 우리만의 환경 구분을 추가할 것입니다. 예를 들어, Debug-development
, Release-production
과 같이 조합하여 사용할 수 있습니다.
스킴 (Schemes)
스킴은 '무엇을(Target), 어떻게(Configuration), 어떤 액션(Run, Test, Profile...)으로 실행할 것인가'를 정의하는 '실행 계획'입니다. Xcode 상단에서 타겟 기기 옆에 보이는 드롭다운 메뉴가 바로 스킴을 선택하는 곳입니다.
하나의 스킴은 다음과 같은 정보를 포함합니다.
- 빌드할 타겟(들)의 목록
- 빌드(Build), 실행(Run), 테스트(Test), 프로파일링(Profile), 분석(Analyze), 아카이브(Archive) 각 액션에 사용할 빌드 설정(Configuration)
- 실행 시 전달할 인자(Arguments)나 환경 변수(Environment Variables)
우리는 "MyApp-Dev", "MyApp-Prod"와 같은 스킴을 만들고, "MyApp-Dev" 스킴의 'Run' 액션은 Debug-development
설정을 사용하고, "MyApp-Prod" 스킴의 'Archive' 액션은 Release-production
설정을 사용하도록 연결할 것입니다. 이로써 개발자는 단순히 스킴을 전환하는 것만으로 빌드 환경 전체를 손쉽게 바꿀 수 있습니다.
2. 단계별 실전 가이드: 빌드 환경 구축하기
이제 개념을 알았으니, 실제 프로젝트에 적용해 보겠습니다. 여기서는 간단하게 개발(development)과 운영(production) 두 가지 환경을 분리하는 것을 목표로 하겠습니다. 이 원리를 이해하면 스테이징(staging) 등 더 많은 환경으로 확장하는 것은 매우 쉽습니다.
Step 1: Firebase에서 환경별 프로젝트 및 `GoogleService-Info.plist` 준비
- Firebase 콘솔로 이동하여 2개의 프로젝트를 생성합니다. 하나는 개발용(예: 'MyAwesomeApp-Dev'), 다른 하나는 운영용(예: 'MyAwesomeApp-Prod')입니다.
- 각 Firebase 프로젝트에 iOS 앱을 추가합니다. 이때 가장 중요한 것은 Bundle Identifier를 다르게 설정하는 것입니다.
- 개발용 앱:
com.mycompany.myawesomeapp.dev
- 운영용 앱:
com.mycompany.myawesomeapp
- 개발용 앱:
- 각 프로젝트에서
GoogleService-Info.plist
파일을 다운로드합니다. 이제 2개의.plist
파일이 준비되었습니다. - 혼동을 피하기 위해 파일 이름을 명확하게 변경합니다.
- 개발용 파일 →
GoogleService-Info-dev.plist
- 운영용 파일 →
GoogleService-Info-prod.plist
- 개발용 파일 →
Step 2: Xcode에서 커스텀 빌드 설정(Configurations) 생성하기
이제 Xcode 프로젝트로 돌아와 빌드 설정을 복제하고 새로운 설정을 만듭니다.
- Xcode에서 프로젝트 파일을 선택하고, 'PROJECT' 섹션의 'Info' 탭으로 이동합니다.
- 'Configurations' 항목을 찾습니다. 기본적으로 'Debug'와 'Release'가 보일 것입니다.
- 하단의 '+' 버튼을 클릭하고 'Duplicate "Debug" Configuration'을 선택합니다. 새로 생성된 'Debug Copy'의 이름을
Debug-development
로 변경합니다. - 같은 방식으로 'Duplicate "Debug" Configuration'을 한번 더 선택하고, 이름을
Debug-production
으로 변경합니다. - 이번에는 'Duplicate "Release" Configuration'을 선택하고, 이름을
Release-development
로 변경합니다. - 마지막으로 'Duplicate "Release" Configuration'을 다시 선택하고, 이름을
Release-production
으로 변경합니다.
작업이 끝나면 아래와 같이 총 6개의 설정(기본 2개 + 신규 4개)이 보일 것입니다. (필요에 따라 기본 Debug/Release는 삭제하거나, 더 단순하게 Debug-dev, Release-prod 2개만 추가해도 무방합니다. 여기서는 모든 경우의 수를 다루기 위해 4개를 추가했습니다.)
Step 3: 환경별 Bundle Identifier 및 기타 설정 적용하기
새로 만든 빌드 설정에 따라 각기 다른 Bundle Identifier를 적용해야 합니다.
- 'TARGETS' 섹션에서 당신의 앱 타겟을 선택하고 'Build Settings' 탭으로 이동합니다.
- 검색창에
Product Bundle Identifier
를 검색합니다. - 해당 항목의 각 설정 옆에 다른 값을 입력할 수 있습니다.
Debug-development
:com.mycompany.myawesomeapp.dev
Release-development
:com.mycompany.myawesomeapp.dev
Debug-production
:com.mycompany.myawesomeapp
Release-production
:com.mycompany.myawesomeapp
- 같은 방식으로 앱 이름(
Product Name
), 앱 아이콘(Asset Catalog App Icon Set Name
) 등도 환경별로 다르게 설정할 수 있습니다. 예를 들어 개발용 앱은 이름 뒤에 '(Dev)'를 붙이고 아이콘에 'DEV' 리본을 추가하면 구분이 매우 용이해집니다.
Step 4: 커스텀 스킴(Schemes) 생성 및 연결하기
이제 이 모든 설정을 편리하게 사용하기 위한 스킴을 만들 차례입니다.
- Xcode 상단의 스킴 드롭다운 메뉴를 클릭하고 'Manage Schemes...'를 선택합니다.
- 기존 스킴을 선택하고 하단의 톱니바퀴 아이콘을 클릭한 뒤 'Duplicate'를 선택합니다.
- 복제된 스킴의 이름을 'MyAwesomeApp-Dev'로 변경합니다.
- 새로 만든 'MyAwesomeApp-Dev' 스킴을 선택하고 'Edit...' 버튼을 클릭합니다.
- 왼쪽 메뉴에서 각 액션(Run, Test, Profile, Analyze, Archive)을 선택하고, 오른쪽의 'Build Configuration'을 우리가 만든 설정과 연결합니다.
- Run:
Debug-development
- Test:
Debug-development
- Profile:
Release-development
- Analyze:
Debug-development
- Archive:
Release-development
- Run:
- 같은 방식으로 'MyAwesomeApp-Prod' 스킴을 생성하고, 이번에는 production용 설정과 연결합니다.
- Run:
Debug-production
- Test:
Debug-production
- Profile:
Release-production
- Analyze:
Debug-production
- Archive:
Release-production
- Run:
이제 Xcode 상단 메뉴에서 'MyAwesomeApp-Dev' 스킴을 선택하고 빌드하면 development 설정으로, 'MyAwesomeApp-Prod'를 선택하면 production 설정으로 앱이 빌드됩니다.
3. 핵심: Run Script로 `GoogleService-Info.plist` 자동 교체하기
지금까지의 설정은 앱의 Bundle ID나 이름을 바꾸는 것이었습니다. 하지만 가장 중요한 GoogleService-Info.plist
파일은 아직 그대로입니다. 이 파일을 빌드 시점에 동적으로 교체해주는 마법이 바로 'Run Script' 빌드 단계입니다.
Step 1: 프로젝트에 설정 파일 추가하기
- Xcode 프로젝트 내부에 설정 파일을 보관할 그룹(폴더)을 만듭니다. 'Config' 또는 'Firebase' 같은 이름이 적합합니다.
- Finder에서 해당 폴더를 열고, Step 1에서 이름을 변경해 두었던
GoogleService-Info-dev.plist
와GoogleService-Info-prod.plist
파일을 복사해 넣습니다. - 이 파일들을 Xcode 프로젝트 내비게이터의 'Config' 그룹으로 드래그 앤 드롭하여 프로젝트에 추가합니다.
- 파일 추가 시 나타나는 옵션 창에서 **'Add to targets' 체크박스를 반드시 해제해야 합니다.** 이것이 매우 중요합니다. 만약 체크하면 Xcode가 이 파일들을 직접 번들에 포함시키려 하여 우리가 작성할 스크립트와 충돌하거나, 의도치 않은 파일이 포함될 수 있습니다. 우리는 스크립트를 통해 '복사'할 것이므로, 타겟 멤버십에는 포함시키지 않습니다.
Step 2: Run Script 빌드 단계 추가하기
이제 빌드 프로세스 중에 실행될 스크립트를 추가합니다.
- 'TARGETS' 섹션에서 당신의 앱 타겟을 선택하고 'Build Phases' 탭으로 이동합니다.
- 상단의 '+' 버튼을 클릭하고 'New Run Script Phase'를 선택합니다.
- 새로 생성된 'Run Script' 단계가 보일 것입니다. 이 단계의 순서는 매우 중요합니다. 'Copy Bundle Resources' 단계 **바로 이전**으로 드래그하여 옮겨주세요. 이렇게 해야 우리가 복사한 파일이 최종 앱 번들에 정상적으로 포함됩니다.
- 'Run Script' 단계의 이름을 'Copy GoogleService-Info.plist'와 같이 명확하게 변경하면 나중에 관리하기 좋습니다.

Step 3: 스크립트 작성 및 적용
이제 새로 만든 Run Script 단계의 쉘 스크립트 입력창에 아래 코드를 붙여넣습니다. 코드는 각자의 프로젝트 구조에 맞게 일부 수정이 필요할 수 있습니다.
# 스크립트 실행 시점에 현재 어떤 빌드 설정(Configuration)인지 출력합니다. (디버깅용)
echo "Executing 'Copy GoogleService-Info.plist' script for configuration: ${CONFIGURATION}"
# plist 파일들이 위치한 경로를 지정합니다.
# ${PROJECT_DIR}은 프로젝트의 루트 디렉토리를 의미하는 Xcode 환경 변수입니다.
# 'Config'는 Step 1에서 우리가 만든 폴더(그룹) 이름입니다. 실제 폴더 경로와 일치해야 합니다.
PATH_TO_GOOGLE_PLISTS="${PROJECT_DIR}/YourAppName/Config"
# 현재 빌드 설정(Configuration)에 따라 사용할 plist 파일을 결정합니다.
case "${CONFIGURATION}" in
# Development 환경용 설정들
"Debug-development" | "Release-development" )
SOURCE_PLIST_PATH="${PATH_TO_GOOGLE_PLISTS}/GoogleService-Info-dev.plist"
echo "Using Development plist."
;;
# Production 환경용 설정들
"Debug-production" | "Release-production" )
SOURCE_PLIST_PATH="${PATH_TO_GOOGLE_PLISTS}/GoogleService-Info-prod.plist"
echo "Using Production plist."
;;
# 그 외의 경우 (예: 기본 Debug, Release 등) - 기본적으로 Production을 사용하도록 설정하거나 에러를 발생시킬 수 있습니다.
*)
SOURCE_PLIST_PATH="${PATH_TO_GOOGLE_PLISTS}/GoogleService-Info-prod.plist"
echo "Warning: No specific plist for this configuration. Using Production plist as a fallback."
;;
esac
# 최종적으로 앱 번들에 복사될 경로를 지정합니다.
# ${BUILT_PRODUCTS_DIR}는 빌드된 결과물(.app 파일)이 위치한 디렉토리입니다.
# ${PRODUCT_NAME}은 빌드 설정에 따라 정해진 제품 이름입니다.
DESTINATION_PLIST_PATH="${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
# 원본 파일이 존재하는지 확인합니다.
if [ -f "${SOURCE_PLIST_PATH}" ]; then
# 원본 파일을 최종 목적지로 복사합니다.
# 복사 후 파일 이름은 반드시 'GoogleService-Info.plist'가 되어야 Firebase SDK가 인식할 수 있습니다.
cp -r "${SOURCE_PLIST_PATH}" "${DESTINATION_PLIST_PATH}"
echo "Successfully copied ${SOURCE_PLIST_PATH} to ${DESTINATION_PLIST_PATH}"
else
# 원본 파일을 찾을 수 없는 경우, 빌드를 실패시켜 문제를 즉시 인지하게 합니다.
echo "error: GoogleService-Info plist not found at ${SOURCE_PLIST_PATH}. Please check the path and file name."
exit 1
fi
스크립트 해설:
${CONFIGURATION}
: Xcode가 빌드 시점에 자동으로 채워주는 환경 변수로, 현재 사용 중인 빌드 설정의 이름(예: 'Debug-development')을 담고 있습니다.case "${CONFIGURATION}" in ... esac
: 쉘 스크립트의 switch-case 구문입니다.${CONFIGURATION}
변수 값에 따라 다른 동작을 하도록 분기합니다.|
문자는 OR 조건으로, 여러 설정을 하나의 케이스에서 처리할 수 있게 해줍니다.PATH_TO_GOOGLE_PLISTS
:GoogleService-Info-dev.plist
와GoogleService-Info-prod.plist
파일이 위치한 실제 폴더 경로를 지정해야 합니다.${PROJECT_DIR}
은 프로젝트의.xcodeproj
파일이 있는 루트 디렉토리입니다. 그 하위 경로를 정확하게 적어주세요.${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist
: 이 부분이 핵심입니다. 빌드 과정에서 생성되는 앱 패키지(.app
) 내부에, 우리가 선택한 원본 plist 파일을GoogleService-Info.plist
라는 최종 이름으로 복사해 넣는 명령입니다. 어떤 원본 파일을 사용했든, 최종 결과물의 이름은 항상 이것이어야 합니다.if [ -f ... ]
/else
/exit 1
: 스크립트의 안정성을 높이는 부분입니다. 지정된 경로에 원본 plist 파일이 존재하는지 확인하고, 만약 없다면 빌드 로그에 에러 메시지를 출력하고 빌드를 강제로 실패시킵니다. 이는 잘못된 설정으로 앱이 빌드되어 런타임에 크래시가 발생하는 것을 사전에 방지하는 매우 중요한 방어 코드입니다.
이제 모든 설정이 끝났습니다. Xcode 상단에서 'MyAwesomeApp-Dev' 스킴을 선택하고 빌드(Cmd+R)하면 Run Script가 실행되어 GoogleService-Info-dev.plist
가 앱 번들에 포함되고, 'MyAwesomeApp-Prod' 스킴으로 빌드하면 GoogleService-Info-prod.plist
가 포함됩니다. 이제 더 이상 수동으로 파일을 관리할 필요가 없습니다!
4. 고급 팁: `.xcconfig` 파일로 설정 관리 고도화하기
Run Script 방식은 매우 강력하지만, Bundle ID나 Product Name 같은 설정들을 Xcode의 GUI(Build Settings)에서 직접 관리하는 것은 프로젝트가 커질수록 불편해질 수 있습니다. 이 설정들을 텍스트 기반의 .xcconfig
파일로 분리하면 훨씬 깔끔하고 전문적인 관리가 가능합니다.
`.xcconfig` 파일이란?
Xcode Configuration Settings File(.xcconfig
)은 빌드 설정을 Key-Value 형태로 저장하는 텍스트 파일입니다. 이를 사용하면 다음과 같은 장점이 있습니다.
- 가독성 및 관리 용이성: 복잡한 Build Settings UI 대신 텍스트 파일에서 설정을 한눈에 파악하고 수정할 수 있습니다.
- 소스 컨트롤(Git 등) 친화적:
.pbxproj
파일의 충돌(conflict)을 줄여줍니다. 여러 개발자가 빌드 설정을 변경할 때.pbxproj
파일은 충돌이 잦지만,.xcconfig
파일은 텍스트 기반이라 병합(merge)이 훨씬 쉽습니다. - 재사용성: 공통 설정을 담은 파일을 만들고, 각 환경별 파일에서 이를 가져와(import) 사용하는 등 모듈화된 관리가 가능합니다.
`.xcconfig` 파일 적용 방법
- Xcode에서 'File' > 'New' > 'File...'을 선택하고 'Configuration Settings File'을 검색하여 선택합니다.
- 먼저 공통 설정을 담을
Shared.xcconfig
파일을 생성합니다. - 같은 방식으로
Development.xcconfig
와Production.xcconfig
파일을 생성합니다. - Shared.xcconfig:
// 모든 환경에 공통으로 적용될 설정 MARKETING_VERSION = 1.0.0 CURRENT_PROJECT_VERSION = 1 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
- Development.xcconfig:
// Shared.xcconfig 파일을 가져옵니다. #include "Shared.xcconfig" // Development 환경 고유의 설정 PRODUCT_BUNDLE_IDENTIFIER = com.mycompany.myawesomeapp.dev PRODUCT_NAME = MyAwesomeApp (Dev) // 다른 커스텀 플래그 등도 추가 가능 // GCC_PREPROCESSOR_DEFINITIONS[config=Debug-development] = $(inherited) IS_DEV=1 // GCC_PREPROCESSOR_DEFINITIONS[config=Release-development] = $(inherited) IS_DEV=1
- Production.xcconfig:
// Shared.xcconfig 파일을 가져옵니다. #include "Shared.xcconfig" // Production 환경 고유의 설정 PRODUCT_BUNDLE_IDENTIFIER = com.mycompany.myawesomeapp PRODUCT_NAME = MyAwesomeApp // GCC_PREPROCESSOR_DEFINITIONS[config=Debug-production] = $(inherited) IS_PROD=1 // GCC_PREPROCESSOR_DEFINITIONS[config=Release-production] = $(inherited) IS_PROD=1
- 마지막으로, 이
.xcconfig
파일들을 프로젝트의 빌드 설정과 연결합니다. 'PROJECT' > 'Info' > 'Configurations'로 이동하여 각 설정에 맞는.xcconfig
파일을 지정해줍니다.Debug-development
,Release-development
→Development.xcconfig
Debug-production
,Release-production
→Production.xcconfig
- 이제 Build Settings에서 수정했던 Bundle Identifier, Product Name 등의 값들은
.xcconfig
파일에서 정의한 값으로 덮어씌워집니다. Xcode UI에서는 해당 값들이 볼드체로 표시되며, 마우스를 올리면 어떤.xcconfig
파일에서 온 설정인지 확인할 수 있습니다.
이처럼 .xcconfig
를 도입하면 설정이 코드처럼 관리되어 프로젝트의 확장성과 유지보수성이 크게 향상됩니다.
결론: 안정적이고 자동화된 개발 환경을 향하여
우리는 Xcode의 빌드 설정, 스킴, 그리고 Run Script를 조합하여 여러 개발 환경을 관리하는 체계적인 시스템을 구축했습니다. 이 방법을 통해 얻는 이점은 명확합니다.
- 휴먼 에러 방지: 더 이상 개발자가 직접 파일을 바꾸거나 설정을 수정할 필요가 없으므로, 실수로 개발용 설정으로 운영 버전을 배포하는 등의 치명적인 사고를 예방할 수 있습니다.
- 생산성 향상: 스킴 전환 한 번으로 모든 관련 설정(Bundle ID, 앱 이름, 아이콘, 서버 환경 등)이 자동으로 변경되어, 개발자는 환경 전환에 드는 시간과 노력을 아끼고 핵심 로직 개발에만 집중할 수 있습니다.
- 팀 협업 효율 증대: 프로젝트를 처음 접하는 팀원도 스킴의 의미만 이해하면 복잡한 설정 과정 없이 즉시 원하는 환경에서 개발을 시작할 수 있습니다.
- 확장성: 'staging', 'qa' 등 새로운 환경이 추가되더라도, 설정과 plist 파일을 추가하고 Run Script의 case 구문에 한 줄을 더하는 것만으로 손쉽게 확장이 가능합니다.
단순히 GoogleService-Info.plist
파일을 교체하는 것을 넘어, 빌드 환경 자동화는 모든 전문 iOS 개발팀이 갖춰야 할 필수적인 역량입니다. 오늘 배운 내용을 당신의 프로젝트에 바로 적용하여, 한 단계 더 성숙하고 안정적인 개발 프로세스를 경험해 보시길 바랍니다.
0 개의 댓글:
Post a Comment