ABCApp Internal Architecture Note

ABCApp의 Package, Dependency, Target, Scheme, Configuration 흐름 정리

이 문서는 특정 iOS 앱 프로젝트를 기준으로, `packages`, `dependencies`, `target`, `scheme`, `build configuration`이 서로 어떻게 연결되는지 질문 중심으로 정리한 문서다.

Observed from local manifests and generated files Local study doc, no push assumption to company remote
학습 날짜

2026-04-10

처음 보는 사람용 아주 쉬운 설명

이 프로젝트를 레고 조립이라고 생각하면 쉽다. `package`는 부품 상자 목록이고, `dependency`는 어떤 부품을 실제로 쓰겠다는 선언이고, `target`은 실제 완성품 하나고, `scheme`은 그 완성품을 어떤 모드로 실행할지 정하는 버튼이다.

큰 흐름도

flowchart TD
    A["Package.swift (외부 SPM과 product 타입 정책을 모아 두는 파일)"]
    B["ABCAppPackages.swift (프로젝트가 인식할 로컬 패키지 목록 파일)"]
    C["ABCAppDependencies.swift (앱 타깃 의존성을 적는 파일)"]
    D["ABCAppProject.swift (앱 타깃 생성 규칙을 모아 둔 helper 파일)"]
    E["Project.swift (packages, targets, schemes를 최종 연결하는 파일)"]
    F["생성된 .xcodeproj (Tuist가 만들어 낸 Xcode 프로젝트 결과물)"]
    G["Target (앱, 테스트, 위젯, 확장 기능처럼 실제로 빌드되는 단위)"]
    H["Scheme (어떤 타깃을 어떤 설정으로 실행할지 정하는 실행 묶음)"]
    I["Build Configuration (Debug_Qa, Release_QA 같은 빌드 환경 이름)"]

    A --> C
    B --> E
    C --> D
    D --> E
    E --> F
    F --> G
    F --> H
    H --> I
    G --> I
        
한 줄로 줄이면: 패키지를 등록하고 -> 의존성을 정하고 -> 타깃을 만들고 -> 스킴으로 실행 방식과 설정을 고른다.
박스 이게 뭔지 이게 무엇인가
외부 SPM을 중앙에서 등록하고 product 타입 정책을 정하는 파일 중앙 설정 파일 프로젝트 전체에서 공통으로 쓰는 외부 라이브러리 목록과 static/dynamic 같은 링크 정책을 모아 두는 곳이다.
프로젝트가 알고 있어야 할 로컬 패키지 목록을 모아 둔 파일 패키지 등록 파일 `App`, `Core`, `Feature` 아래 있는 로컬 SPM 중에서 이 프로젝트가 조립 대상으로 인식해야 하는 패키지들을 등록하는 곳이다.
앱 타깃이 실제로 링크할 로컬 패키지, 외부 패키지, 다른 타깃을 적는 파일 의존성 선언 파일 앱이 실제 빌드될 때 무엇을 붙일지 적는 목록이다. 여기에는 로컬 SPM product, 외부 SPM product, 위젯/확장 타깃 같은 항목이 함께 들어갈 수 있다.
앱 타깃과 설정값을 어떤 방식으로 만들지 helper 함수로 정의한 파일 타깃 생성 규칙 파일 타깃을 하나하나 직접 쓰기보다, 공통 규칙으로 앱 타깃을 만들 수 있게 도와주는 helper 정의다. 소스, 리소스, 스크립트, configuration 연결 규칙이 여기 들어간다.
실제 프로젝트를 조립하면서 packages, targets, schemes를 연결하는 메인 프로젝트 파일 최종 조립 파일 위에서 준비한 패키지 목록, 의존성 목록, 타깃 생성 helper, 스킴 정의를 한곳에서 묶어 실제 프로젝트 구조를 결정하는 최종 조립 지점이다.
Tuist가 위 설정들을 읽고 실제로 만들어 낸 Xcode 프로젝트 생성 결과물 개발자가 직접 여는 Xcode 생성물이다. 결국 Xcode는 이 결과물을 보고 타깃, 스킴, 빌드 설정을 보여 준다.
앱, 테스트, 위젯, 확장 기능처럼 실제로 빌드되는 단위 빌드 단위 실제로 컴파일되어 결과물이 만들어지는 단위다. 앱 하나, 테스트 번들 하나, 위젯 하나가 각각 타깃이 된다.
어떤 타깃을 어떤 설정으로 빌드, 실행, 테스트, 아카이브할지 정하는 실행 묶음 실행 시나리오 Xcode 상단에서 선택하는 실행 세트다. 같은 타깃이라도 QA용, Dev용처럼 여러 스킴으로 다르게 돌릴 수 있다.
Debug_Qa, Release_QA처럼 실행 환경과 빌드 방식을 나누는 설정 이름 빌드 환경 이름 디버그/릴리즈 구분뿐 아니라 서버 환경, 번들 ID, plist, 서명 설정 같은 차이를 나누기 위한 빌드 설정 이름이다.

Then 1. `packages`와 `dependencies`의 관계

let project = Project(
    name: "ABC",
    packages: abcAppPackages,
    targets: abcAppTargets(variant: .global)
)

여기서 `packages: abcAppPackages`는 프로젝트가 알고 있는 로컬 패키지 목록이다. 반면 각 `target` 안의 `dependencies`는 그 타깃이 실제로 링크할 대상이다.

dependency 종류 무엇에 포함되어 있어야 하나
`.App.*`, `.Feature.*`, `.Core.*` 원본 패키지가 `packages: abcAppPackages`에 등록되어 있어야 한다.
`.external(name: "...")` 해당 외부 패키지가 central package registry file 에 선언되어 있어야 한다.
`.target(name: "...")` 그 이름의 target이 같은 프로젝트 안 `targets:` 배열에 실제로 있어야 한다.
즉 “dependency는 반드시 package 안에 있어야 하나?”라는 질문에는 패키지 의존성은 맞고, 프로젝트 target 의존성은 예외라고 답하는 게 정확하다.

Then 2. 이 프로젝트에서 앱 타깃 의존성은 실제로 무엇을 의미하나

dependency definition 을 보면 앱 타깃이 붙이는 의존성이 한곳에 모여 있다.

public let abcAppDependencies: [ProjectDescription.TargetDependency] = abcSharedAppDependencies + [
    .target(name: "ABCWidgetExtension"),
    .target(name: "ABCShareExtension"),
    .target(name: "ABCNotificationServiceExtension")
]

Then 3. `Scheme.scheme(...)` 파라미터 설명

파라미터 의미
`name` Xcode 상단에서 보이는 스킴 이름이다.
`buildAction` 이 스킴에서 빌드할 타깃 목록이다.
`testAction` 어떤 테스트 플랜을 어떤 configuration으로 실행할지 정한다.
`runAction` 앱 실행 시 어떤 configuration으로 돌릴지 정한다.
`archiveAction` Archive 시 어떤 configuration을 사용할지 정한다.
Scheme.scheme(
    name: "StreamingPlayQAApp",
    buildAction: .buildAction(targets: ["StreamingPlayQAApp"]),
    testAction: TestAction.testPlans(["ABCTests.xctestplan"], configuration: "Debug_Qa"),
    runAction: .runAction(configuration: "Debug_Qa"),
    archiveAction: .archiveAction(configuration: "Release_QA")
)

위 예시는 “`StreamingPlayQAApp` 타깃을 QA 디버그 설정으로 실행하고, 아카이브는 QA 릴리즈 설정으로 하라”는 뜻이다.

Then 4. 왜 Xcode에서 스킴이 안 보일 수 있나

선언이 안 된 것은 아니다. 실제 생성된 스킴 파일이 이미 존재한다.

보통 안 보이는 원인은 아래 쪽이다.

  1. 예전에 생성된 다른 `.xcodeproj` 또는 다른 워크스페이스를 열고 있는 경우
  2. `Project.swift` 수정 후 `tuist generate`를 다시 안 한 경우
  3. Xcode가 이전 scheme cache를 보고 있는 경우
즉 지금 케이스는 “스킴 선언 누락”보다는 열고 있는 생성물 또는 Xcode 상태 문제일 가능성이 크다.

Then 5. `Debug_Qa`, `Debug_Qa2`는 무엇인가

이건 단순 문자열이 아니라 커스텀 build configuration 이름이다. 기본 Xcode는 보통 `Debug`, `Release`만 두지만, 이 프로젝트는 환경이 많아서 이를 더 세분화했다.

configuration 의미
`Debug` 기본 디버그 빌드
`Debug_Dev` 개발 환경용 디버그 빌드
`Debug_Qa` QA 환경용 디버그 빌드
`Debug_Qa2` 두 번째 QA 환경용 디버그 빌드
`Release_Qa`, `Release_Qa2` QA/QA2 환경용 릴리즈 빌드
이 이름은 실제로 `.xcconfig`, 번들 ID, Firebase plist, 서버 환경 같은 설정 분기에 연결될 수 있다. 즉 `Debug_Qa2`는 그냥 이름이 아니라 “QA2 환경으로 실행되는 빌드 모드”다.

Then 6. 한 타깃과 여러 스킴, 한 스킴과 여러 타깃

질문
한 타깃에 여러 스킴이 가능하나 가능하다. 같은 타깃을 `Debug`, `QA`, `Sandbox`처럼 여러 실행 방식으로 돌릴 수 있다.
한 스킴에 여러 타깃이 가능하나 가능하다. `buildAction`에 여러 타깃을 넣어 함께 빌드할 수 있다.

다만 역할은 다르다. `target`은 결과물 단위이고, `scheme`은 그 결과물을 빌드/실행/테스트하는 시나리오 단위다.

Then 7. 프로젝트 타깃과 스킴의 buildAction 타깃 관계

스킴의 `buildAction` 안에 적힌 타깃은 새로 만드는 게 아니라, 프로젝트 안에 이미 정의돼 있는 target 이름을 참조하는 것이다.

// Project.swift
targets: abcAppTargets(variant: .global) + [
    qrLoginTestApp,
    secondOTPTestApp,
    streamingPlayTestApp,
    v3LoginApp,
    noticeTestApp,
    localNotificationTestApp
]

// same file
Scheme.scheme(
    name: "QRLoginTestApp",
    buildAction: .buildAction(targets: ["QRLoginTestApp"])
)

위 예시에서는 `qrLoginTestApp` helper가 실제 target `QRLoginTestApp`을 만들고, 스킴은 그 이름을 build 대상으로 다시 참조한다.

프로젝트에 정의된 helper 실제 target 이름 대응 스킴 buildAction
`qrLoginTestApp` `QRLoginTestApp` `QRLoginTestApp.xcscheme`에서 확인됨
`secondOTPTestApp` `SecondOTPTestQAApp` `SecondOTPTestQAApp.xcscheme`에서 확인됨
`streamingPlayTestApp` `StreamingPlayQAApp` `sample app scheme file`에서 확인됨
`v3LoginApp` `V3LoginApp` `V3LoginApp.xcscheme`에서 확인됨
`noticeTestApp` `NoticeTestQAApp` `NoticeTestQAApp.xcscheme`에서 확인됨
`localNotificationTestApp` `LocalNotificationTestApp` `LocalNotificationTestApp.xcscheme`에서 확인됨
`abcTest` `ABCTests` `ABCTests.xcscheme`에서 확인됨
즉 질문에 대한 기본 답은 “네, 스킴의 buildAction 타깃은 프로젝트에 정의된 target을 참조해야 한다” 이다.

Then 8. 지금 선언 기준으로 정말 전부 포함되나

확인 결과, 대부분은 포함된다. 실제로 아래 buildAction 타깃은 프로젝트 target과 대응하고 생성된 `.xcscheme` 파일도 존재한다.

하지만 `HomeTestQAApp`은 예외 가능성이 있다. 현재 확인한 범위에서는:

그래서 정확한 결론은: “거의 전부 맞지만, `HomeTestQAApp`은 현재 선언과 생성 결과가 어긋난 흔적이 있다” 이다.

Then 9. `Project(... packages: ...)`에 외부 패키지를 넣으면 어디까지 쓸 수 있나

결론부터 말하면 앱 타깃 수준에서 아는 것로컬 Swift Package 내부 코드가 직접 import 가능한 것은 다르다.

어디에 선언했는가 무엇이 가능해지나
`Project(... packages: ...)` 그 Xcode 프로젝트와 앱 target이 그 패키지를 dependency로 붙여 링크할 수 있다.
각 로컬 패키지의 package manifest 그 패키지 내부 소스가 직접 `import` 해서 사용할 수 있다.
즉 `Project(... packages: ...)`에 외부 패키지를 등록했다고 해서 `Feature/...`, `Core/...`, `App/...` 아래 모든 로컬 패키지 소스가 자동으로 그 패키지를 쓸 수 있게 되는 것은 아니다.

앱 타깃에서 쓰는 경우

// Project.swift
let project = Project(
    name: "ABC",
    packages: abcAppPackages
)

// dependency definition
.external(name: "GoogleSignIn")

이런 경우 앱 target은 `GoogleSignIn`을 dependency로 붙여 사용할 수 있다. 즉 프로젝트/앱 레벨에서는 외부 패키지를 알고 있다.

로컬 패키지 내부에서 쓰는 경우

// local package manifest
.package(url: "https://github.com/Alamofire/Alamofire.git", .upToNextMajor(from: "5.7.1"))

`Feature/ABCV3Home` 소스 안에서 `import Alamofire`를 하려면 이 패키지 자신의 package manifest에 `Alamofire`가 있어야 한다. `Project.swift` 선언만으로는 부족하다.

한 줄 정리

질문을 그대로 답하면: `Project(... packages: ...)`에 외부 패키지를 등록하면 앱 타깃에서는 쓸 수 있지만, 로컬 패키지에서는 자기 package manifest에 없으면 못 쓴다.

Then 10. `.external(name: ...)`를 쓸 수 있는 이유

`dependency definition`에서 `.external(name: "GoogleSignIn")`, `.external(name: "LineSDK")`, `.external(name: "AmplitudeSwift")` 같은 선언을 할 수 있는 이유는, central package registry file 에서 외부 package를 먼저 등록해 두었기 때문이다.

let package = Package(
    name: "PackageName",
    dependencies: [
        .package(url: "https://github.com/Alamofire/Alamofire", from: "5.0.0"),
        .package(url: "https://example.com/company-sdk", exact: "5.14.0"),
        .package(url: "https://github.com/amplitude/Amplitude-Swift", from: "1.17.0")
    ]
)

여기서 중요한 건 `.external(name: ...)`는 URL이 아니라 그 package가 노출하는 product 이름을 참조한다는 점이다. 그래서 Facebook SDK처럼 package 하나에서 `FacebookCore`, `FacebookLogin`, `FacebookBasics` 여러 product가 나올 수도 있다.

Then 11. `productTypes`를 static으로 강제하면 로컬 패키지도 동일하게 적용되나

질문의 핵심은 이것이다. `central package registry file`에서 `Alamofire`를 `.staticFramework`로 강제했으면, 로컬 패키지에서 `Alamofire`를 쓸 때도 static으로 들어오나?

이 프로젝트 기준으로는 그렇게 보는 게 맞다. 즉 Tuist가 생성하는 프로젝트 안에서는 `Alamofire` product를 어떤 타깃이 참조하든, 그 product type 정책은 중앙에서 통일된다고 보는 편이 정확하다.

let packageSettings = PackageSettings(
    productTypes: [
        "Alamofire": .staticFramework,
        "PanModal": .staticFramework,
        "RxSwift": .staticFramework,
        "LineSDK": .framework
    ]
)
경우 해석
앱 target이 `Alamofire`를 직접 dependency로 붙일 때 `Alamofire`는 static framework 정책으로 들어온다.
로컬 패키지(`Feature/ABCV3Home` 등)가 `Alamofire`를 dependency로 쓸 때 그 로컬 패키지 안에서 참조하는 `Alamofire` product도 같은 static 정책으로 해석된다.
즉 “앱에서는 static, 로컬 패키지에서는 package마다 다를 수 있나?”라는 질문에는 이 프로젝트처럼 Tuist가 product type을 중앙 통제하는 구조에서는 보통 동일 정책이 적용된다고 보는 게 맞다고 답할 수 있다.

다만 로컬 패키지 소스가 `Alamofire`를 직접 쓰려면, 여전히 그 패키지 자신의 package manifest에 `Alamofire` dependency 선언은 있어야 한다. 즉 “쓸 수 있느냐”와 “어떤 타입으로 링크되느냐”는 다른 문제다.

로컬 SPM 패키지 논의를 같은 문서에 붙이기

앞으로 `App`, `Core`, `Feature` 아래 로컬 패키지 논의는 이 문서에 계속 이어 붙인다. 이유는 `Package`, `Dependency`, `Target`, `Scheme`, `Configuration`, 로컬 패키지 구조가 모두 서로 연결돼 있기 때문이다.

그룹 개수 의미
`App` 25 앱 공통 서비스, 리소스, 인터페이스
`Core` 8 기반 유틸, 네트워크, 정책, SDK 래퍼
`Feature` 18 실제 화면과 기능 조합
개별 패키지는 다음 순서로 보는 것이 좋다. `Core/ABCBaseUtil` -> `Core/ABCNetwork` -> `Core/ABCSDKWrapper` -> `App/Resource` -> `Feature/ABCV3Home`

Then 12. 문자열 dependency는 target 이름인가, product 이름인가

패키지 파일들을 실제로 대조해 보면, 많은 로컬 패키지는 product 이름과 target 이름을 같게 만들어 둔다. 그래서 `"ABCV3LoginETCUseCases"` 같은 문자열을 보면 target 같아 보이지만, 실제로는 같은 이름의 product와 target이 동시에 존재하는 경우가 많다.

이름 실제 상태
`ABCV3LoginETCUseCases` local package manifest 기준으로 product도 있고 target도 있다.
`ABCV3LoginCommon` product도 있고 target도 있다.
`ABCNetwork` local package manifest 기준으로 product도 있고 target도 있다.
`STDS` local package manifest 기준으로 product도 있고 target도 있다.
그래서 이런 문자열 dependency는 실무적으로는 “그 이름으로 노출된 모듈을 참조한다”라고 읽는 게 가장 안전하다. 같은 패키지 내부라면 target 관점으로 읽히고, 다른 패키지에서는 그 target을 감싼 product 관점으로 소비되는 경우가 많다.

반면 `.product(name: "RxCocoa", package: "RxSwift")` 같은 표기는 외부 package의 특정 product를 정확히 집어서 쓰겠다는 뜻이라 더 명시적이다.

Then 13. 프로젝트 안의 로컬 패키지는 실제로 몇 개고, 무엇이 사용되나

실제 저장소에서 package manifest를 전부 확인해 보면 샘플용 Sample/SwiftConcurrencyPlayground와 Tuist 설정용 central package registry file를 제외한 로컬 Swift Package는 총 53개다.

구분 개수 설명
레포에 존재하는 로컬 패키지 전체 53 `App`, `Core`, `Feature`, `stds-ios`, `abcfoundation-ios` 기준 전체
`abcAppPackages`에 직접 등록된 패키지 48 루트 프로젝트가 알고 있는 로컬 package 목록
레포에는 있지만 루트 등록 목록에는 없는 패키지 5 다른 로컬 패키지가 자기 package manifest에서 직접 참조하는 성격이 강하다.

53개 전체 목록

영역 패키지
`App` 25개 `ABTest`, `Biometric`, `BlockUserInterface`, `CookieManager`, `Environment`, `GOTPLib`, `HomeTemplate`, `LocalNotificationManager`, `LoginState`, `ABCBaseUI`, `ABCChatManager`, `OTPServiceInterface`, `PerformanceTraceInterface`, `ProfileManager`, `RealtimeEventManagerInterface`, `RealtimeEventStream`, `RemoteConfig`, `RemoteNotification`, `Resource`, `SchemeInterface`, `SocialSchemeInterface`, `TinodeEventManager`, `TinodeServiceExtension`, `TokenManager`, `TokenManagerInterface`
`Core` 8개 `ABCBaseUtil`, `ABCNetwork`, `ABCNetworkInterface`, `ABCPolicy`, `ABCSDKWrapper`, `ABCSDKWrapperInterface`, `NotificationUtils`, `ABCSocialData`
`Feature` 18개 `CharacterShop`, `Gamification`, `GamificationList`, `LanguageSetup`, `LoginHistory`, `ABCV3EditProfile`, `ABCV3Home`, `ABCV3Login`, `Notice`, `OneTimeLogin`, `PlatformPopup`, `QRLogin`, `RemoteLogout`, `ABCSocialUI`, `SecondAuthenticationOTP`, `SocialScene`, `ABCGameString`, `StreamingPlay`
`Root` 2개 `stds-ios`, `abcfoundation-ios`

루트 프로젝트 등록 목록에는 없지만 실제 레포에는 있는 5개

패키지 왜 중요한가
`App/SocialSchemeInterface` `Feature/SocialScene` 등이 자기 package manifest에서 직접 가져다 쓴다. product 이름은 `ABCSocialSchemeInterface`다.
`Core/ABCBaseUtil` 여러 Feature/App 패키지가 직접 의존한다. 앱 타깃 dependency에도 `.Core.ABCBaseUtil`로 들어간다.
`Core/ABCNetworkInterface` `ABCV3Home`, `ABCSDKWrapper` 등에서 인터페이스 계층으로 사용된다.
`Core/ABCPolicy` `ABCSocialData`, `ABCV3Login` 등에서 직접 참조한다.
`Core/ABCSDKWrapperInterface` `ABCV3Home`, `ABCV3Login`, `Gamification` 등에서 SDK 추상화 인터페이스로 쓴다.
중요한 해석: “프로젝트 내 사용되는 로컬 패키지”는 `abcAppPackages` 48개만이 아니라, 각 로컬 패키지의 package manifest가 다시 끌고 오는 5개까지 포함해서 봐야 한다. 즉 실사용 기준으로는 53개를 보는 것이 맞다.
flowchart TD
    A["레포에 존재하는 로컬 패키지 53개"]
    B["abcAppPackages에 직접 등록 48개"]
    C["루트 등록 없음 5개"]
    D["앱 타깃에서 직접 연결"]
    E["다른 로컬 패키지 내부에서 재사용"]

    A --> B
    A --> C
    B --> D
    B --> E
    C --> E
        

Then 14. `ABCV3Home`가 앱까지 연결되는 실제 경로

`ABCV3Home`는 이름만 보면 하나의 패키지처럼 보이지만, 실제 앱 연결에서는 하나의 폴더 안에 있는 여러 product 중 두 개가 앱 타깃에 직접 연결된다.

단계 실제 내용
로컬 패키지 등록 package list definition 에 `Feature/ABCV3Home`가 들어간다.
Tuist dependency 별칭 dependency alias file 에서 `.Feature.ABCV3Home = package(product: "ABCV3HomeFeature")`, `.Feature.ABCV3Data = package(product: "ABCV3HomeData")`로 매핑된다.
앱 타깃 dependency dependency definition 에 `.Feature.ABCV3Home`, `.Feature.ABCV3Data` 둘 다 들어간다.
실제 product 정의 local package manifest 에 `ABCV3HomeFeature`, `ABCV3HomeDataInterface`, `ABCV3HomeData` product가 정의되어 있다.
패키지 내부 연결 `ABCV3HomeFeature`는 `ABCV3HomeDataInterface`에 의존하고, `ABCV3HomeData`는 `ABCV3HomeDataInterface`를 구현하면서 `ABCNetworkInterface`, `ABCNetwork`, `ABTest`, `ABCSocialData` 등에 의존한다.
flowchart LR
    A["Feature/ABCV3Home package folder"]
    B["product: ABCV3HomeFeature"]
    C["product: ABCV3HomeData"]
    D["product: ABCV3HomeDataInterface"]
    E[".Feature.ABCV3Home"]
    F[".Feature.ABCV3Data"]
    G["abcAppDependencies"]
    H["ABC / QA_ABC / DEV_ABC app target"]

    A --> B
    A --> C
    A --> D
    B --> E
    C --> F
    E --> G
    F --> G
    G --> H
    B --> D
    C --> D
        
즉 앱이 직접 붙잡는 것은 단순히 `Feature/ABCV3Home` 폴더가 아니라, 그 안에서 노출한 `ABCV3HomeFeature` product와 `ABCV3HomeData` product다. 이름이 달라서 헷갈리지만, 실제 연결 경로는 이 두 product를 따라가야 맞다.

Then 15. 앱, 로컬 SPM, 외부 SPM 전체 의존성 인터랙티브 맵

이 다이어그램은 프로젝트 조립 관점의 핵심 의존성을 한 번에 보도록 만든 지도다. 기본 상태에서는 선을 거의 숨겨서 복잡해 보이지 않게 했고, 특정 타깃이나 패키지에 마우스를 올리면 관련된 패키지와 선만 강조되도록 만들었다.

hover: 관련 노드와 선만 하이라이트 click: 현재 노드 고정 빈 영역 click: 고정 해제 구성: 앱 타깃 -> 로컬 패키지 -> 외부 SPM
의존성 설명 패널
노드에 마우스를 올리면 이 대상이 무엇에 의존하고, 누가 이 대상을 사용 중인지 바로 볼 수 있다.

이 대상이 의존하는 것

아직 선택된 노드가 없다.

이 대상을 사용하는 것

마우스를 올려서 관계를 확인하면 된다.
이 맵은 선을 읽기 쉽게 유지하기 위해 루트 프로젝트의 직접 의존성 + support target 의존성 + 내부 구조를 이해하는 데 중요한 로컬 패키지 간 연결을 중심으로 그렸다. 특히 `ABCSDKWrapperInterface`, `ABCNetworkInterface`, `SocialSchemeInterface`처럼 앱 타깃에서 바로 안 보이는 내부 인터페이스 패키지도 같이 연결해 두었다.

질문들을 한 번에 묶는 최종 흐름도

flowchart LR
    P1["로컬 패키지를 프로젝트에 등록하는 목록"]
    P2["외부 SPM을 중앙에서 등록하고 타입 정책을 주는 목록"]
    D1["앱 타깃이 실제로 붙이는 의존성 목록"]
    T1["앱, 테스트, 확장 기능 등 실제 빌드되는 타깃 목록"]
    S1["어떤 타깃을 어떤 설정으로 실행할지 정하는 스킴 목록"]
    C1["Debug_Qa / Release_Qa2 같은 실행 환경 설정"]
    X1["Tuist가 생성한 xcscheme 파일들"]
    A1["Xcode 상단에서 고르는 스킴 선택 UI"]

    P1 --> D1
    P2 --> D1
    D1 --> T1
    T1 --> S1
    C1 --> S1
    S1 --> X1
    X1 --> A1
        
핵심 문장: 먼저 프로젝트가 어떤 로컬 패키지와 외부 패키지를 알고 있는지 등록하고, 그다음 앱 타깃이 실제로 어떤 모듈을 붙일지 정하고, 그 결과 만들어진 타깃을 스킴이 어떤 환경 설정으로 실행할지 고른다.

예시 흐름

flowchart LR
    A["sample app helper가 테스트용 앱 타깃 정의를 만든다"]
    B["실제로 생성되는 타깃 이름은 StreamingPlayQAApp 이다"]
    C["이 타깃을 실행하기 위한 스킴 파일이 함께 생성된다"]
    D["그 스킴의 buildAction은 StreamingPlayQAApp 을 빌드 대상으로 잡는다"]
    E["그 스킴의 runAction은 Debug_Qa 설정으로 실행한다"]
    F["그 스킴의 archiveAction은 Release_QA 설정으로 아카이브한다"]

    A --> B
    B --> C
    C --> D
    C --> E
    C --> F
        

이 예시는 테스트용 샘플 앱 helper 하나가 먼저 `StreamingPlayQAApp`이라는 실제 타깃을 만들고, 그 다음 스킴이 그 타깃을 빌드 대상으로 선택한 뒤, 실행할 때는 `Debug_Qa`, 아카이브할 때는 `Release_QA`를 사용하도록 묶는 흐름이다.