Skip to content

Module Structure

JEONG edited this page Aug 24, 2026 · 2 revisions

Module Structure

Tuist 기반 UMCApp/ 모듈 구조와 의존성 방향. 빌드 명령은 Build & Run 참고.

두 빌드 축

형태 상태
UMCApp/ Tuist 활성 — 모든 신규·유지보수 작업 대상
AppProduct/ xcodeproj 레거시, v2.2.0 동결 (열람/참고 전용)

UMCApp 전체 구조

UMCApp/
├── Makefile                       # 빌드/생성 래퍼 (mise exec 기반)
├── MAKEFILE_GUIDE.md              # 팀원용 사용 가이드
├── mise.toml                      # tuist 버전 고정 (4.155.0)
├── Project.swift                  # 앱 타겟 정의 (com.umc.product)
├── Workspace.swift                # 워크스페이스 (., Core/*, Features/*, Widget, Watch)
├── Tuist.swift                    # Tuist 시스템 설정
├── Scripts/verify-secrets.sh      # 시크릿 누락 가드 (Release 빌드 실패)
├── Secrets/                       # xcconfig 기반 환경/시크릿 주입
├── Tuist/
│   ├── Package.swift              # SPM 외부 의존성
│   └── ProjectDescriptionHelpers/
│       ├── Project+Core.swift          # Core 모듈 헬퍼
│       ├── Project+Feature.swift       # Feature 모듈 헬퍼 (3 타겟 + 테스트)
│       ├── Project+WidgetExtension.swift
│       ├── Project+WatchApp.swift
│       └── Settings+Recommended.swift  # 권장 빌드 설정 · 버전 · 서명
├── Core/                          # 11개 공유 인프라 모듈
│   ├── Foundation/                # UMCFoundation — Config, 에러 타입, Loadable,
│   │                              #   AlertPrompt, 위치(LocationManager/Geofence),
│   │                              #   Date/String 확장, Flexible 디코딩 헬퍼 [iOS+watchOS]
│   ├── Domain/                    # CoreDomain — 교차 도메인(Member/Authorization/Post)
│   ├── Network/                   # CoreNetwork — Moya, 소셜 SDK, Keychain, STOMP 실시간
│   ├── DI/                        # CoreDI — DIContainer, Environment 주입
│   ├── Routing/                   # CoreRouting — NavigationTab, PathStore
│   ├── DesignSystem/              # CoreDesignSystem — 색상/타이포/레이아웃 토큰, 폰트
│   ├── UIComponents/              # CoreUIComponents — 공용 컴포넌트 (Kingfisher, MapKit, TipKit)
│   ├── Photo/                     # CorePhoto — 사진 선택 프로토콜
│   ├── NearbyExchange/            # CoreNearbyExchange — MPC 전송 + NearbyInteraction 거리
│   ├── WatchConnectivity/         # CoreWatchConnectivity — iOS ↔ watchOS [iOS+watchOS]
│   └── WidgetShared/              # CoreWidgetShared — Widget-App 공유 모델
├── Features/                      # 9개 기능 모듈 (각 Domain/Data/Presentation 3 타겟)
│   ├── Activity/                  # 출석·스터디·커리큘럼·멤버 관리
│   ├── Auth/                      # 소셜/이메일 로그인, 회원가입, 비밀번호
│   ├── Badge/                     # 배지
│   ├── BusinessCard/              # 전자명함 · 명함첩 · 근거리 교환 · QR
│   ├── Community/                 # 스레드(채팅)·게시물, 온디바이스 AI 요약/분류
│   ├── Home/                      # 홈 대시보드·일정·출석 (CoreML 분류 모델 포함)
│   ├── Maintenance/               # 원격 점검(킬스위치)·강제 업데이트 (RemoteConfig)
│   ├── MyPage/                    # 마이페이지·프로필
│   └── Notice/                    # 공지 (The Ping)
├── UMCAppWidget/                  # Widget Extension (com.umc.product.widget)
└── UMCWatchApp/                   # watchOS Companion App (com.umc.product.watchkitapp)

모듈 의존성 방향

App Target (UMCApp)
    ↓
Feature Presentation  (자기 Domain + CoreDesignSystem + CoreUIComponents + UMCFoundation)
    ↓
Feature Domain        (UMCFoundation [+ CoreDomain])
Feature Data          (자기 Domain + CoreNetwork + UMCFoundation)
    ↓
Core Modules          (Foundation / Domain / Network / DI / Routing / DesignSystem / …)
    ↓
External Packages     (Moya · Kingfisher · Kakao SDK · GoogleSignIn · Firebase)

경계 규칙

  • Core → Feature 의존 금지. CoreRouting이 타입 소거 NavigationPath를 쓰는 이유가 이것입니다 (Architecture › PathStore 참고).
  • Feature → Feature 의존은 Domain 타겟까지만 허용합니다. 예: MyPageDomainAuthDomain/BadgeDomain/CommunityDomain/BusinessCardDomain을 참조. 예외적으로 HomePresentationActivityPresentation을 링크합니다(홈에서 출석 화면 직접 진입).
  • 두 개 이상의 Feature가 실제로 공유하는 도메인만 CoreDomain으로 승격합니다.
  • staticFramework는 Compile Sources 산출물이 소비 타겟까지 전파되지 않습니다. 런타임 리소스(CoreML .mlmodel 등)는 dataResources로 리소스 번들에 명시해야 합니다. (예: Features/Home/Data/Sources/MLModels/**NoticeClassifierML, ScheduleListClassifierML)

Feature 모듈 = 3개 타겟 (+ 옵션 테스트 타겟)

타겟 Product Type Bundle ID 패턴 Sources
{Name}Domain .staticFramework dev.umc.feature.{name}.domain Domain/Sources/**
{Name}Data .staticFramework dev.umc.feature.{name}.data Data/Sources/**
{Name}Presentation .staticFramework dev.umc.feature.{name}.presentation Presentation/Sources/**
{Name}{Layer}Tests .unitTests …{layer}.tests {Layer}/Tests/**

테스트 타겟은 includesDomainTests / includesDataTests / includesPresentationTests 플래그로 레이어별로 켭니다. 메인 타겟은 자동으로 의존성에 포함됩니다.

ProjectDescriptionHelpers

// Core 모듈 (단일 staticFramework + 옵션 테스트)
coreProject(
    name: "CoreNetwork",
    bundleIdSuffix: "network",
    dependencies: [
        .project(target: "UMCFoundation", path: .relativeToRoot("Core/Foundation")),
        .external(name: "Moya"),
    ],
    includesTests: true
)

// watchOS 공유 모듈은 destinations / deploymentTargets 를 넘긴다
coreProject(
    name: "CoreWatchConnectivity",
    bundleIdSuffix: "watchconnectivity",
    destinations: [.iPhone, .appleWatch],
    deploymentTargets: .multiplatform(iOS: "26.4", watchOS: "26.4"),
    dependencies: [.sdk(name: "WatchConnectivity", type: .framework, status: .required)]
)

// Feature 모듈 (Domain/Data/Presentation 3 타겟 자동 생성)
featureProject(
    name: "Maintenance",
    dataExtraDependencies: [
        .external(name: "FirebaseCore"),
        .external(name: "FirebaseRemoteConfig"),
    ],
    presentationExtraDependencies: [
        .project(target: "CoreDI", path: .relativeToRoot("Core/DI")),
    ],
    includesDomainTests: true,
    includesDataTests: true,
    includesPresentationTests: true
)

주요 설정

항목
Tuist 버전 UMCApp/mise.toml 고정 (4.155.0)
Deployment Target iOS 26.4 / watchOS 26.4
Product Type 모든 모듈 .staticFramework
Bundle ID com.umc.product · Core dev.umc.core.* · Feature dev.umc.feature.*.*
앱 버전 MARKETING_VERSION = 3.0.0, CURRENT_PROJECT_VERSIONTUIST_BUILD_NUMBER 환경변수 (기본 1)
서명 DEVELOPMENT_TEAM / CODE_SIGN_STYLE을 매니페스트에 고정 (generate 시 날아가므로)
인터페이스 스타일 UIUserInterfaceStyle = Light 고정 (검증된 다크 시안이 없음)
외부 의존성 Moya 15.0.3 · Kingfisher 8.6.1 · kakao-ios-sdk 2.27.0 · GoogleSignIn-iOS 9.1.0 · firebase-ios-sdk 12.7.0

Tuist 버전을 올릴 때는 mise.toml 수정하고 PR 본문에 릴리스 노트를 첨부합니다. Makefile / 로컬 tuist 설치는 손대지 않습니다.

앱 타겟이 선언하는 권한·스킴 (Project.swift)

이유
NSLocalNetworkUsageDescription + NSBonjourServices MPC 근거리 명함 교환. 없으면 MPC가 시작조차 안 됨. 서비스 타입은 MPCTransport.serviceType과 동일해야 함
NSNearbyInteractionUsageDescription 명함 교환 거리 측정
NSCameraUsageDescription 명함 QR 스캔
NSPhotoLibraryAddUsageDescription 명함 QR 이미지 저장 (읽기 없는 Add 전용 키)
NSLocationWhenInUseUsageDescription GPS 스마트 출석
CFBundleURLTypes kakao{KEY} · Google reversed client ID · umc:// 딥링크
UIBackgroundModes: remote-notification 백그라운드 푸시 → 알림 보관함 저장

레거시 AppProduct 구조 (참고)

AppProduct/AppProduct/
├── Core/            # Alert, Common(DesignSystem/Enum/Error/UIComponents), DIContainer, Manager, Navigation, NetworkAdapter, Secret
├── Utilities/       # Extensions, Keychain, Modifier, RemoteImage, Shadow, ToolBar
└── Features/        # Activity, Auth, Community, Home, MyPage, Notice, Splash, Tab

AppProduct는 v2.2.0에 동결되어 있습니다. 수정하지 않습니다.


관련 문서: Build & Run · Architecture

Clone this wiki locally