빌드를 App Store로 보내는 길
1편에서는 MAS 빌드 타겟을, 2편에서는 두 채널을 가르는 설정 파일과 코드 분기를 만들었습니다. 이제 FocusTimer MAS 타겟은 App Store에 올릴 수 있는 형태가 되었습니다.
이 마지막 편에서는 그 빌드를 App Store Connect에 업로드하는 길을 마련하고, 두 채널이 앞으로도 깨지지 않도록 검증하는 방법까지 다뤄 시리즈를 마칩니다.
1·2편과 마찬가지로
FocusTimer,com.example.FocusTimer.mas, Team IDABCDE12345등은 모두 예시 값입니다.
1단계 — 업로드용 ExportOptions-MAS.plist
직접 배포 시리즈에서, 아카이브를 배포용으로 내보낼 때 xcodebuild -exportArchive 명령이 ExportOptions.plist를 읽는다고 했습니다. MAS 채널에는 별도의 내보내기 설정 파일이 필요합니다.
프로젝트의 릴리스 디렉토리에 ExportOptions-MAS.plist를 만듭니다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
<key>destination</key>
<string>upload</string>
<key>signingStyle</key>
<string>automatic</string>
<key>teamID</key>
<string>ABCDE12345</string>
</dict>
</plist>
각 키의 의미는 이렇습니다.
method=app-store— 이 빌드를 Mac App Store 채널용으로 내보낸다는 뜻입니다. 직접 배포 채널의developer-id와 대비됩니다.destination=upload—xcodebuild -exportArchive가 내보낸 결과물을 곧바로 App Store Connect에 업로드합니다. 예전처럼 Transporter나 별도 업로드 도구를 거칠 필요가 없습니다.signingStyle=automatic— Xcode가 Mac App Store 프로비저닝 프로파일과 Apple Distribution 인증서를 자동으로 매칭합니다.teamID— 본인의 Apple Developer Team ID.
이 파일을 한 번 만들어 두면 모든 MAS 릴리스에서 재사용합니다. 직접 배포용 ExportOptions.plist는 그대로 두고, 이 파일을 나란히 둡니다.
2단계 — App Store Connect 앱 레코드 등록
앱을 실제로 심사에 제출하려면 App Store Connect에 앱 **레코드(record)**가 있어야 합니다. 레코드는 앱의 이름·설명·스크린샷·가격 등 스토어에 표시될 모든 정보를 담는 그릇입니다.
레코드 생성은 실제 첫 제출 직전에 해도 됩니다. 사전 설정 단계에서는 “어떤 항목을 어떻게 정해야 하는지"만 미리 알아 두면 충분합니다. 특히 아래 Primary Language는 한 번 정하면 되돌리기 어려우므로 미리 신중히 결정해 두세요.
App Store Connect → My Apps → + → New App에서 다음을 입력합니다.
- Platform — macOS 선택
- Primary Language(기본 언어) — ⚠️ 가장 신중하게 정할 항목. 한 번 정하면 셀프 서비스로 바꾸기가 매우 어렵습니다. 앱을 여러 나라에 낼 계획이라면 **영어(English, U.S.)**를 기본 언어로 두는 경우가 많습니다. 기본 언어는 “특정 국가의 번역이 없을 때 보여줄 기준 언어"이기 때문입니다.
- App Name(앱 이름) — 기본 언어 기준 이름 (예:
FocusTimer). 다른 언어용 이름은 나중에 **언어별 현지화(localization)**로 따로 추가합니다. 예를 들어 한국어 사용자에게는 한국어 이름이, 일본어 사용자에게는 일본어 이름이 보이도록 할 수 있습니다. - Bundle ID — 드롭다운에서 **
com.example.FocusTimer.mas**를 선택합니다. 1편에서 등록해 둔 그 ID입니다. 직접 배포용 Bundle ID(com.example.FocusTimer)와 헷갈리지 마세요 — 반드시.mas가 붙은 쪽입니다. - SKU — 본인만 쓰는 내부 식별용 문자열입니다. 스토어에 노출되지 않으므로 자유롭게 정하면 됩니다 (예:
focustimer-mas-001).
앱 카테고리는 2편에서
Info.plist의LSApplicationCategoryType에 넣은 값과 일치시켜야 합니다. plist에public.app-category.productivity(생산성)를 넣었다면, App Store Connect에서도 카테고리를 “생산성"으로 골라야 심사 단계에서 불일치 경고가 뜨지 않습니다.
3단계 — 두 채널 빌드 검증
이제 하나의 코드베이스가 두 개의 빌드 타겟을 갖게 되었습니다. 여기에는 유지보수 비용이 따라옵니다. 평소에는 직접 배포 채널만 빌드하다 보면, MAS 타겟이 어느새 깨져 있어도 눈치채지 못합니다. 새 코드를 추가했는데 #if canImport(Sparkle) 분기를 빠뜨려, MAS 빌드만 컴파일에 실패하는 식입니다.
이를 막는 가장 확실한 방법은 직접 배포 릴리스를 만들 때마다 MAS 타겟도 함께 아카이브해 보는 것입니다. 빌드가 성공하면 두 채널의 분기가 멀쩡하다는 뜻입니다.
xcodebuild -project FocusTimer.xcodeproj -scheme "FocusTimer MAS" \
-configuration Release \
-destination 'platform=macOS' \
-archivePath build/FocusTimer-MAS.xcarchive \
archive
출력 마지막에 다음이 보이면 MAS 분기가 정상입니다.
** ARCHIVE SUCCEEDED **
이 명령은 릴리스 자동화 스크립트의 마지막 단계에 끼워 넣어, 매 릴리스마다 자동으로 돌게 해 두는 것을 권합니다. MAS 빌드를 실제로 App Store에 올리지 않는 시점이더라도, 아카이브가 성공하는지 확인하는 것만으로 두 채널 분기가 깨지지 않았음을 보장할 수 있습니다.
시리즈 정리 — MAS 출시 사전 설정 완료
3편에 걸친 Mac App Store 출시 사전 설정이 모두 끝났습니다. 이제 다음이 갖춰져 있습니다.
- ✅ (1편) MAS 전용 Bundle ID + 복제한
FocusTimer MAS빌드 타겟 - ✅ (2편) MAS 전용 entitlements·Info.plist + 빌드 설정 +
#if canImport(Sparkle)코드 분기 - ✅ (3편) 업로드용
ExportOptions-MAS.plist+ App Store Connect 레코드 등록 방법 + 두 채널 빌드 검증
이로써 하나의 macOS 앱이 직접 배포(Developer ID)와 Mac App Store 두 채널을 모두 갖추게 되었습니다. 핵심 구조를 다시 정리하면 이렇습니다.
- 같은 코드베이스를 공유하되, 두 개의 빌드 타겟으로 나눈다.
- 직접 배포 타겟은 Sparkle을 포함하고, MAS 타겟은 Sparkle을 제외한다.
- 둘의 차이는 별도의 entitlements·Info.plist·ExportOptions와
#if canImport(Sparkle)코드 분기로 표현된다.
처음 설정할 때는 손이 많이 가지만, 이 역시 한 번 갖춰 두면 계속 재사용되는 구조입니다. 이후로는 직접 배포 릴리스를 낼 때 MAS 빌드도 함께 아카이브해 분기가 살아 있는지만 확인하면 됩니다. 실제 App Store 제출(스크린샷·설명·심사 대응)은 이 사전 설정 위에서 진행하는 별도의 작업이며, 다른 글에서 다룰 주제입니다.