iOS 개발자가 Android 개발할 때 조심해야 할 것들
iOS 중심 사고방식에서 Android로 전환할 때, 플랫폼 철학·수명주기·툴체인·백그라운드 제약의 차이를 먼저 이해하는 것이 중요!
아래 체크리스트와 섹션별 가이드를 참고하기
TL;DR 체크리스트
- 언어/툴: Swift → Kotlin, Xcode → Android Studio/Gradle. Nullable/Coroutine 규칙 숙지
- 수명주기: ViewController →
Activity/Fragment
+ViewModel
(Lifecycle-aware) - UI: Auto Layout/SwiftUI → XML 레이아웃/Jetpack Compose,
dp/sp
단위 구분 - 네비게이션: iOS 제스처/내장 뒤로가기 ↔ 하드웨어 Back 및 NavGraph
- 비동기: GCD/async-await → Coroutines/Flow, 스코프/캔슬 전파 주의
- 백그라운드: iOS Background fetch ↔ Doze/앱 대기/포그라운드 서비스/WorkManager
- 권한: 최초/런타임 권한 흐름, 배터리 최적화 예외(백그라운드 작업 시) 고려
- 리소스/해상도:
@1x/2x/3x
↔ mdpi~xxxhdpi 및 리소스 퀄리파이어 - 스토리지: 샌드박스/키체인 ↔ Scoped Storage, SAF, FileProvider
- 빌드/배포: SPM/CocoaPods ↔ Gradle, Build Variant/Flavor, 서명키(keystore), R8/ProGuard
- 호환성/단편화: iOS 단일 생태계 ↔ 다양한 OEM/OS 버전/디바이스 대응
- 푸시: APNs ↔ FCM, 채널/우선순위/알림 정책
- 테스트/디버깅: Xcode Instruments ↔ Android Profiler/Systrace/ADB
1) 플랫폼 마인드셋 차이
- 단편화(Fragmentation): 화면 크기·비율·제조사 커스텀(OS·전원 정책) 차이가 큼 → 레이아웃, 퍼포먼스, 배터리에 보수적으로 접근
- 사용자 기대: 하드웨어 Back 버튼 중심 탐색, 시스템 설정(알림/배터리) 간섭이 잦음
- 하위 호환:
minSdk
에 따라 사용 API 제한 → Jetpack/지원 라이브러리 적극 활용
2) 언어·툴체인 전환 포인트
- Kotlin: 널 안정성(
?
,!!
지양), 확장함수/스코프 함수(apply/let/run/also
) 패턴 익히기 - Gradle: 모듈화, Build Types( debug/release ) + Product Flavors 조합 = Build Variant
- 의존성 주입: Hilt/Dagger, Koin 중 하나 표준화
- 로그/디버그: Logcat/ADB 명령, StrictMode, Timber 등
Tip: 멀티모듈 구조 + 버전 카탈로그(
libs.versions.toml
)로 의존성 관리 안정화
3) 수명주기 & 아키텍처
- Activity/Fragment는 UI 컨테이너, 비즈니스/상태는
ViewModel
로 분리 - LifecycleOwner에 맞춘 옵저빙: LiveData/StateFlow/Flow +
repeatOnLifecycle
- 구성 변경(Configuration changes): 화면 회전/다크모드/언어 변경 시 상태 유지 설계 필수
- 아키텍처: MVVM + Repository + UseCase, 단방향 데이터 흐름(MVI)도 고려
주의: iOS처럼 컨트롤러 객체에 과도한 책임을 몰아주면 메모리 릭/수명주기 버그 발생
4) UI 레이어(Compose vs XML)
- 단위: pt ↔ dp(밀도 무관 픽셀), 글꼴 sp
- 리소스:
values-kr
,drawable-xxxhdpi
,layout-sw600dp
등 퀄리파이어로 분기 - 리스트: UITableView/UICollectionView ↔ RecyclerView(DiffUtil/ListAdapter)
- 네비게이션: Storyboard/Coordinator ↔ Navigation Component(NavGraph, SafeArgs)
- Jetpack Compose: 선언형 UI(=SwiftUI 유사)지만 수명주기/상태 호이스팅,
remember
/LaunchedEffect
등 Compose 고유 규칙을 별도 학습
5) 비동기·동시성
- iOS: GCD/Operation/async-await ↔ Android: Coroutines/Flows
- 스코프 관리:
viewModelScope
,lifecycleScope
사용. 커스텀 스코프는 캔슬 전파 설계 필요 - Cold/Hot 스트림: Flow(Cold) ↔ SharedFlow/StateFlow(Hot) 이해
- 스레딩:
Dispatchers.IO/Main/Default
적절 분배,withContext(…)
로 컨텍스트 스위칭
함정: 잘못된 스코프/잡 관리 → View 종료 후 작업 지속, 크래시/리크 유발
6) 백그라운드·배터리 정책
- Doze/앱 대기(App Standby): 네트워크/알람 제한 강함
- 정확 알람은 제한적(
S+
에서 권한), 일반 작업은 WorkManager에 위임 - 지속 작업은 Foreground Service + 알림 필수
- OEM마다 배터리 최적화가 공격적 → 중요한 백그라운드는 예외 허용 안내 UX 준비
7) 권한·보안
- 런타임 권한(카메라/위치/알림 등) 요청 타이밍과 Rationale UX
- 알림 채널(중요도/사운드) 설계, Do Not Disturb 정책 고려
- 서명키 관리: keystore 분리 보관, Play App Signing 활용
- 네트워크 보안: Network Security Config(평문 HTTP 차단/허용 도메인 지정)
8) 스토리지·파일 접근
- Scoped Storage: 앱별 사유 영역 원칙, 갤러리/다운로드 접근은 SAF 또는 MediaStore
- 파일 공유:
FileProvider
로 URI 제공(직접 경로 노출 금지) - 암호화: EncryptedSharedPreferences/SQLCipher 등 고려
9) 네트워킹·이미지
- Retrofit + OkHttp 표준 조합, 응답 파서는 kotlinx.serialization 또는 Moshi
- 이미지 로딩: Coil/Glide. 생명주기/캐시 정책 맞추기
- 오프라인: Room(DB) + Repository + 캐시 전략(ETag/Cache-Control)
10) 빌드·서명·배포
- 버전:
versionCode
(정수 증가),versionName
(표시용 문자열) - 빌드 최적화: R8/ProGuard 규칙, 리소스 축소,
minifyEnabled
,shrinkResources
- 멀티덱스: 메서드 65K 초과 시
multiDexEnabled true
- ABI/Bundle: Play 앱 번들(
.aab
), ABI/언어/스크린 밀도 스플릿
11) 테스트·품질
- 단위 테스트: JUnit/Mockito/Kotest
- UI 테스트: Espresso, Compose UI Test
- 프로파일링: CPU/메모리/네트워크 Android Profiler, Systrace, LeakCanary
12) 푸시·딥링크·인텐트
- FCM: 토픽/토큰 관리, 백그라운드 알림 채널·우선순위
- 딥링크: App Links(HTTP), 인텐트 필터/스킴 설계
- 인텐트/브로드캐스트: 컴포넌트 간 통신 규칙, 암시적 인텐트 보안 주의
13) 국제화·접근성
- 문자열/플러럴, RTL, 폰트 확장(sp), 시스템 글자크기 변경 대응
- TalkBack 접근성 속성(contentDescription 등) 채우기
14) 디바이스·OEM 핏
- Back 버튼 UX(더블백 종료, Navigation 재사용), 제스처 내비게이션 충돌 방지
- 카메라/마이크/블루투스 등 제조사별 권한/동작 차이 사전 QA
- 시스템 테마/다크모드, 컷아웃/노치, 다양한 리프레시 레이트(90/120Hz)
15) iOS ↔ Android 개념 매핑 치트시트
iOS | Android |
---|---|
ViewController | Activity / Fragment |
AppDelegate/SceneDelegate | Application / Process & Task |
Swift Concurrency | Kotlin Coroutines |
Combine/RxSwift | Flow/LiveData/RxJava |
Auto Layout / SwiftUI | XML ConstraintLayout / Jetpack Compose |
Table/CollectionView | RecyclerView(ListAdapter/DiffUtil) |
Background Fetch | WorkManager / Foreground Service |
Keychain | EncryptedSharedPreferences / Keystore |
APNs | FCM |
Info.plist | AndroidManifest.xml |
Bundle ID | applicationId |
SPM/CocoaPods | Gradle/MavenCentral |
Instruments | Android Profiler/Systrace |
16) 추천 기본 스택 (안전한 초반 선택지)
- 언어/아키텍처: Kotlin + MVVM + ViewModel/Repository + Coroutines/Flow
- UI: Compose(신규) 또는 XML + ConstraintLayout(안정)
- DI: Hilt
- 네트워크: Retrofit + OkHttp + kotlinx.serialization
- 로컬: Room
- 백그라운드: WorkManager
- 이미지: Coil
- 로깅/크래시: Timber, Crashlytics
17) 흔한 함정 & 회피 전략
- 수명주기 미스매치: Activity/Fragment 참조를 ViewModel/코루틴 안에 오래 보관 → 메모리 릭. → 약한 참조/Context 구분, 스코프 준수
- Back 처리 중복: NavController와 수동
onBackPressedDispatcher
충돌 → 단일 경로로 처리 - 권한/배터리 예외 누락: 위치/백그라운드 작업 실패 → 예외 허용 안내 UX 및 폴백
- 리소스 퀄리파이어 오남용: 불필요한 분기 남발 → 공통 레이아웃 + 최소 분기 원칙
- 스레드 안전성 간과: UI 스레드/IO 스레드 전환 누락 →
withContext
습관화 - R8 규칙 누락: 리플렉션/직렬화 라이브러리 난독화로 런타임 크래시 → keep 규칙 점검
- Compose 상태 관리 혼동:
remember
범위/상태 호이스팅 누락 → 미묘한 재구성 버그
18) 온보딩 로드맵(2주 샘플)
- D1–D2: Kotlin 기본(Null-safety, Coroutines) + Gradle 기초
- D3–D4: Activity/Fragment + ViewModel + Navigation
- D5–D6: Retrofit/Room + Flow(오프라인 우선)
- D7–D8: 권한/알림/WorkManager + FCM
- D9–D10: Compose 기본 컴포저블 + 상태/재구성
- D11–D12: 프로파일링/LeakCanary/StrictMode
- D13–D14: R8/서명/Play Console(내부 테스트 트랙)