평소 Xcode Cloud를 사용했지만 main, release, develop (Release Note) 등 번거로운 배포 과정과 빌드 기다리는 시간이 너무 아까웠다
JOBIS_iOS_develop: PR 검증 (빌드 테스트)JOBIS_iOS_release: App Store 배포 (Main Merge 시)Jenkins가 빌드 시 보안 파일(Firebase 설정, XCConfig)을 주입하는 방식을 사용함.
JOBIS-DSM-iOS-v2/
├── Projects/
│ └── App/
│ └── Resources/
│ └── Firebase/ # Jenkins가 원본 경로에서 복사하여 주입
│ ├── GoogleService-Info-DEV.plist
│ └── ...
├── XCConfig/ # Jenkins가 원본 경로에서 복사하여 주입
│ └── JOBIS-DSM-iOS-v2/
│ ├── Shared.xcconfig
│ └── ...
├── fastlane/
│ ├── Fastfile # 배포 스크립트
│ ├── .env # 환경변수 (비밀번호 등)
│ └── AuthKey_9WAHTJ84G4.p8 # App Store Connect API Key
└── Tuist/ # 프로젝트 생성 도구
기본적인 빌드 및 자동화 도구 설치를 진행함.
# Homebrew & mise 설치
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install mise
echo 'eval "$(mise activate zsh)"' >> ~/.zshrc
source ~/.zshrc
# Jenkins & ngrok 설치
brew install jenkins-lts
brew services start jenkins-lts
brew install ngrok
ngrok config add-authtoken [YOUR_AUTHTOKEN]
# Ruby 환경 구성 (OpenSSL 1.1 필수 - 호환성 이슈 방지)
brew install openssl@1.1
RUBY_CONFIGURE_OPTS="--with-openssl-dir=/opt/homebrew/opt/openssl@1.1" \
mise install ruby@3.2.6
mise use -g ruby@3.2.6
cd ~/Desktop/프로젝트 경로
# Tuist 설치
mise install tuist@4.119.2
# Fastlane 의존성 설치
git submodule update --init --recursive
cd fastlane
mise exec ruby@3.2.6 -- gem install bundler -v 2.5.22
mise exec ruby@3.2.6 -- bundle install
FCM을 사용하는 프로젝트는 googleInfo 파일이 필수이고 XCConfig 같은 보안 파일은 따로 설정해준다.
Jenkins가 빌드 시 사용할 원본 파일들을 로컬 특정 경로에 미리 배치함.
~/Desktop/JOBIS-GoogleInfo/Firebase/ 경로에 위치시킴.~/Desktop/JOBIS-v2-XCConfig/XCConfig/ 경로에 위치시킴.fastlane/AuthKey_....p8 위치에 저장.http://localhost:8080 
main 브랜치가 아닌 develop 브랜치에서 변경사항이 발생하거나 PR(Pull Request)이 생성될 때 실행됨.main 브랜치에 코드가 머지되거나 직접 푸시될 때 작동함.develop Job과 달리 Fastlane이 자동 실행되어 App Store Connect에 빌드를 업로드함.Info.plist 내의 앱 버전 및 빌드 번호를 자동으로 카운팅한다
민감한 정보는 .env 파일로 분리하여 관리함.
# Apple Account
APPLE_ID=leejjh080628@gmail.com
TEAM_ID=#######
# Fastlane Auth
FASTLANE_USER=leejjh080628@gmail.com
FASTLANE_PASSWORD=####-####-####-####
# App Store Connect API Key
APP_STORE_CONNECT_API_KEY_KEY_ID=######
APP_STORE_CONNECT_API_KEY_ISSUER_ID=######
APP_STORE_CONNECT_API_KEY_KEY=./fastlane/AuthKey_######.p8
버전 관리와 빌드, 업로드를 자동화함.
default_platform(:ios)
platform :ios do
desc "PROD 앱 배포"
lane :release do
# 1. 버전 펌핑 (예: 2.3.9 -> 3.0.0)
# ... (버전 파싱 및 Plist 수정 로직)
# 2. Tuist 프로젝트 생성
Dir.chdir(PROJECT_ROOT) do
sh("mise", "exec", "--", "tuist", "generate")
end
# 3. 빌드 및 IPA 추출
build_app(
scheme: "JOBIS-DSM-iOS-v2-PROD",
export_method: "app-store",
# ...
)
# 4. App Store 업로드
upload_to_app_store(
skip_screenshots: true,
submit_for_review: false,
# ...
)
end
end
로컬 Jenkins를 외부(GitHub)에서 접근할 수 있도록 ngrok을 사용함.
nohup ngrok http 8080 --log stdout > /tmp/ngrok.log 2>&1 &https://[ngrok-url]/github-webhook/GitHub webhook failed: 404 Not FoundPhaseScriptExecution GoogleService-Info.plist failed.gitignore 처리된 파일이 Jenkins Workspace에 없음.cp 명령어로 복사하도록 수정함.no such module 'ProjectDescriptionHelpers'Tuist.swift) 삭제 및 캐시 초기화(rm -rf ~/.cache/tuist).No signing certificate "iOS Distribution" foundOpenSSL::PKey::ECError: invalid curve nameexport LC_ALL=en_US.UTF-8 추가.기존 프로젝트 Tuist 버전이 3.x.x 였기때문에 호환성 문제가 있어 4.xx대로 마이그레이션 진행, 마이그레이션 시 에러 발생
no such module 에러
ngrok의 한계와 대안
현재 가장 큰 걸림돌은 ngrok의 연결 불안정성
학교 와이파이 환경이나 맥북이 절전 모드에 들어가면 터널링이 끊김
세션 만료로 인해 URL이 바뀌면 Webhook 설정을 매번 변경해야 하는 번거로움이 존재
협업 시 다른 팀원이 PR을 날렸을 때 내 로컬 서버가 꺼져있으면 CI가 돌지 않는 치명적인 문제
해결 방안: Cloudflare Tunnel
이를 해결하기 위해 다음 스텝으로 Cloudflare Tunnel 도입을 고려하고 있습니다. ngrok보다 연결이 안정적이고 도메인 고정이 용이하여 로컬 CI/CD 환경의 단점을 보완해 줄 것으로 기대합니다.