SwiftUI 멀티 플랫폼 아키텍처 (iOS, iPadOS, macOS, visionOS)

발행: (2026년 1월 7일 오전 04:32 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

위에 제공된 소스 링크 외에 번역할 텍스트가 포함되어 있지 않습니다. 번역을 원하는 전체 내용을 제공해 주시면 한국어로 번역해 드리겠습니다.

🧠 핵심 원칙

동작을 공유합니다.
프레젠테이션을 특화합니다.

  • 비즈니스 로직은 100 % 공유되어야 합니다.
  • UI 구성은 플랫폼마다 다르게 적용됩니다.

🧱 1. 기능별로 구분하고, 플랫폼별로 구분하지 않기

나쁜 구조

iOS/
macOS/
Shared/

좋은 구조

Features/
    Home/
    Profile/
    Settings/
Platform/
    iOS/
    macOS/
    visionOS/

각 기능은 다음을 포함합니다:

  • ViewModel
  • State
  • Business logic

플랫폼 폴더는 다음을 포함합니다:

  • Wrappers
  • Layout adapters
  • Platform‑specific views

🧩 2. 플랫폼 추상화 레이어

작은 어댑터를 만들기:

protocol PlatformMetrics {
    var sidebarWidth: CGFloat { get }
    var toolbarHeight: CGFloat { get }
}

플랫폼별 구현:

struct iOSMetrics: PlatformMetrics {
    let sidebarWidth: CGFloat = 0
    let toolbarHeight: CGFloat = 44
}

struct macOSMetrics: PlatformMetrics {
    let sidebarWidth: CGFloat = 240
    let toolbarHeight: CGFloat = 52
}

적절한 구현을 주입하기:

.environment(\.metrics, currentMetrics)

뷰 내부에 조건문이 필요하지 않습니다.

🧭 3. 플랫폼별 내비게이션 아키텍처

PlatformNavigation style
iPhone단일 스택, 푸시 기반
iPad분할 뷰, 컬럼 내비게이션
macOS사이드바 + 디테일, 다중‑윈도우
visionOS공간 스택, 씬‑기반 내비게이션

단일 내비게이션 모델을 강제하지 마세요. 대신:

struct RootView: View {
    var body: some View {
        PlatformContainer {
            HomeFeature()
        }
    }
}

PlatformContainer는 현재 플랫폼에 따라 구현을 전환합니다.

🪟 4. 윈도우 및 씬 관리

macOS와 visionOS는 윈도우‑우선 플랫폼입니다. 명시적으로 설계하세요:

WindowGroup {
    RootView()
}

WindowGroup(id: "inspector") {
    InspectorView()
}

iOS에서는 이러한 그룹이 무시되지만, macOS에서는 필수적입니다. 아키텍처는 여러 인스턴스를 가정해야 합니다.

🧠 5. 입력 모델 차이점

플랫폼입력 양식
iOS터치, 제스처, 스와이프
macOS포인터, 호버, 오른쪽 클릭, 키보드
visionOS시선, 핀치, 공간 포커스

절대로 단독으로 의존하지 마세요:

.onTapGesture {  }

항상 지원하세요:

  • Button (키보드 단축키 포함)
  • 포커스 처리
  • 컨텍스트 메뉴

⌨️ 6. 키보드 및 명령 시스템

macOS와 iPad는 명령 정의가 필요합니다:

.commands {
    CommandGroup(replacing: .newItem) {
        Button("New Item") {
            create()
        }
        .keyboardShortcut("N")
    }
}

공유 로직에서 액션을 노출합니다:

func create()
func delete()
func refresh()

UI 레이어가 이러한 액션을 연결하고, 기본 로직은 공유된 상태를 유지합니다.

📐 7. 레이아웃 전략

고정 레이아웃을 피하세요. 적응형 스택, 유연한 그리드, 동적 타입 및 사이즈 클래스를 사용하세요. 예시 패턴:

if horizontalSizeClass == .compact {
    CompactLayout()
} else {
    RegularLayout()
}

이 로직은 기능 뷰가 아니라 레이아웃 컨테이너에 분리하세요—기능 뷰에 두지 마세요.

🧪 8. 멀티플랫폼 정확성 테스트

다음 항목에 대해 플랫폼 간 테스트:

  • 윈도우 생성
  • 플랫폼별 딥링크
  • 키보드 탐색 및 포커스 동작
  • 스플릿‑뷰 상태
  • 멀티 윈도우 복원

대부분의 멀티플랫폼 버그는 UI보다 상태와 관련되어 있습니다.

❌ 9. 일반적인 안티‑패턴

피해야 할 것:

  • #if os(iOS)를 기능 뷰 안에 사용
  • ViewModel 안에서 플랫폼 검사
  • 중복된 비즈니스 로직
  • 모든 곳에서 단일 UI를 강제하는 네비게이션 해킹

해결책이 억지로 느껴진다면, 아마도 그렇습니다.

🧠 10. 멘탈 모델

계층적으로 생각하세요:

Business Logic (shared)
State Management (shared)
Navigation Model (per platform)
Layout System (per platform)
Input Model (per platform)
Presentation (per platform)

하단 세 계층만 플랫폼마다 달라집니다.

🚀 Final Thoughts

  • 기능은 재사용 가능하게 유지됩니다
  • 코드는 깔끔하게 유지됩니다
  • 플랫폼은 네이티브처럼 느껴집니다
  • 유지보수는 합리적으로 유지됩니다
  • visionOS 지원이 간단해집니다
Back to Blog

관련 글

더 보기 »

SwiftUI #21: 그룹

Group이란? Group은 여러 뷰를 함께 묶어 Stack이 가지고 있는 최대 10개의 서브 뷰 제한을 피하고 여러 뷰에 스타일을 적용할 수 있게 합니다.

SwiftUI #20: 우선순위

소개 SwiftUI에서 Stack은 뷰 사이의 공간을 균등하게 나눕니다. 뷰가 들어가지 않으면 Image에 고정 크기를 할당하고 축소합니다.