Files
2nd/10_Wiki/Topics/Coding/Mobile_TestFlight_Distribution.md
T
2026-05-10 22:08:15 +09:00

7.1 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
mobile-testflight-distribution Beta Distribution — TestFlight / Firebase / internal Coding draft B conceptual 2026-05-09 2026-05-09
mobile
beta
testflight
vibe-coding
language applicable_to
process
Mobile
TestFlight
Firebase App Distribution
internal beta
external beta
Play Store internal track

Beta Distribution

Production 전에 internal / external beta. TestFlight (iOS), Play Store internal track + Firebase App Distribution (Android). 빠른 iterate + early feedback.

📖 핵심 개념

  • Internal: 팀 (개발 / QA).
  • External: 선택 사용자 (beta tester).
  • Public beta: 모든 사용자 (open).
  • 매 build = beta → review.

💻 코드 패턴

TestFlight (iOS)

1. Xcode → Product → Archive
2. Distribute → App Store Connect
3. 처리 (5-30 min) → TestFlight
4. Internal: 100 테스터 (instant)
5. External: 10000 테스터 (Apple review 1-2 days)

TestFlight 그룹

Internal Group:
- 매 team member (App Store Connect access)
- 즉시 update
- 90 days 만료

External Group:
- 외부 사용자 (email + name)
- Apple review (첫 build)
- Public link 가능

Internal 추가

App Store Connect → Users and Access → Add user.
Role: Developer / Marketing / etc.
TestFlight access: ON.

External 추가 (email)

TestFlight tab → External Group → Add testers.
- Email
- 또는 public link (URL 하나)
- "Join beta" link
- 무한 invite (limit 10000)
- Limit 가능 (300 user 면 close)
- Link 노출 → 누구나 = OK

Build expiration

TestFlight build = 90 days 만료.

→ 매 90 days 새 build 필요.
또는 expiry 전 새 build (더 좋음).

What to test

TestFlight 가 "What to Test" 표시.

Markdown 가능.
- 새 feature
- Test 가 focus
- Known issue
- Feedback 요청

→ Tester 가 무엇 test 모름.

Feedback (TestFlight)

- Screenshot 캡처 (iOS shake / volume)
- 자동 attach + crash log
- Email 으로 form
- App Store Connect 에 표시

→ Email 보다 in-app feedback 친화.

Firebase App Distribution (Android / iOS)

# Setup
firebase login
firebase init appdistribution

# Distribute
firebase appdistribution:distribute app.apk \
  --app 1:1234567890:android:abcdef \
  --release-notes "Bug fixes" \
  --groups "qa-team"

→ iOS / Android 둘 다. Email + Firebase app.

Play Store Internal Track

Google Play Console → Testing → Internal testing.
- 100 user limit
- 즉시 (review X)
- Email 가입

Play Store Closed / Open Testing

Closed: external (1k+ tester)
Open: 모두 (Play Store 검색 가능, "Beta" 라벨)

→ Closed = staging.
Open = public beta.

Play Store track

internal → closed → open → production

매 단계 = 별 release.
Promote = 한 번에 다음 track 으로.

Fastlane (자동)

# Fastfile
lane :beta do
  match(type: 'appstore')  # cert
  build_app(scheme: 'MyApp')
  upload_to_testflight(
    skip_waiting_for_build_processing: true,
    changelog: "Beta update",
    distribute_external: true,
    groups: ['Beta Testers'],
  )
end
fastlane beta

→ Local 또는 CI 가 한 번에 archive + upload.

Android 자동 (Fastlane)

lane :playstore_internal do
  gradle(task: 'bundle', build_type: 'Release')
  upload_to_play_store(
    track: 'internal',
    aab: 'app/build/outputs/bundle/release/app-release.aab',
  )
end

CI (GitHub Actions)

- uses: actions/checkout@v4
- run: bundle install
- run: bundle exec fastlane beta
  env:
    APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_API_KEY }}

Code signing (iOS)

match (Fastlane):
- Cert + provisioning profile in private git repo
- 모든 dev / CI 가 같은 cert

→ "code signing 안 됨" 문제 해결.
fastlane match appstore

Build number 자동

# Fastfile
lane :beta do
  increment_build_number(xcodeproj: 'MyApp.xcodeproj')
  build_app
  upload_to_testflight
end

→ 매 beta = 새 build number (자동).

Versioning

1.2.3 (build 45)

Major.Minor.Patch + Build.

Build = 매 upload.
Patch = bug fix (1.2.3 → 1.2.4).
Minor = 새 feature (1.2.3 → 1.3.0).
Major = breaking (1.2.3 → 2.0.0).

Crash 추적 (beta)

// Sentry / Crashlytics
import Sentry
SentrySDK.start { o in
    o.dsn = "..."
    o.environment = "beta"
    o.tracesSampleRate = 1.0
}

→ Beta crash = critical signal.

Phased rollout (production)

App Store: phased release (7 days).
- Day 1: 1%
- Day 2: 2%
- Day 7: 100%

Play Store: staged rollout (X%).

→ Crash 발견 시 halt.

Rollback (Play Store 만)

Play Console → 옛 release 다시 publish.
→ 새 install 가 옛 version.

iOS = rollback 안 됨. 새 release 가 fix.

Beta 관리

- Tester 그룹 (segments)
- Feedback channel (Slack, Discord, email)
- 매 release = 매 변경 list
- Critical bug 가 아닌 = 다음 build

TestFlight invite UX

1. User 가 email click
2. TestFlight app 다운로드 (없으면)
3. 가입 + redeem code
4. Beta app install

→ 3 step 가 pain. Public link 가 더 부드러움.

Demo build (다른 ID)

Build configuration:
- Production: com.app
- Beta: com.app.beta
- Demo: com.app.demo

→ Tester 가 production 과 beta 동시 install.

LinkedIn / external review

일부 tester:
- Power user 가 좋음
- Diverse device (구 iPhone, 작은 storage)
- 자주 보고

→ 50명 < 5000 명 (engagement 가 중요).

Play Open testing → production

Open testing 의 user 가 production track 으로 자동 이동.
- "Beta" 라벨 사라짐
- 같은 update 받음

→ Soft launch.

비용

TestFlight: 무료 (Apple Developer $99/year 포함).
Firebase App Distribution: 무료 (15k user 까지).
Play Console: 무료 ($25 one-time).
Fastlane: 무료.

Beta 함정

- Build expiration 간과 (90 days)
- "What to test" 안 적음
- Crash 무시 (production 에 가져감)
- Tester 안 답 (ghost beta)
- 너무 잦은 build (test 불가)

🤔 의사결정 기준

단계 추천
Internal QA Internal group (TestFlight / Play internal)
External beta (작음) Closed group + email
Public beta Public link + Open testing
Crash 추적 Sentry / Crashlytics
자동화 Fastlane + match
Multi-platform Firebase App Distribution
Phased prod Phased release / staged rollout

안티패턴

  • Direct production: bug 가 모든 사용자.
  • Beta tester 0명: feedback 없음.
  • "What to test" 없음: tester 가 뭐 할지 모름.
  • Crash report 무시: production 가져감.
  • Manual upload: 매번 30 min.
  • Build version 가 같음: TestFlight reject.
  • Phased rollout 무시: 1% 가 100% 가능.

🤖 LLM 활용 힌트

  • TestFlight (iOS) + Play Internal Track (Android) = baseline.
  • Fastlane + match = 자동 + 안정.
  • Firebase App Distribution = cross-platform.
  • Crash + feedback channel 항상.

🔗 관련 문서