또 하나의 배포 채널 — Mac App Store
지난 시리즈에서는 macOS 앱을 Developer ID로 직접 배포하는 사전 설정을 다뤘습니다. 인증서, 공증, Sparkle 자동 업데이트, 업데이트 피드 호스팅까지 갖추면, App Store를 거치지 않고 .dmg 파일을 직접 내려받게 할 수 있었습니다.
이번 시리즈는 같은 앱을 **Mac App Store(MAS)**에도 올리기 위한 사전 설정을 다룹니다. 두 배포 방식은 양자택일이 아닙니다. 하나의 앱을 직접 배포 채널과 App Store 채널 양쪽으로 동시에 운영할 수 있습니다. App Store는 결제·환불·검색 노출을 Apple이 대신 처리해 주고 사용자 신뢰도가 높다는 장점이 있어, 직접 배포와 병행하는 경우가 많습니다.
이 시리즈는 앱이 이미 Developer ID로 직접 배포되고 있고, Sparkle 자동 업데이트가 들어가 있다는 전제에서 출발합니다. 그 설정은 Developer ID 직접 배포 시리즈 1편부터 다뤘습니다. 아직 읽지 않았다면 먼저 보고 오는 것을 권합니다.
왜 MAS는 “또 하나의 타겟"이 필요한가
직접 배포 앱에는 Sparkle 자동 업데이트가 들어 있습니다. 앱이 스스로 업데이트 피드를 확인하고, 새 버전을 내려받아 자기 자신을 교체하는 기능입니다.
그런데 Mac App Store의 심사 규정은 이런 동작을 금지합니다. App Store에 올라간 앱은 외부에서 코드를 내려받아 자기 자신을 갱신해서는 안 되며, 업데이트는 오직 App Store를 통해서만 이루어져야 합니다. 즉 MAS에 올릴 빌드에는 Sparkle이 들어 있으면 안 됩니다.
하지만 직접 배포용 빌드에는 Sparkle이 반드시 있어야 합니다. 같은 빌드 결과물로 두 요구를 동시에 만족시킬 수는 없습니다. 그래서 해법은 이렇습니다.
같은 코드베이스, 두 개의 빌드 타겟.
| 채널 | 빌드 타겟 | Sparkle | 배포처 |
|---|---|---|---|
| Developer ID | FocusTimer | 포함 | 직접 배포 (.dmg) |
| Mac App Store | FocusTimer MAS | 제외 | App Store |
소스 코드는 하나로 공유하되, 빌드 타겟을 둘로 나눠 한쪽에서만 Sparkle을 링크하는 것입니다. 이 시리즈는 그 두 번째 타겟(FocusTimer MAS)을 만드는 사전 설정을 다룹니다.
이 시리즈에서 만드는 것
3편에 걸쳐 다음을 갖추게 됩니다.
- (1편, 이 글) MAS 전용 Bundle ID 등록 + Xcode 빌드 타겟 복제
- (2편) 두 채널을 가르는 설정 파일(entitlements·Info.plist)과 코드 분기
- (3편) 업로드용
ExportOptions-MAS.plist+ App Store Connect 등록 + 빌드 검증
시리즈를 마치면 다음이 갖춰져 있어야 합니다.
- Apple Developer Portal에 등록된 MAS 전용 Bundle ID
- Sparkle 의존성이 빠진
FocusTimer MAS빌드 타겟 - MAS 전용 entitlements 파일과 Info.plist 파일
-
#if canImport(Sparkle)로 분기된 코드 - App Store 업로드용
ExportOptions-MAS.plist
예시 앱 — FocusTimer
지난 시리즈와 동일하게, 가상의 macOS 앱 FocusTimer(집중 시간을 관리하는 간단한 타이머 앱)를 예로 듭니다. FocusTimer, 번들 식별자 com.example.FocusTimer, Team ID ABCDE12345 등은 모두 예시 값입니다. 실제로는 본인 앱과 계정 정보로 바꿔 사용하세요.
이 글은 Xcode 26 기준으로 작성했습니다.
1단계 — MAS 전용 Bundle ID 등록
왜 별도 Bundle ID인가
직접 배포 채널의 Bundle ID가 com.example.FocusTimer라면, MAS 채널에는 다른 Bundle ID를 씁니다.
| 채널 | Bundle ID |
|---|---|
| Developer ID | com.example.FocusTimer |
| Mac App Store | com.example.FocusTimer.mas |
기존 Bundle ID 뒤에 .mas를 붙이는 식입니다. 왜 굳이 나눌까요?
- 한 대의 Mac에 두 채널이 공존 가능 — Bundle ID가 같으면 macOS가 두 앱을 같은 앱으로 인식해 충돌합니다. 다르면 직접 배포판과 App Store판을 한 머신에 동시에 깔아 둘 수 있습니다(개발·테스트에 유용).
UserDefaults도메인 분리 — 설정 저장 공간이 Bundle ID 기준으로 나뉘므로, 두 채널의 설정이 섞이지 않습니다.- Launch Services 충돌 회피 — macOS가 “이 앱을 열어라” 같은 요청을 어느 앱에 보낼지 결정할 때 생기는 혼선을 막습니다.
직접 배포 채널의 Bundle ID는 그대로 유지하세요. 기존 사용자를 식별하는 기준이므로 바꾸면 안 됩니다. MAS용으로 새 ID를 하나 더 등록하는 것입니다.
등록 절차
- developer.apple.com/account에 로그인 (Apple Developer Program 계정)
- Certificates, Identifiers & Profiles → 왼쪽 메뉴 Identifiers →
+버튼 - App IDs 선택 → Continue → 유형은 App → Continue
- Description: 알아보기 쉬운 이름 입력 (예:
FocusTimer MAS) - Bundle ID:
- Explicit(명시적) 선택
- 입력값:
com.example.FocusTimer.mas
- Capabilities: 앱이 쓰지 않는 기능은 모두 끕니다. iCloud, 푸시 알림, 인앱 결제, Sign in with Apple 등을 쓰지 않는다면 아무것도 켜지 않아도 됩니다. (App Sandbox는 Xcode 측 entitlements가 처리하므로 여기서 켤 필요 없습니다.)
- Continue → Register
검증
Identifiers 목록에 FocusTimer MAS — com.example.FocusTimer.mas 항목이 보이면 등록 완료입니다.
2단계 — Xcode 빌드 타겟 복제
이제 Xcode 프로젝트에 FocusTimer MAS 빌드 타겟을 만듭니다. 가장 빠른 방법은 기존 타겟을 복제하는 것입니다.
2-1. 타겟 복제(Duplicate)
- Xcode에서
FocusTimer.xcodeproj열기 - Project navigator(왼쪽 패널) 맨 위의 프로젝트 아이콘(파란색) 클릭
- 가운데 편집 영역의 TARGETS 목록에서
FocusTimer우클릭 → Duplicate - 대화상자가 뜨면 Duplicate Only 선택
- 새 타겟 이름이
FocusTimer copy로 생깁니다. 이름을 더블 클릭해 **FocusTimer MAS**로 변경 - “새 Scheme을 추가할까요?” 프롬프트가 뜨면 Activate(또는 나중에 Manage Schemes에서 추가)
2-2. Bundle ID 즉시 변경
복제 직후 가장 먼저 할 일은 Bundle ID 교체입니다. 그대로 두면 두 타겟이 같은 ID를 갖게 됩니다.
TARGETS → FocusTimer MAS → General → Identity → Bundle Identifier를 1단계에서 등록한 값으로 바꿉니다.
com.example.FocusTimer.mas
2-3. 핵심 함정 — Duplicate가 남기는 “정리 부담”
여기가 이 편에서 가장 까다로운 부분입니다. Xcode의 Duplicate Target은 **“안전한 기본값”**으로 동작하는데, 그 안전함이 오히려 손이 가는 뒷정리를 남깁니다. 복제 직후 다음 네 가지를 점검해야 합니다.
① 새 타겟이 소스 파일을 포함하도록 만들기
Duplicate는 안전을 위해 새 타겟에서 모든 소스 파일을 자동 제외합니다. 그대로 두면 FocusTimer MAS 타겟은 빈 껍데기라 빌드해도 아무 코드가 들어가지 않습니다. 새 타겟이 기존 소스 파일들을 다시 포함하도록 멤버십을 정리해야 합니다.
② MAS 타겟에서 Sparkle 의존성 제거
Duplicate는 원본의 Swift Package 의존성을 그대로 복제합니다. 그래서 FocusTimer MAS 타겟에도 Sparkle이 딸려 옵니다. 이 시리즈의 출발점이 바로 “MAS 빌드에는 Sparkle이 없어야 한다"였으므로, MAS 타겟의 패키지 의존성 목록과 Frameworks 빌드 단계에서 Sparkle을 제거합니다.
③ 임시 Info.plist 파일 정리
Duplicate는 FocusTimer copy-Info.plist 같은 임시 Info.plist 파일을 만들어 둡니다. 우리는 2편에서 MAS 전용 Info.plist를 따로 만들 것이므로, 이 임시 파일들은 프로젝트 참조와 실제 파일 모두 삭제합니다.
④ MAS 타겟 빌드 설정 갱신
번들 식별자, Info.plist 경로, entitlements 경로 등 빌드 설정을 MAS용으로 맞춰야 합니다. 이 작업은 2편에서 전용 설정 파일들을 만든 뒤 한꺼번에 다룹니다.
①·②는 Xcode UI의 타겟 멤버십·패키지 의존성 화면에서 대부분 처리할 수 있지만, 복제 과정에서 남은 중복 참조가 깔끔하게 지워지지 않으면 프로젝트 파일(
.pbxproj)을 직접 들여다봐야 할 때도 있습니다. 정리가 끝났다고 판단되면 두 타겟을 각각 빌드해 보고, MAS 타겟 빌드에서import Sparkle이 깨지는지 확인하는 것이 가장 확실한 검증입니다(2편에서 이 코드 분기를 다룹니다).
1편 정리
여기까지 따라왔다면 이제 다음을 갖췄습니다.
- ✅ Apple Developer Portal에 등록된 MAS 전용 Bundle ID(
com.example.FocusTimer.mas) - ✅ 복제로 생성한
FocusTimer MAS빌드 타겟 - ✅ 복제가 남긴 정리 부담(소스 멤버십·Sparkle 의존성·임시 파일)에 대한 이해
타겟이라는 “그릇"은 만들어졌습니다. 하지만 아직 이 타겟은 사실상 FocusTimer의 복사본일 뿐, 진짜로 “MAS용"이 되지는 못했습니다. MAS 빌드는 직접 배포 빌드와 다른 권한(entitlements), 다른 Info.plist를 써야 하고, 코드도 Sparkle이 없을 때 깨지지 않도록 갈라져야 합니다.
다음 편에서는 그 두 채널을 가르는 설정 파일과 코드 분기를 다룹니다.