Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Kotlin Multiplatform Meetup - Compose Multiplat...

Avatar for Suhyeon Kim Suhyeon Kim
December 21, 2025

Kotlin Multiplatform Meetup - Compose Multiplatform 외부 의존성 아키텍처 설계부터 운영까지

Avatar for Suhyeon Kim

Suhyeon Kim

December 21, 2025
Tweet

More Decks by Suhyeon Kim

Other Decks in Programming

Transcript

  1. Kotlin User Groups Seoul Kotlin Multiplatform Meetup Compose Multiplatform 외부

    의존성 아키텍처 설계부터 운영까지 세 가지 Case Study로 알아보는 코드 레벨의 고민 Suhyeon Kim 우아한형제들 Android 개발자 & 교육자 GDG Korea Android Organizer
  2. Kotlin User Groups Seoul 발표자 소개 - 우아한형제들 Android 개발&교육

    - 우아한테크코스 Android 코치 - NEXTSTEP 강사/코드 리뷰어 - 개발자 커뮤니티 Organizer (GDG Korea Android, DroidKnights 등) GitHub: @wisemuji Gmail: [email protected]
  3. Kotlin User Groups Seoul 목차 - Compose Multiplatform: 희망편 -

    Compose Multiplatform: 절망편 - Case Study1: 네이티브 상태 옵저버 구현 - Case Study2: KMP 미지원 외부 라이브러리(Firebase) 연동 + Compose Multiplatform 아키텍처 설계 - Case Study3: iOS 모듈에 CocoaPods 설치 - KotlinConf 2025에서 JetBrains 개발자에게 피드백 받은 썰 +) 팀이 얻은 러닝 +) 구체적인 코드 레벨의 고민
  4. Kotlin User Groups Seoul 들어가기 전에… - Android 개발에 익숙한

    사람의 경험(iOS 초보) - KMP, Compose Multiplatform에 대한 기초 개념 설명은 최소화 - 실제 운영 경험과 케이스 스터디 위주의 구성(정답 X) - 설계 측면과 코드 레벨에서의 고민 포함 - 더 좋은 제안과 토론 환영
  5. Kotlin User Groups Seoul Compose Multiplatform: 희망편 하나의 코드베이스, 두

    개의 플랫폼. Android의 선언형 UI(Jetpack Compose) 경험을 iOS로 확장 출처: JetBrains의 Compose Multiplatform 공식 홈페이지
  6. Kotlin User Groups Seoul Compose Multiplatform: 희망편 하나의 코드베이스, 두

    개의 플랫폼. Android의 선언형 UI(Jetpack Compose) 경험을 iOS로 확장 출처: JetBrains의 Compose Multiplatform 공식 홈페이지
  7. Kotlin User Groups Seoul Compose Multiplatform: 희망편 하나의 코드베이스, 두

    개의 플랫폼. Android의 선언형 UI(Jetpack Compose) 경험을 iOS로 확장 출처: JetBrains의 Compose Multiplatform 공식 홈페이지 -> 안드로이드 개발자에겐 🍯이네
  8. Kotlin User Groups Seoul Compose Multiplatform: 희망편 하나의 코드베이스, 두

    개의 플랫폼. Android의 선언형 UI(Jetpack Compose) 경험을 iOS로 확장 출처: JetBrains의 Compose Multiplatform 공식 홈페이지 -> 안드로이드 개발자에겐 🍯이네 -> 어마무시한 착각
  9. Kotlin User Groups Seoul 팀 상황: 우아한테크코스 모바일 인하우스 앱

    개발을 위한 기술 스택 선택 필요 Compose Multiplatform: 희망편 󰱹 서X수님 (Android 개발자) 󰞦 정X준님 (Android 개발자) 󰟲 나 (Android 개발자) 우아한테크코스 인하우스 앱 (내부 교육생 출결 관리 시스템) 3개월 내에 출시 필요 우리는 iOS 개발자가 없음 백엔드 개발자도 Kotlin 스택 우아한테크코스 앱 개발 (내부 교육생 출결 관리 시스템) 2개월 내에 출시 필요 팀에 iOS 개발자가 없음 백엔드 개발자도 Kotlin 스택 우리팀 모바일 개발자 구성
  10. Kotlin User Groups Seoul 팀 상황: 우아한테크코스 모바일 인하우스 앱

    개발을 위한 기술 스택 선택 필요 󰱹 서X수님 (Android 개발자) 󰞦 정X준님 (Android 개발자) 󰟲 나 (Android 개발자) 우리팀 모바일 개발자 구성 우아한테크코스 인하우스 앱 (내부 교육생 출결 관리 시스템) 3개월 내에 출시 필요 우리는 iOS 개발자가 없음 백엔드 개발자도 Kotlin 스택 우아한테크코스 앱 개발 (내부 교육생 출결 관리 시스템) 2개월 내에 출시 필요 팀에 iOS 개발자가 없음 백엔드 개발자도 Kotlin 스택 이거 완전 Compose Multiplatform하기 딱 좋은 환경인 걸? Compose Multiplatform: 희망편
  11. Kotlin User Groups Seoul Compose Multiplatform: 희망편 초기 모듈 구성

    계획 이론상 Compose UI 100% = iOS 몰라도 개발 가능?
  12. Kotlin User Groups Seoul Compose Multiplatform: 희망편 성공적인 iOS/Android 앱

    출시, 운영 - 약 2개월간의 개발 끝에 MVP 버전 iOS/Android 각 플랫폼에 출시 완료 - Compose UI 100% - Firebase Distribution, TestFlight 등으로 배포 - 150명의 교육생 대상으로 운영 - 올해 5월에 Compose Multiplatform 1.8.0 출시되며 iOS도 Stable 단계로 격상
  13. Kotlin User Groups Seoul // commonMain expect object Log {

    fun e(tag: String, message: String, throwable: Throwable? = null) fun d(tag: String, message: String) fun i(tag: String, message: String) } // androidMain actual object Log { actual fun e(tag: String, message: String, throwable: Throwable?) { Log.e(tag, message, throwable) } // ... } // iosMain actual object Log { actual fun e(tag: String, message: String, throwable: Throwable?) { if (throwable != null) { NSLog("ERROR: [$tag] $message. Throwable: $throwable") } else { NSLog("ERROR: [$tag] $message") } } // ... }
  14. Kotlin User Groups Seoul 1. Native UI Events (e.g., Push

    Notifications) 2. Asynchronous Platform Logic (e.g., Force Update Check) 3. Integrating Third-Party Native SDKs (e.g., Firebase Remote Config) Compose Multiplatform: 절망편 expect/actual API를 활용하기 어려운 상황
  15. Kotlin User Groups Seoul 1. Native UI Events (e.g., Push

    Notifications) 푸시 알림이 기기로 전달되는 상황. 사용자가 이를 탭하면 애플리케이션은 특정 화면으로 이동하는 등 공유 모듈에 위치한 비즈니스 로직을 트리거 해야 한다. 하지만 푸시 알림은 네이티브 코드가 필수적으로 필요하므로 네이티브 의존성 없이 다루기는 어렵다. Compose Multiplatform: 절망편 expect/actual API를 활용하기 어려운 상황
  16. Kotlin User Groups Seoul 2. Asynchronous Platform Logic (e.g., Force

    Update Check) 앱 스토어 또는 플레이 스토어에서 필수 업데이트가 있는지 확인해야 한다. 이는 플랫폼마다 다르게 처리되는 비동기 네트워크 호출을 수반한다. 플랫폼 코드는 이 작업을 백그라운드에서 수행하고 완료되면 콜백 또는 리스너를 통해 commonMain에 결과를 전달해야 한다. 하지만 commonMain의 단순 함수 호출로는 비동기적 지연 응답을 처리할 수 없다. Compose Multiplatform: 절망편 expect/actual API를 활용하기 어려운 상황
  17. Kotlin User Groups Seoul 3. Integrating Third-Party Native SDKs (e.g.,

    Firebase Remote Config) 네이티브 Firebase SDK를 사용하여 Remote Config 상태를 가져온다. 상태가 변경되면 공유된 Compose UI를 리컴포지션하여 변경사항 반영해야 한다. 코드가 Firebase 상태를 요청하는 것이 아니라, 상태 변경 시 Firebase SDK가 코드에 알려야 한다. 즉, 공유 코드는 능동적인 호출자가 아닌, 수동적인 리스너 역할을 해야 한다. Compose Multiplatform: 절망편 expect/actual API를 활용하기 어려운 상황 출처: Observer Pattern from refactoring.guru
  18. Kotlin User Groups Seoul Case Study1: 네이티브 상태 옵저버 구현

    [요구사항] • 강제 업데이트 기능 개발 필요 • 권장 앱 버전보다 낮으면 앱 초기 진입 불가
  19. Kotlin User Groups Seoul Case Study1: 네이티브 상태 옵저버 구현

    초기 모듈 구성 계획 다시보기 iOS 모듈에서 발생하는 이벤트를 Kotlin 모듈에서 수신할 수 있는가?
  20. Kotlin User Groups Seoul // composeApp/iosMain fun MainViewController() = ComposeUIViewController

    { App() } // iosApp/ContentView.swift struct ComposeView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { MainViewControllerKt.MainViewController() ... } [기존]
  21. Kotlin User Groups Seoul // composeApp/iosMain object SharedViewControllers { private

    data class ViewState(val requiredAppVersionCode: Long = 0) private val state = MutableStateFlow(ViewState()) fun mainViewController(onFinish: () -> Unit): UIViewController { return ComposeUIViewController { // This subscribes to the StateFlow state. val viewState by state.collectAsStateWithLifecycle() if (viewState.requiredAppVersionCode > currentAppVersionCode) { ... } App() } } // This is called from SwiftUI. fun updateRequiredAppVersionCode(code: Long) { state.value = state.value.copy(requiredAppVersionCode = code) } } [변경 후]
  22. Kotlin User Groups Seoul // composeApp/iosMain object SharedViewControllers { private

    data class ViewState(val requiredAppVersionCode: Long = 0) private val state = MutableStateFlow(ViewState()) fun mainViewController(onFinish: () -> Unit): UIViewController { return ComposeUIViewController { // This subscribes to the StateFlow state. val viewState by state.collectAsStateWithLifecycle() if (viewState.requiredAppVersionCode > currentAppVersionCode) { ... } App() } } // This is called from SwiftUI. fun updateRequiredAppVersionCode(code: Long) { state.value = state.value.copy(requiredAppVersionCode = code) } } [변경 후] • StateFlow 는 UI State의 Single Source Of Truth 역할 • mainViewController()는 UIViewController를 리턴하는 Swift에서의 진입점 • collectAsStateWithLifecycle() 는 StateFlow 를 Compose의 상태로 변환하며 UI가 항상 최신값을 노출하도록 함
  23. Kotlin User Groups Seoul // composeApp/iosMain object SharedViewControllers { private

    data class ViewState(val requiredAppVersionCode: Long = 0) private val state = MutableStateFlow(ViewState()) fun mainViewController(onFinish: () -> Unit): UIViewController { return ComposeUIViewController { // This subscribes to the StateFlow state. val viewState by state.collectAsStateWithLifecycle() if (viewState.requiredAppVersionCode > currentAppVersionCode) { ... } App() } } // This is called from SwiftUI. fun updateRequiredAppVersionCode(code: Long) { state.value = state.value.copy(requiredAppVersionCode = code) } } [변경 후] 1. updateRequiredAppVersionCode()가 SwiftUI에서 트리거되면 2. Kotlin 코드에서 StateFlow를 업데이트한 후, UI 리컴포지션이 트리거됨
  24. Kotlin User Groups Seoul // iosApp/ContentView.swift import SwiftUI import ComposeApp

    // Import your KMP shared module if needed struct ComposeView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { // Call the bridge to get our Compose-powered UI return SharedViewControllers().mainViewController(onFinish: { ... }) } func updateRequiredAppVersionCode(code: Int64) { SharedViewControllers() .updateRequiredAppVersionCode(code: requiredAppVersionCode) } // ... } [변경후]
  25. Kotlin User Groups Seoul // iosApp/ContentView.swift func fetchRemoteConfig() { let

    remoteConfig = RemoteConfig.remoteConfig() remoteConfig.fetchAndActivate { status, error in if let error = error { NSLog("Error fetching and activating config: \(error)") } else { let appVersionCode = remoteConfig["required_app_version_code"].numberValue requiredAppVersionCode = appVersionCode.int64Value updateRequiredAppVersionCode(code: requiredAppVersionCode) } } } [변경후]
  26. Kotlin User Groups Seoul // iosApp/ContentView.swift import SwiftUI import ComposeApp

    // Import your KMP shared module if needed struct ComposeView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { // Call the bridge to get our Compose-powered UI return SharedViewControllers().mainViewController(onFinish: { ... }) } func updateRequiredAppVersionCode(code: Int64) { SharedViewControllers() .updateRequiredAppVersionCode(code: requiredAppVersionCode) } // ... } [변경후] 1. updateRequiredAppVersionCode()가 SwiftUI에서 트리거 되면 2. Kotlin 코드에서 StateFlow를 업데이트한 후, UI 리컴포지션이 트리거 됨
  27. Kotlin User Groups Seoul Case Study1: 네이티브 상태 옵저버 구현

    초기 모듈 구성 계획 다시보기 1. Remote Config 트리거됨
  28. Kotlin User Groups Seoul Case Study1: 네이티브 상태 옵저버 구현

    초기 모듈 구성 계획 다시보기 1. Remote Config 트리거됨 2. 변경된 데이터 전달받음 (Kotlin의 StateFlow 업데이트)
  29. Kotlin User Groups Seoul Case Study1: 네이티브 상태 옵저버 구현

    초기 모듈 구성 계획 다시보기 2. 변경된 데이터 전달받음 (Kotlin의 StateFlow 업데이트) 3. Compose UI 리컴포지션 1. Remote Config 트리거됨
  30. Kotlin User Groups Seoul Case Study1: 네이티브 상태 옵저버 구현

    [요구사항] • 강제 업데이트 기능 개발 필요 • 권장 앱 버전보다 낮으면 앱 초기 진입 불가 = 해결 완료
  31. Kotlin User Groups Seoul Case Study2: KMP 미지원 외부 라이브러리

    연동 초기 모듈 구성 계획 다시보기
  32. Kotlin User Groups Seoul // commonMain expect object Log {

    fun e(tag: String, message: String, throwable: Throwable? = null) fun d(tag: String, message: String) fun i(tag: String, message: String) } // androidMain actual object Log { actual fun e(tag: String, message: String, throwable: Throwable?) { Log.e(tag, message, throwable) } // ... } // iosMain actual object Log { actual fun e(tag: String, message: String, throwable: Throwable?) { if (throwable != null) { NSLog("ERROR: [$tag] $message. Throwable: $throwable") } else { NSLog("ERROR: [$tag] $message") } } // ... }
  33. Kotlin User Groups Seoul Case Study2: KMP 미지원 외부 라이브러리

    연동 [요구사항] • Firebase Crashlytics 연동 • 모니터링용 Logging 인터페이스 구현 • Firebase는 KMP SDK를 지원하지 않으므로 플랫폼별 네이티브 SDK 설치 필요 Firebase 등 KMP를 지원하지 않는 외부 라이브러리를 연동해야 하는 상황
  34. Kotlin User Groups Seoul // shared/androidMain/Log.android.kt import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.ktx.Firebase

    actual object Log { actual fun e(tag: String, message: String, throwable: Throwable?) { Log.e(tag, message, throwable) Firebase.crashlytics.recordException(throwable ?: Exception(message)) } // ... } androidMain(Kotlin/JVM)에선 Firebase의 의존성 참조 가능, iosMain(Kotlin/Native)에서는 불가능
  35. Kotlin User Groups Seoul // shared/iosMain/FirebaseCrashlyticsDelegate.kt interface FirebaseCrashlyticsDelegate { fun

    recordException(error: NSError) } // shared/iosMain/Log.ios.kt actual object Log { private var crashlyticsDelegate: FirebaseCrashlyticsDelegate? = null fun initCrashlyticsDelegate(delegate: FirebaseCrashlyticsDelegate) { crashlyticsDelegate = delegate } // ... } 1. commonMain 또는 shared 모듈에서 인터페이스 정의 - 공유 코드에 필요한 함수 정의하는 간단한 Kotlin 인터페이스 생성
  36. Kotlin User Groups Seoul // iosApp/DefaultFirebaseCrashlyticsDelegate.swift class DefaultFirebaseCrashlyticsDelegate: FirebaseCrashlyticsDelegate {

    func recordException(error: Error) { Crashlytics.crashlytics().record(error: error) } } 1. commonMain 또는 shared 모듈에서 인터페이스 정의 - 공유 코드에 필요한 함수 정의하는 간단한 Kotlin 인터페이스 생성 2. androidMain 및 iosMain 소스 세트(또는 네이티브 Swift 및 Kotlin 코드)에서 실제 Firebase SDK를 네이티브 코드로 래핑하여 이 인터페이스를 구현하는 클래스를 생성
  37. Kotlin User Groups Seoul // iosApp/iOSApp.swift class AppDelegate: NSObject, UIApplicationDelegate

    { func application(_ application: UIApplication,did…Options launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { FirebaseApp.configure() Log.shared.doInitCrashlyticsDelegate(delegate: Default…Delegate()) } // ... } 1. commonMain 또는 shared 모듈에서 인터페이스 정의 - 공유 코드에 필요한 함수 정의하는 간단한 Kotlin 인터페이스 생성 2. androidMain 및 iosMain 소스 세트(또는 네이티브 Swift 및 Kotlin 코드)에서 실제 Firebase SDK를 네이티브 코드로 래핑하여 이 인터페이스를 구현하는 클래스를 생성 3. 애플리케이션 시작 시 네이티브 코드(예: iOS의 AppDelegate)가 구현체의 인스턴스를 생성하여 shared 모듈에 전달, 이후 shared 모듈은 이 구현체에 대한 참조를 들고 있고, 앱 전반에서 사용됨
  38. Kotlin User Groups Seoul // iosApp/iOSApp.swift class AppDelegate: NSObject, UIApplicationDelegate

    { func application(_ application: UIApplication,did…Options launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { FirebaseApp.configure() Log.shared.doInitCrashlyticsDelegate(delegate: Default…Delegate()) } // ... } 1. commonMain 또는 shared 모듈에서 인터페이스 정의 - 공유 코드에 필요한 함수 정의하는 간단한 Kotlin 인터페이스 생성 2. androidMain 및 iosMain 소스 세트(또는 네이티브 Swift 및 Kotlin 코드)에서 실제 Firebase SDK를 네이티브 코드로 래핑하여 이 인터페이스를 구현하는 클래스를 생성 3. 애플리케이션 시작 시 네이티브 코드(예: iOS의 AppDelegate)가 구현체의 인스턴스를 생성하여 shared 모듈에 전달, 이후 shared 모듈은 이 구현체에 대한 참조를 들고 있고, 앱 전반에서 사용됨 여기서의 shared는 Kotlin 싱글톤 object와 Swift/Objective-C의 interop(상호 운용) API
  39. Kotlin User Groups Seoul Case Study2: KMP 미지원 외부 라이브러리

    연동 [요구사항] • Firebase Crashlytics 연동 • 모니터링용 Logging 인터페이스 구현 • Firebase는 KMP SDK를 지원하지 않으므로 플랫폼별 네이티브 SDK 설치 필요 Firebase 등 KMP를 지원하지 않는 외부 라이브러리를 연동해야 하는 상황 = 해결 완료 TODO: Koin 등 DI 라이브러리 통합
  40. Kotlin User Groups Seoul Case Study2: KMP 미지원 외부 라이브러리

    연동 Firebase Crashlytics SDK 연동 계획 구현하고 보니 IoC (Inversion of Control)
  41. Kotlin User Groups Seoul Case Study2: KMP 미지원 외부 라이브러리

    연동 Firebase Crashlytics SDK 연동 계획 구현하고 보니 IoC (Inversion of Control) 하지만 항상 계획대로 되진 않는다
  42. Kotlin User Groups Seoul Case Study2: KMP 미지원 외부 라이브러리

    연동 저수준 shared 모듈에서의 문제점 ⚠ iOS용으로 별도의 프레임워크(.xcframework)로 컴파일된 shared 모듈과 composeApp 모듈이 서로 다른 메모리 영역에서 작동하는 것으로 보인다. 명시적인 문서는 찾을 수 없었지만, Delegate 객체를 전달하고 해시 코드를 로깅했을 때 구현체의 전달부와 참조부가 각각 다른 값이였다. -> 인스턴스의 메모리 주소가 다르다. // A function in our `shared` module to receive the delegate fun initCrashlyticsDelegate(delegate: FirebaseCrashlyticsDelegate) { crashlyticsDelegate = delegate // When called from iosApp, this prints one hash code NSLog("Delegate hash in 'shared': ${delegate.hashCode()}") } // Later, when accessing this delegate from a ViewModel in `composeApp`... // This would print a DIFFERENT hash code, implying it's not the same instance. NSLog("Delegate hash in 'composeApp': ${crashlyticsDelegate.hashCode()}")
  43. Kotlin User Groups Seoul Case Study2: KMP 미지원 외부 라이브러리

    연동 최종 모듈 구조 - shared 모듈 제거
  44. Kotlin User Groups Seoul Case Study2: KMP 미지원 외부 라이브러리

    연동 결론1: shared 모듈이 꼭 필요했을까?
  45. Kotlin User Groups Seoul Case Study2: KMP 미지원 외부 라이브러리

    연동 결론3: 다른 대안 살펴보기 - Firebase 팀에게 KMP 지원해 달라고 직접 목소리 내기: https://firebase.uservoice.com/forums/948424-general/suggestions/46591717-support-k otlin-multiplatform-kmp-in-the-sdks - 비공식 Firebase Kotlin SDK 사용: https://github.com/GitLiveApp/firebase-kotlin-sdk - KMP를 지원하는 다른 로깅 SDK 선택 (예: Sentry): https://docs.sentry.io/platforms/kotlin/guides/kotlin-multiplatform ⬆ 블로그에서 링크 바로 접근하기
  46. Kotlin User Groups Seoul Case Study3: iOS 모듈에 CocoaPods 설치

    돌아보기: 외부 라이브러리 연동을 위한 더 좋은 방법은 없었을까?
  47. Kotlin User Groups Seoul Case Study3: iOS 모듈에 CocoaPods 설치

    돌아보기: 외부 라이브러리 연동을 위한 더 좋은 방법은 없었을까? 어떻게 appleMain에서 Firebase 의존성을 가져오는거지? 🤔
  48. Kotlin User Groups Seoul Case Study3: iOS 모듈에 CocoaPods 설치

    돌아보기: 외부 라이브러리 연동을 위한 더 좋은 방법은 없었을까? 출처: JetBrains의 Compose Multiplatform 공식 Documents
  49. Kotlin User Groups Seoul // composeApp/build.gradle(.kts) plugins { kotlin("multiplatform") version

    "2.2.21" kotlin("native.cocoapods") version "2.2.21" } kotlin { iosArm64() cocoapods { version = "2.0" summary = "CocoaPods test library" homepage = "https://github.com/JetBrains/kotlin" ios.deploymentTarget = "16.0" pod("FirebaseCrashlytics") { version = libs.versions.firebase.cocoapods.get() extraOpts += listOf("-compiler-option", "-fmodules") } } }
  50. Kotlin User Groups Seoul // composeApp/build.gradle(.kts) plugins { kotlin("multiplatform") version

    "2.2.21" kotlin("native.cocoapods") version "2.2.21" } kotlin { iosArm64() cocoapods { version = "2.0" summary = "CocoaPods test library" homepage = "https://github.com/JetBrains/kotlin" ios.deploymentTarget = "16.0" pod("FirebaseCrashlytics") { version = libs.versions.firebase.cocoapods.get() extraOpts += listOf("-compiler-option", "-fmodules") } } } = 해결 완료
  51. Kotlin User Groups Seoul JetBrains 개발자에게 구조 피드백 받은 썰

    덴마크 코펜하겐에서 열린 KotlinConf 2025에서 직접 JetBrains 부스를 찾아가다
  52. Kotlin User Groups Seoul JetBrains 개발자에게 구조 피드백 받은 썰

    덴마크 코펜하겐에서 열린 KotlinConf 2025에서 직접 JetBrains 부스를 찾아가다 Compose Multiplatform 커뮤니티 내에서 확립된 모범 사례와 심층적인 아키텍처 논의 부족 -> 이를 만든 개발자들, 즉 JetBrains의 개발자들로부터 직접적인 피드백을 수집하는 것이 좋겠다!
  53. Kotlin User Groups Seoul JetBrains 개발자에게 구조 피드백 받은 썰

    덴마크 코펜하겐에서 열린 KotlinConf 2025에서 직접 JetBrains 부스를 찾아가다 Compose Multiplatform 커뮤니티 내에서 확립된 모범 사례와 심층적인 아키텍처 논의 부족 -> 이를 만든 개발자들, 즉 JetBrains의 개발자들로부터 직접적인 피드백을 수집하는 것이 좋겠다! 1. 나: JetBrains 부스 방문해서 써드파티 라이브러리 구현 방법 질문
  54. Kotlin User Groups Seoul JetBrains 개발자에게 구조 피드백 받은 썰

    덴마크 코펜하겐에서 열린 KotlinConf 2025에서 직접 JetBrains 부스를 찾아가다 Compose Multiplatform 커뮤니티 내에서 확립된 모범 사례와 심층적인 아키텍처 논의 부족 -> 이를 만든 개발자들, 즉 JetBrains의 개발자들로부터 직접적인 피드백을 수집하는 것이 좋겠다! 1. 나: JetBrains 부스 방문해서 써드파티 라이브러리 구현 방법 질문 2. JetBrains 개발자1: standard C-interop 접근 방식 설명
  55. Kotlin User Groups Seoul JetBrains 개발자에게 구조 피드백 받은 썰

    덴마크 코펜하겐에서 열린 KotlinConf 2025에서 직접 JetBrains 부스를 찾아가다 Compose Multiplatform 커뮤니티 내에서 확립된 모범 사례와 심층적인 아키텍처 논의 부족 -> 이를 만든 개발자들, 즉 JetBrains의 개발자들로부터 직접적인 피드백을 수집하는 것이 좋겠다! 1. 나: JetBrains 부스 방문해서 써드파티 라이브러리 구현 방법 질문 2. JetBrains 개발자1: standard C-interop 접근 방식 설명 3. JetBrains 개발자2: “Firebase SDK는 해당 접근 방식을 따를 수 없다”
  56. Kotlin User Groups Seoul JetBrains 개발자에게 구조 피드백 받은 썰

    덴마크 코펜하겐에서 열린 KotlinConf 2025에서 직접 JetBrains 부스를 찾아가다 Compose Multiplatform 커뮤니티 내에서 확립된 모범 사례와 심층적인 아키텍처 논의 부족 -> 이를 만든 개발자들, 즉 JetBrains의 개발자들로부터 직접적인 피드백을 수집하는 것이 좋겠다! 1. 나: JetBrains 부스 방문해서 써드파티 라이브러리 구현 방법 질문 2. JetBrains 개발자1: standard C-interop 접근 방식 설명 3. JetBrains 개발자2: “Firebase SDK는 해당 접근 방식을 따를 수 없다” 4. 나: 우리팀 아키텍처 설명, “리뷰해 주세요^^”
  57. Kotlin User Groups Seoul JetBrains 개발자에게 구조 피드백 받은 썰

    덴마크 코펜하겐에서 열린 KotlinConf 2025에서 직접 JetBrains 부스를 찾아가다 Compose Multiplatform 커뮤니티 내에서 확립된 모범 사례와 심층적인 아키텍처 논의 부족 -> 이를 만든 개발자들, 즉 JetBrains의 개발자들로부터 직접적인 피드백을 수집하는 것이 좋겠다! 1. 나: JetBrains 부스 방문해서 써드파티 라이브러리 구현 방법 질문 2. JetBrains 개발자1: standard C-interop 접근 방식 설명 3. JetBrains 개발자2: “Firebase SDK는 해당 접근 방식을 따를 수 없다” 4. 나: 우리팀 아키텍처 설명, “리뷰해 주세요^^” 5. JetBrains 개발자1, 2: “이것이 현재 가능한 최선의 실용적 해결책이다. 지금 당장 이것보다 더 나은 방법이 생각나지 않는다”
  58. Kotlin User Groups Seoul JetBrains 개발자에게 구조 피드백 받은 썰

    덴마크 코펜하겐에서 열린 KotlinConf 2025에서 직접 JetBrains 부스를 찾아가다 Compose Multiplatform 커뮤니티 내에서 확립된 모범 사례와 심층적인 아키텍처 논의 부족 -> 이를 만든 개발자들, 즉 JetBrains의 개발자들로부터 직접적인 피드백을 수집하는 것이 좋겠다! 1. 나: JetBrains 부스 방문해서 써드파티 라이브러리 구현 방법 질문 2. JetBrains 개발자1: standard C-interop 접근 방식 설명 3. JetBrains 개발자2: “Firebase SDK는 해당 접근 방식을 따를 수 없다” 4. 나: 우리팀 아키텍처 설명, “리뷰해 주세요^^” 5. JetBrains 개발자1, 2: “이것이 현재 가능한 최선의 실용적 해결책이다. 지금 당장 이것보다 더 나은 방법이 생각나지 않는다” 6. JetBrains 개발자2: “너 지금 여기 있을게 아니라 구글 부스 가서 KMP 공식 지원 해달라고 요구해라”
  59. Kotlin User Groups Seoul JetBrains 개발자에게 구조 피드백 받은 썰

    덴마크 코펜하겐에서 열린 KotlinConf 2025에서 직접 JetBrains 부스를 찾아가다 Compose Multiplatform 커뮤니티 내에서 확립된 모범 사례와 심층적인 아키텍처 논의 부족 -> 이를 만든 개발자들, 즉 JetBrains의 개발자들로부터 직접적인 피드백을 수집하는 것이 좋겠다! 1. 나: JetBrains 부스 방문해서 써드파티 라이브러리 구현 방법 질문 2. JetBrains 개발자1: standard C-interop 접근 방식 설명 3. JetBrains 개발자2: “Firebase SDK는 해당 접근 방식을 따를 수 없다” 4. 나: 우리팀 아키텍처 설명, “리뷰해 주세요^^” 5. JetBrains 개발자1, 2: “이것이 현재 가능한 최선의 실용적 해결책이다. 지금 당장 이것보다 더 나은 방법이 생각나지 않는다” 6. JetBrains 개발자2: “너 지금 여기 있을게 아니라 구글 부스 가서 KMP 공식 지원 해달라고 요구해라” 7. 다같이: 깔깔깔~ 😂
  60. Kotlin User Groups Seoul JetBrains 개발자에게 구조 피드백 받은 썰

    덴마크 코펜하겐에서 열린 KotlinConf 2025에서 직접 JetBrains 부스를 찾아가다 Compose Multiplatform 커뮤니티 내에서 확립된 모범 사례와 심층적인 아키텍처 논의 부족 -> 이를 만든 개발자들, 즉 JetBrains의 개발자들로부터 직접적인 피드백을 수집하는 것이 좋겠다! 1. 나: JetBrains 부스 방문해서 써드파티 라이브러리 구현 방법 질문 2. JetBrains 개발자1: standard C-interop 접근 방식 설명 3. JetBrains 개발자2: “Firebase SDK는 해당 접근 방식을 따를 수 없다” 4. 나: 우리팀 아키텍처 설명, “리뷰해 주세요^^” 5. JetBrains 개발자1, 2: “이것이 현재 가능한 최선의 실용적 해결책이다. 지금 당장 이것보다 더 나은 방법이 생각나지 않는다” 6. JetBrains 개발자2: “너 지금 여기 있을게 아니라 구글 부스 가서 KMP 공식 지원 해달라고 요구해라” 7. 다같이: 깔깔깔~ 😂 결론: KMP 커뮤니티는 발전중이고, 개발자들이 계속 좋은 방법을 연구해야 한다.
  61. Kotlin User Groups Seoul - UI 로직 100% Compose -

    자잘한 버그는 있었지만, UI 관련 로직은 100% 공통 코드 (약 97%의 코드를 Kotlin으로 작성) - All Kotlin Project (Server + Client) - 팀 TODO: 단일 Kotlin 프로젝트 구축 - 서버와 클라이언트 모두 하나의 언어 -> 충분한 메리트 - iOS Stable로 격상 (Compose 1.8.0) - 사소한 UI 어긋남, 텍스트 크기 차이를 제외하곤 안정적 CMP 8개월 운영 결론
  62. Kotlin User Groups Seoul - UI 로직 100% Compose -

    자잘한 버그는 있었지만, UI 관련 로직은 100% 공통 코드 (약 97%의 코드를 Kotlin으로 작성) - All Kotlin Project (Server + Client) - 팀 TODO: 단일 Kotlin 프로젝트 구축 - 서버와 클라이언트 모두 하나의 언어 -> 충분한 메리트 - iOS Stable로 격상 (Compose 1.8.0) - 사소한 UI 어긋남, 텍스트 크기 차이를 제외하곤 안정적 CMP 8개월 운영 결론 Compose Multiplatform은 Android 개발자가 가장 낮은 러닝 커브로 멀티플랫폼을 개발할 수 있는 스택
  63. Kotlin User Groups Seoul 발표 내용 요약 - Compose Multiplatform:

    희망편 - Compose Multiplatform: 절망편 - Case Study1: 네이티브 상태 옵저버 구현 - Case Study2: KMP 미지원 외부 라이브러리(Firebase) 연동 + Compose Multiplatform 아키텍처 설계 - Case Study3: iOS 모듈에 SPM/CocoaPods 설치 - KotlinConf 2025에서 JetBrains 개발자에게 피드백 받은 썰 +) 팀이 얻은 러닝 +) 구체적인 코드 레벨의 고민