TECH

Kotlin Multiplatform로 iOS와 안드로이드 비즈니스 로직 공유하기

Kotlin Multiplatform로 iOS와 안드로이드가 비즈니스 로직을 공유하는 크로스플랫폼 아키텍처를 정리한다. expect/actual, 네트워킹, 상태 관리, SwiftUI 연동까지 실무 관점으로 다룬다.

Kotlin Multiplatform로 iOS와 안드로이드 비즈니스 로직 공유하기

Kotlin Multiplatform로 iOS와 안드로이드 비즈니스 로직 공유하기

모바일 앱 개발에서 iOS와 안드로이드를 각각 네이티브로 만들면 같은 로직을 두 번 작성해야 한다. 크로스플랫폼 해법은 여럿이지만, Kotlin Multiplatform은 독특한 균형점을 제시한다. UI는 각 플랫폼의 네이티브 방식을 유지하면서, 비즈니스 로직과 데이터 계층만 공유하는 전략이다. 2026년 현재 KMP는 안정 단계를 지나 상용 서비스에서 폭넓게 채택되고 있다. 이 글은 KMP로 로직을 공유하는 아키텍처를 실전 관점에서 다룬다.

KMP의 철학과 expect/actual 메커니즘

KMP의 핵심 발상은 공유할 것과 그렇지 않을 것을 명확히 나누는 데 있다. 네트워크 통신, 데이터 파싱, 유효성 검증, 상태 관리 같은 비즈니스 로직은 플랫폼과 무관하므로 공유 모듈에 한 번만 작성한다. 반면 UI는 안드로이드에서 Jetpack Compose, iOS에서 SwiftUI로 각각 네이티브 경험을 구현한다. 이 접근은 UI까지 통합하는 방식보다 각 플랫폼의 디자인 가이드라인을 온전히 따를 수 있어, 사용자 경험의 타협이 적다.

플랫폼별로 구현이 달라야 하는 부분은 expect와 actual 메커니즘으로 처리한다. 공유 코드에서 expect로 인터페이스를 선언하면, 각 플랫폼이 actual로 구체적 구현을 제공한다. 예컨대 안전한 저장소 접근은 안드로이드에서 키스토어, iOS에서 키체인을 사용하는데, 공유 코드는 이 차이를 모른 채 추상화된 인터페이스만 호출한다. 이 구조 덕분에 플랫폼 특화 기능을 쓰면서도 공유 로직의 순수성을 유지할 수 있다.

// 공유 모듈: 플랫폼별 구현을 기대
expect class SecureStorage() {
    fun save(key: String, value: String)
    fun load(key: String): String?
}

네트워킹과 상태 관리 공유

KMP 생태계에는 멀티플랫폼을 겨냥한 라이브러리가 잘 갖춰져 있다. 네트워킹은 코루틴 기반의 비동기 HTTP 클라이언트로 처리하는데, 하나의 API 클라이언트 코드가 두 플랫폼에서 그대로 동작한다. 직렬화는 컴파일 타임에 코드를 생성하는 방식이라 리플렉션 없이 빠르고, JSON과 도메인 모델 간 변환을 타입 안전하게 다룬다. 로컬 데이터베이스도 멀티플랫폼 지원 라이브러리로 공유 계층에 둘 수 있어, 오프라인 캐싱 로직까지 한 번에 작성한다.

상태 관리에서 코루틴과 플로우는 KMP의 강력한 무기다. 비동기 데이터 스트림을 플로우로 표현하면, 안드로이드에서는 자연스럽게 소비되고 iOS에서도 브리지를 통해 관찰할 수 있다. 뷰모델 로직을 공유 모듈에 두면 화면의 상태 전이와 이벤트 처리까지 공통화되어, 플랫폼 간 동작 불일치가 근본적으로 줄어든다. 다만 iOS는 코루틴의 취소와 생명주기를 자동으로 관리하지 않으므로, 스코프를 명시적으로 정리하는 규약을 팀 차원에서 세워야 누수를 막을 수 있다.

iOS 연동과 실전 도입 전략

공유 모듈은 iOS에서 프레임워크로 컴파일되어 스위프트 코드에서 호출된다. 다만 코틀린의 모든 개념이 스위프트로 매끄럽게 매핑되지는 않는다. 코루틴의 suspend 함수는 스위프트의 async와 자연스럽게 연결되도록 개선되어 왔지만, 제네릭이나 봉인 클래스 같은 일부 구조는 브리지 과정에서 표현력이 제한된다. 따라서 공유 모듈의 공개 인터페이스는 iOS 소비를 염두에 두고, 스위프트에서 다루기 편한 형태로 설계하는 배려가 필요하다.

도입 전략은 점진적 접근이 정석이다. 기존 앱을 한 번에 전환하기보다, 새로운 기능이나 명확히 분리된 모듈부터 KMP로 시작해 팀이 노하우를 쌓는 편이 안전하다. 네트워크 계층이나 유효성 검증처럼 UI와 결합도가 낮고 로직이 명확한 영역이 첫 후보로 적합하다. 성공적으로 안착하면 데이터 계층과 뷰모델로 공유 범위를 넓혀간다. KMP의 진짜 가치는 완벽한 코드 통합이 아니라, 중복 구현으로 인한 버그와 유지보수 부담을 줄이면서도 각 플랫폼의 네이티브 품질을 지키는 실용적 균형에 있다. 팀의 역량과 서비스 특성을 고려해 공유 범위를 신중히 정하는 것이 성공의 관건이다.

빌드 설정과 의존성 관리

KMP 프로젝트의 진입 장벽 중 하나는 빌드 설정이다. Gradle을 중심으로 여러 타깃을 선언하고 각 타깃별 소스 세트를 구성해야 하는데, 공통 코드가 놓이는 소스 세트와 플랫폼별 소스 세트의 의존성 관계를 정확히 이해하는 것이 중요하다. 공통 코드는 멀티플랫폼을 지원하는 라이브러리만 사용할 수 있고, 플랫폼별 소스 세트에서는 각 플랫폼의 네이티브 의존성을 자유롭게 쓸 수 있다. 버전 카탈로그로 의존성 버전을 한곳에서 관리하면, 여러 모듈에 걸친 버전 불일치를 예방하고 업그레이드를 일관되게 처리할 수 있다.

iOS 빌드는 추가적인 고려가 필요하다. 공유 모듈을 iOS 프로젝트에 연결하는 방식으로는 직접 프레임워크를 임베드하거나 코코아팟, 스위프트 패키지 매니저를 통한 배포가 있다. 팀의 iOS 개발 워크플로에 맞는 방식을 선택하되, 공유 모듈이 변경될 때마다 iOS 쪽에서 최신 빌드를 반영하는 절차를 자동화해두어야 개발 마찰이 줄어든다. 빌드 시간이 길어지는 문제는 KMP의 알려진 단점이므로, 증분 빌드와 캐싱을 적극 활용하고 불필요한 타깃 빌드를 피하는 구성이 생산성에 직접 영향을 준다.

테스트 전략도 공유의 이점을 극대화하는 열쇠다. 공유 모듈의 비즈니스 로직은 공통 소스 세트에 테스트를 작성해 한 번만 검증하면 모든 플랫폼에서 동일하게 보장된다. 플랫폼별 구현이 다른 부분은 각 타깃에서 별도로 테스트한다. 지속적 통합 파이프라인에서 안드로이드와 iOS 양쪽의 빌드와 테스트를 모두 돌려, 어느 한쪽만 깨지는 상황을 조기에 잡아내는 체계를 갖추는 것이 안정적인 크로스플랫폼 개발의 마무리다. 이런 기반이 갖춰지면 KMP는 두 플랫폼의 품질을 동시에 끌어올리는 든든한 지렛대가 된다.