Android 개발자의 React Native iOS 배포 적응기
ios는 항상 낯선 땅이지만, 길을 잃을 순 없으니까...
1. Intro: 익숙함과의 결별
나는 안드로이드 환경이 편하다. 윈도우와 안드로이드 스튜디오, 그리고 Keystore 파일 하나면 배포 준비가 끝나는 그 간결함에 익숙해져 있다.
그런 나에게 React Native(RN) 기반의 iOS 앱 배포는 완전히 다른 문법으로 쓰인 입국 심사서를 작성하는 기분이었다.
인증서(Certificate), 프로비저닝 프로파일(Provisioning Profile), 까다로운 권한 정책 등 챙겨야 할 것이 생각보다 많았다.
빌드 버튼을 누를 때마다 마주한 낯선 에러 로그들은 꽤나 큰 스트레스였다.
이번 글은 iOS 생태계가 낯선 개발자가 맨땅에 헤딩하며 정리한 배포 실전 로그다.
다음에 같은 이슈로 시간을 낭비하지 않기 위해, 그리고 나와 같은 상황에 처한 분들을 위해 기록을 남긴다.
2. 초기 설정 이슈 (Account & Config)
1) 계정 승인 지연과 해결
개발자 등록비($99) 결제 후, 48시간이 지나도 계정 상태가 Pending에서 멈춰 있었다. 자동 승인 시스템이 있는 구글과 달리, 애플은 검토 과정에 시간이 소요되는 듯했다.
- Problem: 주말 포함 3일 이상 대기 상태 지속.
- Solution: 마냥 기다리는 대신 **Apple Developer Contact (전화 상담)**를 요청했다.
- Result: 월요일 오전 9시 상담원 연결 후, 본인 확인 절차를 거쳐 5분 만에
Active상태로 전환되었다. 비즈니스 데이(Business Day) 기준으로 처리가 늦어진다면 전화 문의가 가장 확실한 해결책이다.
2) 의존성 충돌 해결 (Podfile)
안드로이드의 build.gradle에 해당하는 Podfile 설정에서 가장 많은 시간을 썼다. 특히 React Native(0.7x)와 Firebase(v15+)를 함께 사용할 때 동적 링킹(Dynamic Linking)과 정적 링킹(Static Linking) 방식의 충돌이 발생했다.
- Solution:
Podfile최상단에 아래 설정을 명시하여 정적 프레임워크 사용을 강제해야 한다.
platform :ios, '15.5'
prepare_react_native_project!
# [Core Config] Firebase 호환성을 위한 정적 링킹 강제
use_frameworks! :linkage => :static
$RNFirebaseAsStaticFramework = true
target 'babpleApp' do
use_modular_headers!
# Modular Headers 활성화
pod 'FirebaseAppCheck', :modular_headers => true
pod 'FirebaseCore', :modular_headers => true
pod 'FirebaseMessaging', :modular_headers => true
# ... (중략)
post_install do |installer|
react_native_post_install(installer, config[:reactNativePath], :mac_catalyst_enabled => false)
# 헤더 경로 참조 오류 방지
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES'] = 'YES'
end
end
end
end
3) 리소스 중복 (Multiple commands produce)
빌드 시 Multiple commands produce ... Fontisto.ttf 에러가 발생했다.
react-native-vector-icons 라이브러리가 빌드 과정에서 폰트를 자동 복사하는데, 내가 Xcode의 Copy Bundle Resources에 수동으로 파일을 추가하여 중복이 발생한 것이다.
- Fix: Xcode > Build Phases > Copy Bundle Resources 목록에서 중복된
.ttf파일을 제거했다. (Info.plist 목록은 유지)
3. 플랫폼 특성 대응 (Troubleshooting)
4) 권한 명시 (Info.plist)
앱 실행 직후 크래시(Crash)가 발생했다. 원인은 권한 사용 목적(Usage Description) 누락이었다. 안드로이드는 권한 선언만 하면 되지만, iOS는 “왜 이 권한이 필요한지”를 구체적인 문장으로 명시해야 한다.
[Info.plist 필수 설정]
<key>NSLocationWhenInUseUsageDescription</key>
<string>사용자의 현재 위치를 기반으로 주변 맛집 정보를 추천하기 위해 권한이 필요합니다.</string>
<key>GIDClientID</key>
<string>[GCP Console에서 발급받은 iOS Client ID]</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>[REVERSED_CLIENT_ID]</string>
</array>
</dict>
</array>
5) GCP Console 설정 누락
GoogleService-Info.plist 파일이 있다고 끝난 게 아니었다. Firebase 콘솔에서 iOS 앱을 추가했더라도, Google Cloud Platform(GCP) 콘솔에서 iOS용 OAuth 2.0 Client ID가 생성되었는지 반드시 확인해야 한다.
- Check: Firebase 설정 파일에는 안드로이드용 ID만 포함된 경우가 많다. GCP에서 iOS Client ID를 별도로 생성하고, 이를
Info.plist의GIDClientID에 입력해주어야 정상 작동한다.
6) 빌드 환경 초기화 자동화 (Shell Script)
네이티브 설정을 변경하거나 pod install을 반복하다 보면 캐시 문제로 인해 빌드가 실패하는 경우가 잦다. 매번 수동으로 폴더를 삭제하는 비효율을 줄이기 위해 초기화 스크립트를 작성했다.
[scripts/clean-ios.sh]
#!/bin/bash
set -e
# 프로젝트 루트 확인
if [ ! -d "ios" ]; then
echo "Error: 'ios' directory not found. Please run from project root."
exit 1
fi
cd ios
echo "Cleaning build artifacts (Pods, DerivedData, Lockfile)..."
rm -rf Pods
rm -f Podfile.lock
rm -rf ~/Library/Developer/Xcode/DerivedData
echo "Re-installing Pods..."
pod install --repo-update
cd ..
echo "iOS Build environment is clean."
package.json에 "clean:ios" 스크립트로 등록해두면, 예기치 않은 빌드 에러 발생 시 빠르게 환경을 리셋할 수 있다.
4. Outro: 배포를 마치며
우여곡절 끝에 TestFlight 업로드에 성공했다. Xcode의 Archive가 완료되고, 내 아이폰에 설치된 앱 아이콘을 확인했을 때의 안도감은 상당했다.
안드로이드와 iOS는 OS의 철학만큼이나 빌드 프로세스도 다르다. 안드로이드가 자유도가 높은 조립식 도구라면, iOS는 정해진 규격을 엄격히 따라야 하는 정밀 기계 같다는 인상을 받았다.
Swift 언어를 깊이 알지는 못하지만, 이번 과정을 통해 Xcode의 빌드 파이프라인과 필수 설정 파일들의 역할을 명확히 이해하게 되었다. 낯선 환경이었지만, 결국 해결하지 못할 문제는 없었다.
이제 다시 익숙한 안드로이드로 돌아갈 시간이다.
하지만 다음에 다시 맥북을 열어야 할 때는, 지금보다 훨씬 수월하게 이 과정을 통과할 수 있을 것이다.
마침.
다른 글 보기
애자일(Agile)은 '문서 면제권'이 아니다. 노대리의 감리 후기
스프린트만 잘 돌면 되는 줄 알았던 5년 차 개발자의 반성문. 설계 감리의 RTM부터 현장 감리의 100페이지 캡처 보고서까지. 감리 통과를 위해 반드시 준비해야 할 문서 리스트와 실전 노하우 총정리.
AI는 내 질문에 어떻게 대답하는가? Context의 이해
대화가 길어지면 바보가 되는데... 얘 혹시 금붕언가? 하고 봤더니 진짜 금붕어였다. AI의 금붕어화를 막기 위한 Context 관리 방법
프롬프트 엔지니어링? 거창한 게 아니었다 (나의 AI 팀 채용기)
용어가 낯설어서 공부해보니, 1인 개발자가 매일 숨 쉬듯이 하던 '협업'이었다. 기획자, DBA, 디자이너를 내 컴퓨터 속에 채용하는 법.
AI 시대, 나는 더 이상 코드를 공부하지 않는다.
책장에 쌓인 개발 서적들이 의미 없어진 이유. 내가 바라본 지식 소유의 종말과 경험의 시대, 그리고 왜? 회사를 나와서 1인 개발을 시작했는지에 대해서