N개 iOS 앱 출시 운영: 서명 자산과 Fastlane 자동화
- iOS 출시 운영 구분은 팀 공통 자산, 앱별 등록 정보, 출시별 산출물, 배포 자동화의 책임 분리 체계
- 여러 앱이 같은 Apple Developer Team에 속할 때 인증서와 API 키를 공유하고, Bundle ID와 provisioning profile을 앱별로 분리하는 다중 앱 운영 모델
- Fastlane이 반복적인 서명·빌드·업로드를 실행하되 Apple 권한·Capability·심사 판단을 대신하지 않는 자동화 경계
이 구조가 필요한 이유
- 인증서, API 키, Bundle ID, profile을 모두 같은 종류의 설정으로 취급하면 앱이 늘어날수록 소유자와 변경 시점이 불명확해진다.
- API 키나 Distribution Certificate처럼 팀에 귀속된 자산은 공유하고, Bundle ID나 스토어 정보처럼 앱에 귀속된 자산은 분리해야 한다.
- Associated Domains처럼 Capability가 바뀐 경우에는 코드·Developer Portal·provisioning profile이 모두 같은 상태여야 배포 서명이 성공한다.
AS-IS
flowchart LR A[개발자] --> B[인증서, Bundle ID, Profile을 매번 수동 확인] B --> C{누락 또는 만료?} C -->|예| D[빌드/업로드 실패] C -->|아니오| E[수동 빌드 및 업로드]
TO-BE
flowchart LR A[개발자: 앱 등록·Capability·출시 판단] --> B[Fastlane: 서명 동기화] B --> C[Fastlane: IPA 빌드] C --> D[Fastlane: TestFlight 또는 ASC 업로드] D --> E[개발자: 심사 제출·출시]
큰 그림: 개발자 준비 → 서명 자산 → Fastlane 실행
flowchart TB subgraph Manual[1. 개발자가 직접 준비하는 작업] TeamOnce[팀 최초 1회<br/>Developer Team·권한·계약<br/>API Key·Match 저장소 생성] AppOnce[앱별 최초 1회<br/>App ID / Bundle ID 등록<br/>Capability·entitlements·ASC 앱 레코드 설정] ReleaseInput[출시마다<br/>코드·버전·빌드 번호·테스트<br/>메타데이터·심사 정보 준비] TeamOnce --> AppOnce --> ReleaseInput end subgraph Assets[2. 설정·서명 자산 · Git·JSON·Apple 서버에 보관] SharedAssets[팀 최초 1회 후 N개 앱 재사용<br/>API Key JSON/CI Secret · Match Git<br/>Distribution Certificate] AppProfiles[앱별로 생성·보관<br/>각 Bundle ID의 App Store Profile<br/>암호화된 Match Git에 동기화] SharedAssets --> AppProfiles end subgraph Fastlane[3. Fastlane 자동화] Sync[sync_signing<br/>인증서·Profile 생성 및 동기화] Refresh[refresh_signing<br/>Capability 변경 후 Profile 재생성] Build[IPA 빌드·서명] Beta[TestFlight 업로드] Release[App Store Connect 빌드 업로드] end TeamOnce -->|팀 공통 자산 생성| SharedAssets AppOnce -->|앱별 profile의 전제 조건| AppProfiles ReleaseInput -->|빌드 입력| Build SharedAssets -->|인증| Sync AppProfiles -->|서명| Build Sync -->|동기화 완료| Build AppOnce -. Capability 변경 .-> Refresh Refresh --> Sync Build --> Beta Build --> Release classDef teamOnce fill:#EDE9FE,stroke:#7C3AED,color:#4C1D95; classDef appOnce fill:#FFE4E6,stroke:#E11D48,color:#881337; classDef releaseInput fill:#FEF3C7,stroke:#D97706,color:#78350F; classDef asset fill:#DBEAFE,stroke:#2563EB,color:#1E3A8A; classDef automated fill:#DCFCE7,stroke:#16A34A,color:#14532D; class TeamOnce teamOnce; class AppOnce appOnce; class ReleaseInput releaseInput; class SharedAssets,AppProfiles asset; class Sync,Refresh,Build,Beta,Release automated;
- 보라색: 팀에서 최초 한 번 설정하고 다음 앱에도 재사용하는 개발자 직접 작업
- 분홍색: 새 앱마다 최초 한 번 추가하는 개발자 직접 작업
- 노란색: 출시마다 개발자가 준비·판단하는 작업
- 초록색: Fastlane이 반복 실행을 자동화하는 작업
- 파란색: 팀 또는 앱별로 보관·생성되는 자산
- 팀 최초 1회: 같은 Apple Developer Team의 다음 앱에서도 재사용하는 작업
- 앱별 최초 1회: 새 앱을 등록하거나 Capability를 처음 추가할 때 필요한 작업
- 출시마다: Fastlane이 업로드한 뒤에도 개발자가 매 버전 결정해야 하는 작업
이 문서의 세 구분
여기서 “구분”은 코드 아키텍처의 계층이 아니라, 누가 소유하고 언제 관리하는가를 기준으로 묶은 운영 단위다.
1. 개발자가 직접 준비하는 작업
Apple Developer Portal과 App Store Connect에서 사람의 권한과 판단이 필요한 작업이다. 팀 설정은 한 번 해두면 N개 앱에서 재사용하고, App ID·Capability·앱 레코드는 새 앱마다 추가한다. 코드·버전·테스트·심사 판단은 출시마다 직접 준비한다.
2. 설정·서명 자산
1번 작업의 결과로 생기는 인증 정보와 서명 재료다. Fastlane이 이 자산을 저장하는 것은 아니다. Fastlane은 각 자산을 읽어 인증서와 profile을 동기화하고 빌드를 서명한다.
자산은 한 곳에만 두지 않는다. 용도에 따라 일반 프로젝트 Git, 암호화된 Match Git, Git에 커밋하지 않는 JSON/CI secret, Apple 서버로 나뉜다.
| 자산 | 관리 형태 | 실제 보관 위치 | 공유 범위 |
|---|---|---|---|
| Fastlane 설정 | Fastfile, Appfile, Matchfile, 배포 스크립트 등의 일반 텍스트 파일 | 각 앱의 프로젝트 Git | 앱별, 소스 코드와 함께 관리 |
| App Store Connect API Key | AppStoreConnectKey.json 또는 CI secret/환경변수 | 로컬 비밀 저장소 또는 CI secret. 일반 Git에는 커밋하지 않음 | 같은 App Store Connect 조직에서 재사용 가능 |
| Distribution Certificate와 private key | Match가 암호화한 인증서 파일 | Apple Developer Portal 및 암호화된 Match Git 저장소 | 같은 Apple Developer Team에서 재사용 |
| App Store Provisioning Profile | Match가 암호화한 profile 파일 | Apple Developer Portal 및 암호화된 Match Git 저장소 | Bundle ID별로 분리 |
| App Store Connect 앱 레코드·메타데이터 | Apple 서비스의 관리 데이터 | App Store Connect 서버 | 앱별로 분리 |
즉, 프로젝트 Git에는 Fastlane 설정과 실행 스크립트를 넣고, Match Git에는 암호화된 서명 재료를 넣는다. AppStoreConnectKey.json처럼 private key를 담은 JSON은 Git에 넣지 않고 로컬 비밀 저장소나 CI secret으로 주입한다. App ID, Capability, 앱 레코드처럼 Apple이 소유하는 설정은 Apple 서버에 남는다.
3. Fastlane 자동화
준비된 2번 자산을 사용해 반복적인 배포 절차를 재현한다. Match로 인증서와 profile을 동기화하고, IPA를 빌드·서명한 뒤 TestFlight 또는 App Store Connect에 업로드한다. App ID 등록, Capability 활성화, 심사 제출 여부와 공개 출시는 개발자 판단으로 남는다.
개발자가 실제로 따르는 iOS 출시 흐름
공식 절차와 대조하면 App ID·Capability가 먼저여야 한다. 그 다음 App Store Connect 앱 레코드와 App Store profile은 서로 선후 의존하지 않지만, 둘 다 빌드 업로드 전에 준비되어야 한다. 앱 레코드는 이미 등록된 Bundle ID를 선택하며, Capability를 바꾸면 그 App ID를 담은 기존 profile은 무효가 된다. Apple: 신규 앱 레코드, Apple: Capability 변경
아래의 autonumber는 권장 운영 순서를 나타낸다. 보라색 블록은 팀 최초 한 번, 분홍색 블록은 새 앱마다 한 번, 노란색 블록은 매 출시마다 반복한다.
sequenceDiagram autonumber actor Developer as 개발자 participant Portal as Apple Developer Portal participant ASC as App Store Connect participant Project as 앱 프로젝트 Git participant Match as 암호화된 Match Git participant Fastlane as Fastlane rect rgb(237, 233, 254) Note over Developer,Match: 팀 최초 1회 · 다음 앱에도 재사용 Developer->>ASC: API 접근 권한 요청·Team API Key 생성 Developer->>Match: private 저장소·접근 권한·암호 설정 end rect rgb(255, 228, 230) Note over Developer,Fastlane: 새 앱마다 최초 1회 Developer->>Portal: Explicit App ID 등록·Capability 활성화 Note over Portal,Fastlane: 앱 레코드와 최초 signing은 순서 무관 · 업로드 전 둘 다 필요 Developer->>ASC: 등록된 Bundle ID로 앱 레코드 생성 Developer->>Project: Fastlane 설정·배포 스크립트 커밋 Developer->>Fastlane: 최초 signing 동기화 실행 Fastlane->>Portal: 인증서·App Store profile 생성 또는 갱신 Fastlane->>Match: 서명 재료를 암호화해 저장 end rect rgb(254, 243, 199) Note over Developer,ASC: 출시마다 반복 Developer->>Project: 코드·버전·테스트·메타데이터 준비 Developer->>Fastlane: beta 또는 release 실행 Fastlane->>Match: 인증서·profile 동기화 Fastlane->>Fastlane: IPA 빌드·서명 Fastlane->>ASC: 빌드 업로드 Developer->>ASC: TestFlight 검증·심사 제출·공개 결정 end
- 팀 최초 1회: Match Git에는 암호화된 certificate·private key·profile을 두고, API private key는
AppStoreConnectKey.json또는 CI secret으로 보관한다. Team API Key는 같은 App Store Connect 조직의 모든 앱에 적용된다. JSON 파일과 Match 암호는 일반 프로젝트 Git에 커밋하지 않는다. Apple: Team API Key, Fastlane Match - 새 앱 최초 1회: App ID를 먼저 등록하고 Capability를 활성화한다. 이후 앱 레코드 생성과 최초 signing은 어느 쪽을 먼저 해도 되지만, 업로드 전에 모두 완료해야 한다. 앱 프로젝트에
Fastfile,Appfile,Matchfile, 배포 스크립트를 추가하고 최초 signing 동기화를 실행하면 Match가 필요한 인증서·profile을 생성하고 저장소에 보관할 수 있다. - 매 출시: Fastlane은 Match의 서명 재료를 동기화한 후 빌드·서명·업로드한다. 심사 제출·출시 방식 결정은 App Store Connect에서 사람이 수행한다. Capability를 새로 추가하거나 변경했다면, 이 반복 단계 전에 profile을 재생성해야 한다.
팀 공통 설정·서명 자산
같은 Apple Developer Team에 속한 N개 앱이 함께 사용하는 자산이다. 앱별로 복제하지 않으며, 접근 권한과 보안 정책을 팀 단위로 관리한다.
| 컴포넌트 | 공유 범위 | 관리 주기 | 역할 | 개발자가 직접 관리할 일 |
|---|---|---|---|---|
| Apple Developer Team | 같은 개발 조직 | 팀 최초 1회 | Certificates, Identifiers & Profiles와 코드 서명의 소유 주체 | 프로그램 가입, 계약 동의, 구성원 권한 관리 |
| App Store Connect 조직 | 같은 판매자 조직 | 팀 최초 1회 | 앱 레코드, TestFlight, 심사, 판매 정보의 소유 주체 | 사용자 역할, 세금·계약, 앱 접근 권한 관리 |
| App Store Connect API Key | 같은 App Store Connect 조직 | 팀 최초 1회 후 재사용 | Fastlane이 Apple 서버에 인증하여 빌드·메타데이터 작업을 수행하는 비밀 키 | 생성, 최소 권한 선택, 안전한 보관, 유출 시 폐기 |
| Match 인증서 Git 저장소 | 같은 Apple Developer Team | 팀 최초 1회 후 재사용 | 인증서 private key와 Bundle ID별 profile을 암호화해 팀이 공유하는 저장소 | 최초 저장소 생성, 접근 권한, 암호 관리 |
| Apple Distribution Certificate | 같은 Apple Developer Team | 팀 공통, 만료 시 갱신 | App Store/TestFlight용 IPA가 팀 소유임을 증명하는 서명 신원 | 만료·폐기 정책 확인, Match 접근 권한 유지 |
App Store Connect API Key의 의미
AppStoreConnectKey.json은 보통 다음 세 가지를 파일로 묶은 표현이다.
{
"key_id": "KEY_ID",
"issuer_id": "ISSUER_ID",
"key": "-----BEGIN PRIVATE KEY----- ..."
}프로젝트는 같은 정보를 환경변수로 주입하거나, Git에 넣지 않는 로컬 AppStoreConnectKey.json 파일로 참조할 수 있다. 두 방식의 역할은 같고, 차이는 비밀 키를 로컬 JSON 파일로 둘지 실행 환경의 secret으로 둘지다.
- Team API Key는 같은 App Store Connect 조직의 모든 앱에 적용되는 팀 공통 키다.
- Individual API Key는 해당 사용자에게 부여된 앱 접근 권한과 역할을 따른다.
- p8 private key는 한 번만 내려받을 수 있으므로 소스 저장소에 커밋하지 않는다.
앱별 등록·서명 자산
각 앱의 정체성과 Apple 기능 사용 여부를 정의한다. 같은 팀 안에서도 다른 앱과 공유하면 안 되는 영역이다.
| 컴포넌트 | 앱별 분리 이유 | 관리 주기 | 역할 | 개발자가 직접 관리할 일 |
|---|---|---|---|---|
| App ID / Bundle ID | 설치·푸시·서명·스토어 대상을 구분하는 고유 식별자 | 앱 최초 1회 | 예: com.example.product가 하나의 앱을 식별 | Apple Developer Portal에서 Explicit App ID 등록 |
| Capability | 앱이 사용할 Apple 서비스의 허가 목록 | 앱 최초 또는 기능 추가 시 | Associated Domains, Push Notifications, Sign in with Apple 등의 entitlement 근거 | Portal에서 활성화하고 Xcode entitlements와 앱 코드를 함께 변경 |
| App Store Connect 앱 레코드 | 고객에게 보이는 하나의 상품 단위 | 앱 최초 1회 | 이름, SKU, 기본 언어, 카테고리, 가격, 심사 이력의 기준 | App Store Connect에서 신규 앱 생성 및 상품 정보 설정 |
| App Store Provisioning Profile | 특정 Bundle ID·Capability·Distribution Certificate 조합의 서명 허가서 | 앱별 자동 동기화, Capability·인증서 변경 시 갱신 | IPA가 해당 앱의 허용된 entitlement를 포함하도록 제한 | 일반적으로 Match가 생성·동기화, 수동 생성은 자동화가 없을 때만 사용 |
| Store Metadata | 앱 상세 페이지의 판매 정보 | 앱 최초 후 출시마다 갱신 가능 | 이름, 설명, 키워드, 스크린샷, 지원 URL, 심사 정보 | App Store Connect에서 관리하거나 별도 Fastlane metadata 설정으로 자동화 |
Capability 변경 규칙
Capability는 단순히 코드에 entitlement를 추가하는 것으로 끝나지 않는다.
- Developer Portal의 App ID에서 Capability를 활성화한다.
- Xcode entitlements와 앱 코드를 변경한다.
- 기존 App Store profile을 새 Capability 기준으로 재생성한다.
- 새 profile로 IPA를 다시 서명한다.
예를 들어 Associated Domains를 추가하면 applinks: 도메인 변경은 코드/entitlements 변경 대상이고, Capability를 처음 켠 시점에는 profile 재생성이 필요하다. 이후 도메인 값만 바꾸는 경우에도 entitlements가 바뀌므로 새 서명 결과를 TestFlight에서 검증해야 한다.
출시별 산출물과 개발자 판단
한 번의 버전 출시마다 새로 준비하거나 확인해야 하는 항목이다. Fastlane이 파일 생성과 업로드를 수행해도, 제품 품질과 판매·심사 판단은 개발자 책임이다.
| 컴포넌트 | 역할 | 개발자가 직접 관리할 일 |
|---|---|---|
| 코드·테스트 | 실제 배포할 앱 동작 | 기능·회귀·개인정보·정책 검증 |
| 버전·빌드 번호 | App Store Connect가 빌드를 구분하는 버전 식별자 | pubspec.yaml 버전 갱신, 같은 빌드 번호 재업로드 금지 확인 |
| IPA | 배포 가능한 서명 산출물 | 빌드 결과와 대상 Bundle ID 확인 |
| TestFlight 빌드 | 내부/외부 테스터에게 배포하는 검증 대상 | 테스터 그룹, 테스트 범위, 피드백 확인 |
| App Store 버전 | 고객에게 제출할 출시 후보 | 메타데이터, 스크린샷, 연령 등급, 심사 메모, 빌드 선택 |
| 심사·출시 결정 | 공개 배포의 최종 판단 | 심사 제출, 거절 대응, 수동/자동 출시 선택 |
Fastlane: 반복 작업을 실행하는 자동화 도구
Fastlane은 Apple 계정을 대신 만드는 도구가 아니다. 이미 준비된 권한과 설정을 사용해 반복되는 배포 작업을 명령으로 재현하는 도구다.
일반적인 iOS Fastlane 구성은 다음을 자동화한다.
sync_signing: Match 저장소에서 인증서와 각 Bundle ID의 App Store profile을 동기화refresh_signing: Associated Domains 같은 Capability 변경 후 profile을 강제 재생성beta: Flutter IPA를 빌드하고 TestFlight에 업로드release: Flutter IPA를 빌드하고 App Store Connect에 업로드
release는 빌드를 App Store Connect에 올리는 명령이다. 메타데이터·스크린샷 업로드, 심사 제출, 공개 출시는 프로젝트 Fastlane 설정에 따라 별도로 자동화하거나 수동으로 수행한다.
신규 앱 최초 설정 체크리스트
- App Store Connect에서 신규 앱 레코드를 생성한다.
- Developer Portal에서 Explicit App ID와 필요한 Capability를 등록한다.
- 팀 공통 Match Git 저장소에 접근 가능한지 확인하고
MATCH_GIT_URL을 설정한다. - 같은 App Store Connect 조직에서 API Key를 만들거나, 해당 앱에 접근 가능한 기존 키를 사용한다.
- 로컬 또는 CI secret에
ASC_KEY_ID,ASC_ISSUER_ID,ASC_KEY_CONTENT_BASE64,MATCH_GIT_URL을 설정한다. sync_signing으로 앱별 profile을 만든 뒤, 프로젝트의betalane으로 TestFlight 업로드 및 실기기 검증을 수행한다.
매 출시 실행 체크리스트
- 코드·테스트·버전·빌드 번호를 확정한다.
- 새 Capability가 있으면 Portal, entitlements, profile이 모두 같은 상태인지 확인한다.
- 스토어 메타데이터와 심사 정보를 App Store Connect에서 갱신한다.
- 프로젝트의
betalane으로 TestFlight 검증을 수행한다. - 프로젝트의
releaselane으로 App Store Connect에 빌드를 올린다. - App Store Connect에서 빌드를 선택하고 심사 제출 및 출시 방식을 결정한다.