Android(98)
-
Android Compose 앱에서 발생한 오류: java.lang.NoSuchMethodError
1. 오류 내용앱 실행 시 다음과 같은 치명적인 런타임 오류가 발생:java.lang.NoSuchMethodError: No static method setContent$default(...)V in class Landroidx/activity/compose/ComponentActivityKt;1-1. 발생 환경Kotlin 2.0.21Compose Compiler 1.5.13org.jetbrains.kotlin.plugin.compose 플러그인 사용단일 모듈 구조 (멀티모듈 아님)2. 원인build.gradle.kts (app 모듈)에서 아래 설정 누락:buildFeatures { compose = true}해당 설정이 없으면 Compose 관련 코드가 컴파일조차 되지 않음.그로 인해 런타임 시 ..
2025.05.12 -
왜 hiltViewModel()은 밖에서 쓰면 터질까? hiltViewModel의 안전한 사용법
사건의 발달회사 프로젝트는 상당히 오래된 프로젝트이다.Java, Kotlin이 마구 뒤섞여져 있고 당연히 XML은 기본이다.여기에 새로운 기능을 추가하는데, 이번엔 Compose를 도입하려고 했다.기초적인 View 작업을 마치고 이제 Activity와 연결하는데 에러가 발생하였다.확인해보니 hiltViewModel을 밖에서 사용했다고 하는데 이게 무슨 소리일까?IllegalStateException: No ViewModelStoreOwner was provided via LocalViewModelStoreOwner 문제 상황 재현나는 다음과 같은 NavHost를 사용하였다.@Composablefun MyNavGraph(navController: NavHostController) { NavHost(n..
2025.05.08 -
Parcelize vs Serializable 차이점 한눈에 보기
Serializable : Java의 기본 직렬화 방식class User(val name: String, val age: Int) : Serializable장점:구현이 매우 쉽다. implements Serializable만 하면 끝.Java 표준이라 Android 외의 환경에서도 사용 가능.단점:리플렉션 기반 → 느리다.성능이 중요한 Android 환경에선 이게 큰 단점.어떤 필드가 직렬화되고 있는지 명시적으로 제어 불가.Parcelable (+ @Parcelize) : Android 전용 직렬화 방식@Parcelizedata class User(val name: String, val age: Int) : Parcelable장점:Android에 최적화된 고속 직렬화.메모리 복사 수준으로 빨라서 성능이 ..
2025.04.02 -
Single Activity Architecture(SAA) 전환 시 Navigation 구조 설계 방법
안드로이드 앱을 Single Activity Architecture(SAA)로 전환할 때, 가장 많이 고민하게 되는 포인트는 바로 Navigation 구조다.기존에는 Activity 단위로 화면을 나눴다면, 이제는 하나의 Activity와 하나의 NavHost 안에서 모든 화면 전환이 이루어져야 한다.핵심 원칙NavHost는 하나만!AppNavGraph에서 전체 라우팅 담당Activity마다 NavHost를 여러 개 두는 구조는 Compose + SAA에서 지양하위 기능은 NavGraphBuilder 확장 함수로 분리ChatNavGraph, OpenChatNavGraph처럼 묶고 싶다면 NavGraphBuilder 확장 함수로 정의실제 NavHost는 AppNavGraph에만 존재해야 함실전 TIPCha..
2025.03.28 -
[Android] 특정 국가 차단하기 (SIM 정보 이용)
오늘은 Android 앱에서 특정 국가를 차단하는 기능이 필요해졌다.VPN이나 GPS보다 우회가 어렵고, 간단하게 구현할 수 있는 방법은 없을까?그 첫 번째 시도로 SIM 카드의 국가 코드를 활용해보기로 했다. 사용한 API: getSimCountryIso()안드로이드의 TelephonyManager는 SIM에 저장된 ISO 국가 코드를 반환하는 메서드를 제공한다.이 코드를 통해 사용자의 유심이 어느 국가에서 발급된 것인지 확인할 수 있다. 코드 (Kotlin)fun getSimCountryIso(context: Context): String { val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as Telephon..
2025.03.27 -
[Android] 왜 Log.d() 대신 Timber를 써야 할까?
자꾸 Android Studio에서 Log.d를 Timber로 변환하라는 메세지가 뜬다.대체 Timber가 뭐길래.1. Log.d()의 단점1️⃣ 태그를 직접 써야 함Log.d("MyActivity", "Something happened")MyActivity 와 같은 TAG를 계속 명시해줘야 한다.2️⃣ 릴리즈 빌드에서도 로그가 찍힐 수 있음디버깅용 로그가 사용자 환경에서도 노출될 위험이 있음3️⃣ 로그 필터링, 커스터마이징 어려움로그 포맷, 조건부 출력 등 확장성이 떨어짐2. Timber의 장점1️⃣ 자동 태그 처리Timber.d("Hello")로그에 자동으로 클래스 이름 + 라인 번호가 붙는다.D/MainActivity: Hello (MainActivity.kt:42)2️⃣ 릴리즈 빌드에서 로그 자동..
2025.03.26