Swift Package Registry를 도입하기 전에 볼 것

Swift 의존성은 보통 Git 주소로 선언해요. 같은 의존성을 registry의 식별자로도 선언할 수 있어요.

Git 주소로 선언한 경우예요.

.package(url: "https://github.com/apple/swift-log.git", from: "1.5.0")

registry ID로 선언한 경우예요.

.package(id: "apple.swift-log", from: "1.5.0")

한 줄 차이지만 받아 오는 경로가 달라지고, 그에 따라 CI, 재현성, 사내 유통, 보안, Xcode 설정이 영향을 받아요. iOS 팀이 이 방식을 고를지 판단할 때 볼 것을 정리했어요. 특정 프로젝트에 적용한 기록은 아니고, resolve 시간을 잰 결과도 이 글에 없어요.

Git으로 받을 때의 부담

Git 주소로 받으면 처음 빌드할 때 저장소를 clone하고 tag를 보고 버전을 골라요. 릴리스 하나만 필요해도 Git 이력을 함께 다뤄요. 저장소가 크거나 의존 관계가 복잡하면 resolve가 느려질 수 있어요.

재현성에도 위험이 있어요. tag가 가리키는 대상은 바뀔 수 있어요. 저장소가 지워지거나 옮겨지면 Package.resolved를 갖고 있어도 빌드를 보장할 수 없어요.

망이 제한된 환경도 있어요. github.com이 막혀 있으면 clone과 CI가 실패할 수 있어요. 그런 곳에서는 보안팀이 보통 승인된 Artifactory, Git mirror, artifact 저장소를 거치라고 요구해요. registry를 쓰면 GitHub에 접속하지 않고 공급하는 구성을 생각할 수 있어요.

registry가 패키지를 주는 순서

registry의 식별자는 scope.package-name 꼴이에요. mona.LinkedList라면 scope가 mona이고 이름이 LinkedList예요.

.package(id: "mona.LinkedList", .upToNextMajor(from: "1.0.0"))

서비스 규격에 따른 주요 요청은 세 단계예요. 먼저 /{scope}/{name}에서 릴리스 목록을 받고, 그 버전의 Package.swift를 받고, /{scope}/{name}/{version}.zip에서 archive를 받아요. checksum과 서명을 확인할 때에는 릴리스의 metadata도 조회해요(사용 문서). HTTP로 archive를 내려 주는 방식이라서, 릴리스를 바꾸지 않는 것으로 운영할 수 있고 CDN이나 내부 저장소를 활용할 수 있어요. 소스 관리와 배포가 분리돼요. 이 배경은 SE-0292에 있어요.

개발하는 저장소를 옮길 필요는 없어요. GitHub, GitLab, GitHub Enterprise Server, 사내 Git에서 계속 개발할 수 있고, registry는 릴리스를 전달하는 역할만 해요.

설정이 놓이는 자리

기본 registry는 CLI로 지정해요.

swift package-registry set https://packages.example.com

설정은 프로젝트 범위와 사용자 범위에 저장돼요. 프로젝트 범위의 파일 위치예요.

.swiftpm/configuration/registries.json

사용자 범위의 파일 위치예요.

~/.swiftpm/configuration/registries.json

파일에는 기본 URL과 형식 버전이 들어가요.

{
  "registries": {
    "[default]": {
      "url": "https://packages.example.com"
    }
  },
  "version": 1
}

Swift 6.4의 도움말에서 set은 URL 하나를 받고 --global과 --scope 옵션이 있어요. 2026년 10월 4일에 --help로만 확인한 것이고, registry 서버에 접속하거나 설정을 바꿔 보지는 않았어요.

패키지 프로젝트라면 Package.swift에 ID로 선언하면 돼요. iOS 앱은 사정이 달라요. Xcode 앱 프로젝트에는 보통 Package.swift가 없고, project.pbxproj, workspace, Package.resolved로 의존성을 관리해요. CLI로 넣은 설정이 Xcode에 그대로 적용된다고 추정하지 말고, 설정이 실제로 어디에 저장되는지 확인해야 해요.

팀에서는 registry URL 설정을 공유하되 access token은 커밋하지 않아요. workspace나 .xcodeproj에서 설정 위치를 찾아 주고 mirrors.json을 만들어 주는 CLI나 GUI 도구를 두는 것도 방법이에요. 이것은 제 제안이고, 만들어 본 사례는 이 글에 없어요.

Git과 registry가 섞이면

가장 조심할 것은 같은 패키지가 두 경로로 들어오는 경우예요. 직접 의존성은 registry ID로 선언했는데, 다른 패키지가 같은 것을 Git 주소로 끌고 올 수 있어요.

직접 의존성이에요.

.package(id: "apple.swift-log", from: "1.5.0")

간접 의존성으로 들어오는 선언이에요.

.package(url: "https://github.com/apple/swift-log.git", from: "1.5.0")

origin이 다르면 identity가 따로 잡힐 수 있어요. 그러면 module이나 product가 중복되고 symbol이 충돌할 수 있어요.

이 둘을 이어 주는 수단이 /identifiers?url=이에요. Git 주소에 대응하는 registry ID를 조회해요. URL과 ID의 대응이 정확해야 해요. 한 패키지의 origin은 하나로 맞추는 것이 좋고, 선언을 ID로 바꾸는 동안에도 Git이나 mirror 경로를 유지하면서 단계별로 옮기기를 제안해요.

mirror는 다른 것이에요

mirror는 registry와 목적이 달라요. SE-0219의 mirror는 .package(url:) 선언을 그대로 두고, 가져오는 주소만 다른 Git으로 바꿔요.

원래 주소와 내부 Git 주소의 대응을 적은 예시예요. git.example.com 자리에 사내 Git 서버의 주소가 들어가요.

{
  "object": [
    {
      "original": "https://github.com/apple/swift-log.git",
      "mirror": "https://git.example.com/mirrors/apple/swift-log.git"
    }
  ],
  "version": 1
}

registry는 ID와 archive로 받는 경로이고, mirror는 Git을 다른 Git으로 바꾸는 경로예요. 망이 제한된 곳에서는 내부 mirror를 먼저 적용하고 그다음에 registry로 넘어가는 순서를 제안해요.

인증과 서명

비공개 registry에는 인증이 필요하고, login 명령으로 인증 정보를 저장할 수 있어요(SE-0378).

swift package-registry login https://packages.example.com --token "$TOKEN"

인증은 접근 권한에 관한 것이에요. 배포물이 진짜인지는 서명이 다뤄요. 서명은 발행자가 그 키를 갖고 있다는 것과 배포물이 바뀌지 않았다는 것의 증거예요. archive 응답에 실린 signature header 두 개(X-Swift-Package-Signature-Format, X-Swift-Package-Signature)를 확인하고, 서명과 인증서 체인을 검사해요. 서명이 검증됐다고 해서 그 발행자를 믿어도 된다는 뜻은 아니에요. 어떤 CA를 믿을지와 어떤 인증서가 어떤 scope를 발행할 수 있는지는 조직이 따로 정해요.

서명하려면 code signing용 X.509 인증서와 private key가 있어야 해요. macOS에서는 Keychain의 identity를 쓸 수 있고, CI에서는 키 파일과 인증서 체인 파일을 넘기는 방식도 가능해요(SE-0391). 아래 명령이 archive를 올리는 publish endpoint는 SE-0321에 정의돼 있어요.

swift package-registry publish mycompany.design-system 1.2.0 \
  --url https://packages.example.com \
  --private-key-path ./private-key.pkcs8.der \
  --cert-chain-paths ./leaf-cert.der ./intermediate.der ./root.der

login의 --token과 publish의 --url, --private-key-path, --cert-chain-paths가 Swift 6.4의 도움말에 있는 것까지는 확인했어요. 명령을 실제 서버에 실행하지는 않았어요. 이전 판에서 CI용으로 든 SWIFTPM_REGISTRY_TOKEN이라는 환경 변수는 도움말에 나오지 않았어요. main 브랜치의 사용 문서에는 이 변수가 인증 수단으로 적혀 있어요(2026년 10월 4일 확인). Swift 6.4가 이 변수를 지원하는지는 확인하지 못했으므로, 쓰기 전에 사용하는 toolchain에서 확인해야 해요.

조직 차원에서는 인증서마다 허용하는 scope와 package를 관리하고, 내부 CA를 운영한다면 SwiftPM의 trust root 설정을 팀에 배포해야 해요.

도입을 판단할 때

의존성이 적고 GitHub에 자유롭게 접속할 수 있다면 얻는 것이 크지 않을 수 있어요. 검토할 계기는 이런 것들이에요. resolve가 느려요. 사내 패키지가 많아요. 외부 접속이 제한돼 있어요. JFrog, Nexus, Artifactory 같은 저장소를 이미 운영해요. 고객사에 비공개 SDK를 전달해요. checksum, 서명, audit log에 대한 요구가 있어요.

확인은 아래 순서로 하기를 제안해요.

  1. 의존성 목록을 뽑아요.
  2. Package.resolved와 Xcode에 적힌 Git 주소를 봐요.
  3. CI에서 resolve에 걸리는 시간을 재요.
  4. GitHub, GitHubusercontent, 외부 binary URL이 허용되는지 확인해요.
  5. registry로 받을 것과 mirror로 받을 것을 나눠요.
  6. 같은 패키지가 Git 주소와 registry ID 두 경로로 함께 들어오는 의존 그래프가 없는지 점검해요.
  7. workspace와 .xcodeproj에서 설정이 놓이는 위치를 확인해요.
  8. token과 CI 변수를 어떻게 다룰지 정해요.
  9. 서명이 필요한지와 어떤 CA를 믿을지 정해요.
  10. 사내 저장소와 어떻게 연결할지 봐요.

패키지 승인, registry와 mirror 가운데 무엇을 쓸지, Xcode 설정의 공유, CI에서 허용할 외부 host는 팀이나 플랫폼 조직이 정책으로 정할 일이에요.

요청 순서, 설정 파일, 인증, 서명은 Swift Evolution 제안과 SwiftPM 문서에 있는 내용이에요. 도입 순서와 점검 목록은 그 위에 세운 제 해석이고, 적용해서 확인한 결과가 아니에요. Xcode 버전마다 설정이 놓이는 위치와 호환성은 확인하지 않았어요. 섞인 그래프의 실제 동작이나 registry로 바꾼 앞뒤의 속도를 확인한 결과도 이 글에 없어요. 문서 링크는 main 브랜치를 가리키므로 내용이 바뀔 수 있어요.