Kotlin이 Swift 쪽으로 한 발 더 들어왔다

이경규·2026년 9월 12일

Kotlin이 Swift 쪽으로 한 발 더 들어왔다

Kotlin 2.4.20의 Swift Export, 그리고 iPhone Duo 시대의 KMP

Kotlin 2.4.20이 9월 7일 정식 출시됐습니다.

전체 변경사항을 보면 Standard Library, Kotlin/Wasm, Kotlin/JS, Gradle 등 여러 영역이 업데이트됐지만 iOS 개발자라면 Kotlin/Native 쪽 변화를 눈여겨볼 만합니다.

이번 버전에서 특히 볼 부분은 세 가지입니다.

  • Swift Export 기능 확대
  • SwiftPM 연동을 위한 Package.swift 생성 지원
  • Kotlin/Native Incremental Compilation 개선

각각만 보면 작은 개선처럼 보일 수도 있습니다.

하지만 방향은 꽤 명확합니다.

기존 KMP의 iOS 연동은 대체로

Kotlin
   ↓
Kotlin/Native
   ↓
Objective-C Interop
   ↓
Swift

구조였습니다.

Kotlin 2.4.20에서 계속 진행되고 있는 방향은 이 중간 단계를 줄여

Kotlin
   ↓
Swift

에 더 가까운 개발 경험을 만드는 것입니다.

그리고 최근 발표된 iPhone Duo까지 같이 보면 이 변화가 더 흥미로워집니다.

앞으로 KMP에서 중요한 질문은 단순히

얼마나 많은 코드를 공유할 수 있는가?

가 아니라

어디까지 공유하는 것이 Apple 플랫폼 변화에 가장 자연스럽게 대응할 수 있는가?

가 될 가능성이 높습니다.


KMP에서 항상 애매했던 iOS 쪽 경계

Kotlin Multiplatform을 사용하는 가장 현실적인 방식은 UI 전체를 공유하는 것보다 비즈니스 로직을 공유하는 구조입니다.

예를 들면 이런 형태입니다.

Android
   │
   ├─ Compose
   │
   └─────────┐
             │
        Shared KMP
             │
   ┌─────────┘
   │
iOS
   │
   └─ SwiftUI

공유 영역에는 보통

Network
Repository
Domain
Validation
Business Logic

이 들어갑니다.

iOS UI는 SwiftUI,

Android UI는 Compose로 만드는 방식입니다.

구조 자체는 상당히 자연스럽습니다.

문제는 Shared Module이 Swift와 만나는 지점이었습니다.

Kotlin/Native는 오랫동안 Objective-C compatibility를 기반으로 Swift에 API를 노출했습니다.

그래서 Kotlin에서는 자연스러운 타입도 Swift에 넘어오는 순간 조금 다른 모습이 됩니다.

대표적으로

Package
Nullable
Generic
Coroutine
Flow
Sealed Class

같은 부분입니다.

결국 Shared Logic을 만들고도 iOS 쪽에 Adapter나 Wrapper가 다시 생기는 경우가 많았습니다.

Kotlin Model
     ↓
Interop
     ↓
Swift Adapter
     ↓
SwiftUI

KMP를 사용하면서도 iOS 개발자가 계속 Interop 구조를 의식해야 했던 이유입니다.


Swift Export가 줄이려는 것이 바로 이 경계다

Swift Export의 목표는 단순합니다.

Kotlin API를 Swift에서 보다 Swift다운 형태로 사용할 수 있게 만드는 것.

기존 구조가

Kotlin
 ↓
Objective-C
 ↓
Swift

였다면,

Swift Export는

Kotlin
 ↓
Swift Module
 ↓
Swift

에 가까운 구조를 만듭니다.

Kotlin package 구조도 더 자연스럽게 Swift 쪽으로 전달할 수 있고, 여러 Kotlin Module을 각각 Swift Module 형태로 Export하는 것도 가능합니다.

예를 들어 KMP 프로젝트가

shared-domain
shared-network
shared-database

처럼 구성돼 있다면 Swift에서도 Module 경계를 더 명확하게 유지할 수 있습니다.

결국 목표는 하나입니다.

iOS 개발자가 KMP를 사용하면서 Kotlin/Native의 내부 사정을 최대한 덜 신경 쓰게 만드는 것.


2.4.20에서는 sealed class가 Swift에서 훨씬 자연스러워졌다

이번 버전에서 iOS 개발자가 체감하기 좋은 변화 중 하나가 sealed class와 sealed interface 지원입니다.

Kotlin에서는 상태를 표현할 때 sealed hierarchy를 자주 사용합니다.

sealed interface LoginState

data object Loading : LoginState

data class Success(
    val userName: String
) : LoginState

data class Failure(
    val message: String
) : LoginState

Kotlin에서는 when으로 모든 상태를 안전하게 처리할 수 있습니다.

when (state) {
    Loading -> showLoading()
    is Success -> showUser(state.userName)
    is Failure -> showError(state.message)
}

새로운 subtype이 추가되면 Compiler가 누락된 case를 알려줍니다.

Swift의 enum을 switch하는 것과 비슷합니다.

문제는 기존 Swift Interop이었습니다.

Swift에서는 Kotlin sealed hierarchy를 처리할 때 결국

default:
    break

같은 case를 넣어야 하는 경우가 있었습니다.

이렇게 되면 Kotlin에 새로운 상태가 추가돼도 Swift Compiler가 누락을 잡아주지 못할 수 있습니다.


이제 Swift에서도 exhaustive switch가 가능하다

Kotlin 2.4.20의 Swift Export에서는 sealed hierarchy를 Swift enum 형태로 연결할 수 있습니다.

sealedType()을 통해 Swift에서 exhaustive switch가 가능합니다.

예를 들어 Kotlin에

sealed interface Shape

class Circle : Shape
class Rectangle : Shape

가 있다면 Swift에서는 개념적으로 이런 식입니다.

switch shape.sealedType() {
case .circle(let circle):
    print(circle.value)

case .rectangle(let rectangle):
    print(rectangle.value)
}

default가 필요 없습니다.

Kotlin에 새로운 subtype이 추가되면 Swift Compiler도 처리되지 않은 case가 있다는 것을 알려줄 수 있습니다.

KMP에서 UI State를 공유한다면 꽤 의미가 큽니다.

예를 들어

Loading
Success
Empty
Failure
Expired

같은 상태를 Kotlin에서 정의하고 SwiftUI에서 그대로 처리하기가 훨씬 자연스러워집니다.


SwiftUI와 KMP 사이에 만들던 Adapter도 줄일 수 있다

기존 KMP + SwiftUI 프로젝트에서는 이런 구조가 자주 생깁니다.

Kotlin State
    ↓
Swift Adapter
    ↓
Swift Enum
    ↓
SwiftUI

Shared Logic을 만들었는데 iOS에서 다시 한번 Model 변환을 하는 셈입니다.

Swift Export가 발전하면 목표 구조는 훨씬 단순합니다.

Kotlin State
     ↓
Swift Export
     ↓
SwiftUI

실제 프로젝트에서 KMP 도입 비용을 높이는 것은 Kotlin 코드 자체보다 이런 Glue Code인 경우가 많습니다.

따라서 Swift Export 개선은 단순한 문법 편의보다 Architecture 관점에서 의미가 있습니다.


Swift가 Kotlin interface를 직접 구현할 수도 있다

2.4.20에서 또 하나 중요한 변화가 Cross-language inheritance입니다.

Kotlin에서 Contract를 정의하고 실제 구현을 Swift에서 제공할 수 있습니다.

예를 들어 Shared Module에서

interface SecureStorage {
    fun save(
        key: String,
        value: String
    )
}

라는 Interface를 정의했다고 해보겠습니다.

iOS에서는 실제 구현에 Keychain을 사용하고 싶습니다.

이 로직을 Kotlin으로 억지로 옮길 필요가 없습니다.

Swift가 구현하면 됩니다.

Shared Kotlin

SecureStorage
     ↑
     │
Swift Implementation
     │
  Keychain

CryptoKit처럼 Apple 전용 Framework를 사용하는 것도 같은 방식으로 생각할 수 있습니다.

이건 KMP Architecture에서 상당히 중요한 변화입니다.


KMP라고 해서 모든 코드를 공유할 필요는 없다

KMP를 처음 적용할 때 흔히 나오는 고민이 있습니다.

어디까지 Kotlin으로 만들 것인가?

공유할 수 있다는 이유로 모든 영역을 Shared Module로 올릴 필요는 없습니다.

예를 들어

Keychain
CryptoKit
LocalAuthentication
AVFoundation
HealthKit
StoreKit

같은 기능은 Apple Framework를 Native에서 직접 사용하는 편이 더 자연스러운 경우가 많습니다.

Shared Module에서는 Contract만 정의합니다.

Shared Kotlin

AuthenticationProvider
SecureStorage
CryptoProvider

그리고 각 플랫폼에서 구현합니다.

Android
 └─ Android implementation

iOS
 └─ Swift implementation

Cross-language inheritance가 좋아질수록 이런 구조의 비용이 줄어듭니다.

KMP의 방향도

모든 것을 Kotlin으로 만든다

보다

공유할 가치가 있는 것만 Kotlin으로 만든다

쪽이 더 현실적입니다.


SwiftPM 연결도 계속 자연스러워지고 있다

iOS 개발자 입장에서 또 중요한 부분이 Swift Package Manager입니다.

KMP Library를 iOS에서 사용하려면 일반적으로 XCFramework를 사용합니다.

Kotlin
   ↓
Kotlin/Native
   ↓
XCFramework
   ↓
iOS

SwiftPM으로 배포한다면 Package.swift 관리가 추가됩니다.

특히 해당 XCFramework가 다른 Swift Package에 의존하면 배포 과정이 더 복잡해집니다.

Kotlin 2.4.20에서는 SwiftPM dependency가 있는 XCFramework를 만들 때 assembleSharedXCFramework 작업이 필요한 Package.swift 파일을 자동 생성할 수 있게 됐습니다.

KMP
 ↓
Gradle
 ↓
XCFramework
 +
Package.swift
 ↓
SwiftPM
 ↓
Xcode

여기서 주의할 점도 있습니다.

모든 KMP 프로젝트에서 Package.swift가 자동으로 만들어진다는 의미는 아닙니다.

이번 기능은 특히 SwiftPM dependency가 포함된 XCFramework를 Swift Package 형태로 배포하는 과정을 단순화하는 변화입니다.


Native Build도 계속 줄이려 한다

KMP 프로젝트에서 실제 개발 경험을 좌우하는 부분 중 하나가 Build Time입니다.

순수 iOS 프로젝트와 다르게

Gradle
+
Kotlin Compiler
+
Kotlin/Native
+
Xcode

가 함께 움직입니다.

Shared Module이 커지면 Kotlin/Native Compile Time도 체감됩니다.

2.4.20에서는 .klib artifact를 대상으로 한 Incremental Compilation도 개선됐습니다.

현재는 Beta 기능이며

kotlin.incremental.native=true

를 통해 사용할 수 있습니다.

목표는 단순합니다.

작은 코드 변경
       ↓
Native 전체 재빌드

가 아니라

작은 코드 변경
       ↓
변경된 부분 중심 Compile

로 가는 것입니다.

Interop이 아무리 좋아져도 Build가 느리면 실제 개발 경험은 좋기 어렵습니다.

JetBrains가 Swift Export와 Native Build를 동시에 개선하고 있는 이유입니다.


그런데 iPhone Duo가 나오면서 새로운 질문이 생겼다

여기까지는 Kotlin 2.4.20 자체의 이야기입니다.

그런데 최근 발표된 iPhone Duo를 같이 보면 KMP UI 전략에도 재미있는 문제가 하나 생깁니다.

Apple은 Duo의 접힌 화면과 펼쳐진 내부 화면에서 표준 Navigation Container가 자동으로 적응하도록 설계했습니다.

대표적으로

TabView
NavigationSplitView

UITabBarController
UISplitViewController

입니다.

접힌 상태에서는 Compact한 Navigation으로 동작하고,

내부의 넓은 화면에서는 Sidebar나 Split 구조를 사용할 수 있습니다.

즉 같은 iPhone 앱이

Compact
   ↓
Tab Bar

에서

Expanded
   ↓
Sidebar

로 바뀔 수 있습니다.

여기서 KMP를 사용하는 앱도 UI를 어떻게 구성했느냐에 따라 차이가 생깁니다.


KMP + SwiftUI라면 사실 큰 문제가 없다

KMP로 비즈니스 로직만 공유하고 UI를 SwiftUI로 만든 앱이라면 Duo 대응 때문에 KMP 자체를 크게 걱정할 이유는 없습니다.

구조가 이런 식이기 때문입니다.

┌────────────────────────────┐
│            iOS             │
│                            │
│ SwiftUI                    │
│ TabView                    │
│ NavigationSplitView        │
│ Native Sidebar             │
│ Safe Area                  │
│ iPhone Duo Adaptivity      │
└─────────────┬──────────────┘
              │
         Swift Export
              │
┌─────────────▼──────────────┐
│         Shared KMP         │
│                            │
│ Domain                     │
│ Repository                 │
│ Network                    │
│ Validation                 │
│ Business Logic             │
└────────────────────────────┘

iOS Navigation은 Apple Framework가 담당합니다.

따라서 Apple이 새로운 Sidebar나 Window Adaptivity를 추가하면 SwiftUI 쪽에서 대응하면 됩니다.

KMP는 그 아래의 Business Logic을 계속 제공합니다.

이 구조에서는 KMP가 오히려 거의 보이지 않습니다.


UIKit 시스템 Navigation을 사용해도 마찬가지다

꼭 SwiftUI여야 하는 것도 아닙니다.

UIKit에서도

UITabBarController
UINavigationController
UISplitViewController

같은 시스템 Navigation을 사용한다면 Apple이 제공하는 Adaptivity를 활용할 수 있습니다.

Compose Multiplatform 화면을 사용하더라도 ComposeUIViewController를 Native Navigation 아래에 넣는 구조가 가능합니다.

예를 들면

UITabBarController
        ↓
UINavigationController
        ↓
ComposeUIViewController
        ↓
Compose Screen

입니다.

JetBrains 공식 가이드에서도 이와 비슷하게 SwiftUI 또는 UIKit이 Navigation을 담당하고 Compose는 Screen Content를 그리는 Hybrid 구조를 안내하고 있습니다.

즉 중요한 것은

SwiftUI인가?
Compose인가?

만이 아닙니다.

더 중요한 것은

누가 Navigation을 소유하고 있느냐입니다.


진짜 문제가 될 수 있는 건 오래된 Custom Navigation이다

iPhone Duo에서 더 신경 써야 할 쪽은 KMP가 아니라 오히려 이런 코드입니다.

Custom Tab Bar
Custom Navigation Bar
Custom Sidebar
Fixed Width Layout
Orientation 기반 Layout
UIScreen.main 기반 계산

예를 들어 SwiftUI에서 시스템 TabView 대신 이런 식으로 Tab Bar를 직접 만들었다고 해보겠습니다.

ZStack(alignment: .bottom) {
    content

    CustomTabBar()
        .frame(height: 64)
}

여기에

let width = UIScreen.main.bounds.width

같은 계산까지 들어가 있다면 Duo에서는 확인해야 할 부분이 많아집니다.

Apple의 표준 Navigation은 Duo에서

접힘
펼침
Sidebar
Split
Safe Area
Camera 영역

등을 시스템이 알고 처리합니다.

Custom Navigation은 그 정보를 직접 반영해야 합니다.


Duo에서는 Safe Area조차 좌우가 같다는 보장이 없다

이 부분도 중요합니다.

Apple은 iPhone Duo에서 Safe Area와 Layout Margin이 비대칭일 수 있다고 안내하고 있습니다.

따라서 이런 가정도 위험해집니다.

left inset == right inset

Custom Tab Bar나 Custom Navigation Bar에서 Safe Area를 직접 계산했다면 반드시 다시 확인해야 합니다.

특히

UIScreen.main.bounds

처럼 Main Screen을 직접 참조하는 방식도 Duo 같은 Multi-display Device에서는 모호해집니다.

Apple은 Layout 판단에

Environment
Trait Collection
Scene Bounds
Size Class

를 사용하는 방향을 권장하고 있습니다.


Orientation 기준 Layout도 다시 봐야 한다

오래된 iOS 앱에는 이런 코드도 많습니다.

if orientation.isLandscape {
    showExpandedLayout()
}

하지만 Duo 내부 Display에서는 기존 Orientation 가정을 그대로 사용할 수 없습니다.

따라서

Portrait
Landscape

보다

Compact
Regular
Available Width
Scene Bounds

를 기준으로 Layout을 판단하는 것이 중요해집니다.

이 문제 역시 KMP와 직접적인 관계가 없습니다.

Swift로 만들었더라도 Custom Layout을 오래된 방식으로 구성했다면 똑같이 문제가 됩니다.


Compose Multiplatform으로 UI 전체를 공유했다면?

여기에서는 조금 이야기가 달라집니다.

Compose Multiplatform 자체도 Adaptive Layout을 지원합니다.

WindowSizeClass나 Material 3 Adaptive API 등을 활용해서

Compact
Medium
Expanded

환경에 따라 다른 Layout을 만들 수 있습니다.

따라서

Duo 외부 화면

Bottom Navigation
Duo 내부 화면

Navigation Rail
또는
Sidebar 형태 UI

를 직접 구현하는 것은 가능합니다.

문제는 이게 Apple의 Native Sidebar와 동일한 것은 아니라는 점입니다.

Compose에서 직접 만든 Sidebar는 Compose Component입니다.

Apple의

TabView
NavigationSplitView
UITabBarController
UISplitViewController

가 제공하는 시스템 Navigation과는 다릅니다.


이미 Liquid Glass에서 같은 문제가 한번 드러났다

이 부분은 Duo만의 새로운 문제가 아닙니다.

iOS 26의 Liquid Glass에서도 같은 일이 있었습니다.

Compose Multiplatform으로 Tab Bar와 Navigation 전체를 직접 그리면 Apple의 Native Tab Bar에 자동으로 적용되는 Liquid Glass 효과를 그대로 받을 수 없습니다.

그래서 JetBrains 공식 가이드에서도 한 가지 현실적인 Architecture를 제시합니다.

SwiftUI

TabView
NavigationStack
      ↓
Compose
Screen Content

즉 Navigation은 Apple의 Native Component가 담당하고,

화면 Content는 Compose가 담당하는 방식입니다.

UIKit에서도 같은 구조가 가능합니다.

UITabBarController
UINavigationController
        ↓
ComposeUIViewController

Liquid Glass에서 나온 이 문제가 iPhone Duo에서도 똑같은 Architecture 질문으로 이어집니다.


Duo에서는 이 차이가 더 커질 수 있다

Liquid Glass는 주로 Visual Style의 문제였습니다.

하지만 Duo는 조금 다릅니다.

이번에는 Navigation 구조 자체가 바뀝니다.

Compact
   ↓
Tab Bar

에서

Expanded
   ↓
Sidebar
   ↓
Split Content

로 전환될 수 있습니다.

시스템 Navigation을 사용하면 이 변화에 Apple Framework가 적극적으로 참여합니다.

반면 Custom Navigation이나 Compose에서 직접 만든 Navigation이라면 이 Adaptive Behavior를 직접 구현해야 합니다.

그래서 앞으로 KMP UI를 설계할 때도 질문이 달라집니다.

Compose로 만들 수 있는가?

보다

Compose로 직접 만들어야 하는가?

가 더 중요해집니다.


결국 KMP보다 Custom UI가 더 큰 변수다

정리하면 iPhone Duo 대응에서 문제를 단순히

Native SwiftUI = 안전

KMP = 위험

으로 볼 수는 없습니다.

실제로는 이렇게 보는 편이 더 정확합니다.

KMP + SwiftUI Navigation
        ↓
대응 수월

KMP + UIKit System Navigation
        ↓
대응 수월

Compose Content
+ Native Navigation
        ↓
대응 수월

반면

Swift + Custom Tab Bar
        ↓
직접 대응 필요

Compose + Custom Navigation
        ↓
직접 대응 필요

UIScreen / Orientation
기반 Layout
        ↓
직접 대응 필요

입니다.

즉 문제의 기준은 언어가 아닙니다.

얼마나 시스템 UI와 Navigation을 활용하고 있는가가 더 중요합니다.


Swift Export가 좋아지는 것이 여기에서도 의미가 있다

이 지점에서 다시 Kotlin 2.4.20의 Swift Export로 돌아오면 이야기가 연결됩니다.

Swift Export가 좋아질수록

Business Logic
     ↓
Kotlin

Platform UI
     ↓
SwiftUI / UIKit

를 분리하는 비용이 낮아집니다.

예전에는 이 경계에서 Objective-C Interop과 Wrapper Code가 많이 필요했습니다.

그 비용이 줄어든다면 굳이 Apple 플랫폼 UI까지 Kotlin으로 공유할 이유도 줄어들 수 있습니다.

Shared

Domain
Repository
Network
Business Logic

──────────────

Native iOS

Tab Bar
Sidebar
Navigation
Safe Area
Liquid Glass
Duo Adaptivity

이런 구조가 더 매력적으로 보이는 이유입니다.


그렇다고 Compose UI 공유가 의미 없다는 것은 아니다

Compose Multiplatform UI를 공유하는 것도 분명 장점이 있습니다.

화면이 많은 앱에서 Android와 iOS UI를 동시에 개발해야 한다면 공유 효과가 큽니다.

다만 플랫폼 변화가 빠르게 들어오는 영역은 판단이 필요합니다.

대표적으로

Navigation
System Bar
Window Behavior
Platform Animation
Safe Area
New Form Factor

같은 부분입니다.

이 영역까지 Custom UI로 가져가면 공유 코드는 늘어납니다.

대신 Apple의 새로운 UI 변화에 대응해야 하는 책임도 App 쪽으로 넘어옵니다.

따라서 좋은 KMP Architecture는 공유율 자체를 최대화하는 것이 아니라

공유했을 때 이득이 큰 영역과 Native로 남겼을 때 이득이 큰 영역을 구분하는 것

에 가깝습니다.


Kotlin 2.4.20과 iPhone Duo를 같이 보면 이런 구조가 보인다

앞으로 상당히 현실적인 KMP Architecture는 이런 형태일 수 있습니다.

┌───────────────────────────────┐
│             iOS               │
│                               │
│ SwiftUI / UIKit               │
│                               │
│ TabView / UITabBarController  │
│ Sidebar                       │
│ NavigationSplitView           │
│ Safe Area                     │
│ Liquid Glass                  │
│ iPhone Duo                    │
└───────────────┬───────────────┘
                │
          Swift Export
                │
┌───────────────▼───────────────┐
│          Shared KMP           │
│                               │
│ Domain                        │
│ Repository                    │
│ Network                       │
│ Validation                    │
│ Business Rules                │
└───────────────┬───────────────┘
                │
┌───────────────▼───────────────┐
│           Android             │
│                               │
│ Compose                       │
└───────────────────────────────┘

여기서 중요한 것은 SwiftUI를 많이 사용한다는 것이 아닙니다.

플랫폼의 책임과 Shared Logic의 책임을 명확하게 나눈다는 것입니다.


Swift Export는 아직 Alpha다

물론 지금 바로 모든 KMP 프로젝트를 이 구조로 바꿔야 한다는 의미는 아닙니다.

Swift Export는 여전히 Alpha 단계입니다.

현재도 몇 가지 제한이 있습니다.

  • 일부 Kotlin 타입 Export 제한
  • Generic Type Parameter의 Type Erasure
  • 기존 Objective-C Interop 프로젝트 자동 Migration 부재
  • 일부 기능에 별도 설정 필요
  • API와 동작이 변경될 가능성

따라서 Production Project에서는 전면 전환보다는 작은 Module부터 검증하는 방식이 현실적입니다.

다만 JetBrains가

Swift Export
SwiftPM
Cross-language inheritance
Incremental Compilation

을 계속 동시에 개선하고 있다는 것은 방향을 보여줍니다.


iOS 개발자가 KMP를 볼 때 질문도 달라져야 한다

예전에는 KMP를 검토할 때 주로 이런 질문을 했습니다.

몇 %까지 코드를 공유할 수 있는가?

하지만 앞으로는 이 질문이 더 중요할 수 있습니다.

어떤 코드를 공유해야
두 플랫폼 모두 자연스러운가?

예를 들어

Domain
Repository
API
Validation
Business Rule

은 공유 효과가 큽니다.

반면

Tab Bar
Navigation
Sidebar
Safe Area
Platform Interaction

은 플랫폼 변화의 영향을 직접 받습니다.

iPhone Duo처럼 새로운 Form Factor가 등장할수록 이 차이는 더 분명해집니다.


마치며

Kotlin 2.4.20의 Swift Export는 겉으로 보면 KMP의 Interop 개선입니다.

하지만 조금 넓게 보면 더 흥미로운 방향이 보입니다.

KMP가 iOS UI를 더 많이 가져가는 것이 아니라,

Kotlin과 Swift가 서로 잘하는 영역을 유지하면서 경계를 더 얇게 만드는 방향입니다.

그리고 iPhone Duo는 이 Architecture가 왜 중요한지를 보여주는 좋은 사례가 됐습니다.

Duo 대응에서 가장 걱정해야 할 것은 KMP 자체가 아닙니다.

오히려

Custom Tab Bar
Custom Navigation
UIScreen.main
Orientation 분기
고정 Width
Safe Area 수동 계산

같은 오래된 UI 코드가 더 큰 변수입니다.

Apple의 시스템 TabView, NavigationSplitView, UITabBarController, UISplitViewController를 사용하고 있다면 새로운 화면 형태와 Sidebar에 대응하기가 훨씬 수월합니다.

KMP 역시 마찬가지입니다.

Business Logic은 Kotlin으로 공유하고,

Navigation과 Apple 고유 UI는 SwiftUI나 UIKit에 맡길 수 있습니다.

Compose를 사용하더라도 Native Navigation 안에 Compose Screen을 넣는 Hybrid 구조가 가능합니다.

결국 Kotlin 2.4.20과 iPhone Duo를 같이 보면 하나의 결론으로 이어집니다.

KMP의 경쟁력은 UI까지 얼마나 많이 공유하느냐보다, 공유할 가치가 있는 로직을 얼마나 자연스럽게 Swift에 전달할 수 있느냐에 있다.

Swift Export가 발전하는 이유도 결국 그 경계를 줄이기 위해서입니다.

그리고 새로운 Apple Hardware가 등장할수록,

공유 코드와 플랫폼 코드의 경계를 어디에 둘 것인가

라는 질문은 더 중요해질 것 같습니다.

참고자료

  • Kotlin Documentation — What's new in Kotlin 2.4.20
    Swift Export의 sealed class/interface 지원, cross-language inheritance, SwiftPM dependency용 Package.swift 생성, Kotlin/Native Incremental Compilation 개선을 확인할 수 있습니다.
    Kotlin 2.4.20 공식 변경사항

  • Kotlin Documentation — Interoperability with Swift using Swift Export
    Swift Export의 현재 지원 범위와 Alpha 상태, multi-module, package preservation 등 Swift와 Kotlin 사이의 새로운 Interop 방향을 확인할 수 있습니다.
    Kotlin Swift Export 공식 문서

  • Kotlin Documentation — SwiftPM Export
    Kotlin Multiplatform에서 XCFramework와 Swift Package Manager를 연결하는 방식을 확인할 수 있습니다.
    Kotlin SwiftPM 공식 문서

  • Apple Developer — Prepare your app for iPhone Duo
    iPhone Duo 내부 화면의 Size Class, Sidebar, Adaptive Layout, Safe Area와 시스템 Navigation의 동작을 확인할 수 있습니다.
    Apple iPhone Duo 대응 공식 Tech Talk

공식 자료 기준으로 보면, Kotlin 2.4.20의 Swift Export는 Kotlin과 Swift 사이에서 필요했던 Objective-C 기반 Interop을 점점 줄이는 방향으로 발전하고 있습니다.

이번 버전에서는 sealed hierarchy를 Swift에서 더 자연스럽게 처리할 수 있고, Swift에서 Kotlin Interface를 구현하는 cross-language inheritance도 추가됐습니다. SwiftPM dependency가 포함된 XCFramework 배포 과정도 조금 더 단순해졌습니다.

다만 Swift Export 자체는 아직 Alpha이므로 기존 Production 프로젝트를 바로 전면 전환하기보다는 작은 Module부터 검증하는 방식이 현실적입니다.

iPhone Duo까지 연결해서 보면 더 중요한 기준은 KMP 자체가 아닙니다.

Shared KMP
Domain / Repository / Network
        ↓
Swift Export
        ↓
SwiftUI / UIKit
        ↓
TabView
NavigationSplitView
Sidebar
        ↓
iPhone Duo

이 구조라면 Apple의 새로운 Navigation과 Adaptive Layout을 그대로 활용할 수 있습니다.

반대로 KMP 여부와 관계없이 Custom Tab Bar, Custom Navigation, 고정 Width, UIScreen.main, Orientation 기반 Layout을 많이 사용했다면 Duo 대응을 직접 해야 하는 영역이 늘어납니다.

따라서 KMP의 현실적인 방향은 Business Logic은 공유하고 Apple 플랫폼의 Navigation과 System UI는 Native Framework에 맡기는 구조로 보는 것이 자연스럽습니다.

profile
iOS 앱 개발자

0개의 댓글