본문 바로가기

분류 전체보기140

Android Build process 훑어보기 (1) - 빌드, 배포, 컴파일,런타임 빌드와 배포 서버에 기능을 추가 하려면 개발자가 로컬 PC에서 개발을 하고 테스트까지 진행한 뒤에 문제가 없을 경우 사용자가 사용할 수 있도록 수정된 코드를 실 서버에 반영해야 한다. 실서버에 반영을 하는 것을 "배포"라고 하고 배포를 하기 위한 과정을 "빌드"라고 한다. 컴파일(Compile) 컴파일은 프로그램을 생성하기 위해 개발자가 작성한 소스코드을 "컴파일" 이라는 과정을 통해 기계가 알아들을 수 있는 과정을 말한다. 그러나 Java 와 kotlin은 조금 다른 과정을 거친다. (우리는 안드로이드 개발자이니 여기를 좀더 중점적으로 보자. 자바(확장자 : '.java')와 코틀린(확장자 : '.kt')으로 구성된 파일이 있을때 각각의 javac 와 kotlinc에 의해서 자바 바이트 코드로 컴파일 .. 2024. 3. 5.
Serializable의 위험성을 알아보자!!! 이전 버전의 호환성을 고려한 앱 업데이트 및 이슈 대응 경험 Background 자동 로그인을 위해 Serializable로 로컬에 저장하던 데이터 클래스가 존재함. 이전 버전에서 새 버전으로 업데이트하는 과정에서 역직렬화 에러가 발생 하여 자동로그인이 되지 않음. Problem Situation 새 버전 개발 도중 데이터 클래스를 수정 했고 이전 버전에서 저장된 데이터와 새 버전에서 수정된 데이터의 형태 차이로 역 직렬화 과정에서 Exception 발생 역 직렬화 오류 Exception이 OS 버전에 따라 다르게 발생. OS 6 버전 이하 : IllegalArgumentException OS 6 버전 초과 : InvalidClassException Troubleshooting & Result OS 버전.. 2024. 2. 15.
2장 코루틴 라이브러리 - 공유 상태로 인한 문제 사작하기 전에 아래에 있는 UserDownloader 클래스를 살펴보자. 이 클래스는 아이디로 사용자를 받아오거나, 이전에 전송받은 모든 사용자를 얻을 수 있다. 이렇게 구현하면 무슨 문제가 있을까? class UserDownloader( private val api: NetworkService, ) { private val users = mutableListOf() fun downloaded(): List = users.toList() suspend fun fetchUser(id: Int) { val newUser = api.fetchUser(id) users.add(newUser) } } 앞의 구현 방식에는 동시사용에 대한 대비가 되어 있지 않다. fetchUser 호출은 user를 변경한다. 이 경.. 2024. 2. 10.
2장 코루틴 라이브러리 - 코루틴 스코프 만들기 이번에는 스코프에 대해 배운것들을 요약해보고 일반적으로 사용하는 방법에 대해서 알아보자. CoroutineScope 팩토리 함수 CoroutineScope는 CoroutineContext를 유일한 프로퍼티로 가지고 있는 인터페이스이다. interface CoroutineScope { val coroutineContext: CoroutineContext } CoroutineScope 인터페이스를 구현한 클래스를 만들고 내부에서 코루틴 빌더를 직접 호출할 수 있다. 안드로이드에서 스코프 만들기 이번 예시는 BaseViewModel에 scope를 정의해보자. abstract class BaseViewModel : ViewModel() { protected val scope = CoroutineScope(TOD.. 2024. 2. 10.
2장 코루틴 라이브러리 - 디스패쳐 코루틴 라이브러리가 제공하는 중요한 기능은 코루틴이 실행되어야 할 스레드를 결정할 수 있다는 것이다. 이 결정은 디스패쳐를 이용하여 사용할 수 있다. 디스패쳐의 사전적 의미 : 사람이나 차량, 특히 긴급 차량을 필요한 곳에 보내는 것을 담당하는 사람 코루틴 코틀린에서 코루틴이 어떤 스레드에서 실행될지 정하는 것은 coroutineContext이다. 기본 디스패쳐 디스패쳐를 설정하지 않으면 기본적으로 설정되는 디스패쳐는 CPU 집약적인 연산을 수행하도록 설계된 Dispatcher.Default 이다. 이 디스패쳐는 코드가 실행되는 컴퓨터의 CPU 개수와 동일한 수(최수 두개 이상)의 스레드 풀을 가지고 있다. (필자의 컴퓨터는 12개의 CPU를 가지고 있으므로 풀의 스레드 수는 12개이다.) suspend.. 2024. 2. 9.
2장 코루틴 라이브러리 - 코루틴 스코프 함수 먼저 여러개의 엔드포인트에서 데이터를 동시에 얻어야하는 중단 함수를 떠올려보자. 일단 가장 바람직한 방법을 보기 전에 차선책부터 보자. 아래는 동기적으로 진행되므로 2초가 걸린다. suspend fun getUserProfile(): UserProfileData { val user = getUserData() //(1초후) val notifications = getNotifications() //(1초후) return UserProfileData(user, notifications) } 두개의 중단 함수를 동시에 실행하기 위해 async로 매칭해야 한다. 하지만 async는 스코프를 필요로 하고 차선으로 GlobalScope를 사용하는 것은 올바르지 않다. suspend fun getUserProfile.. 2024. 2. 4.
2장 코루틴 라이브러리 - 예외 처리 코루틴은 부모-자식 관계이므로 자식 코루틴이 예외가 발생하면되면 부모 코루틴도 예외가 발생한다. 이러한 해결책을 try-catch 가 아닌 다른 방법을 사용해야 한다. 코루틴의 상호작용으로 인한 예외이므로 try-catch는 아무 기능도 할 수 없다. SupervisorJob 코루틴 종료를 멈추는 방법은 SupervisorJob를 사용하는 것이다. SupervisorJob를 사용하면 자식에서 발생한 모든 예외를 무시할 수 있다. fun main() { runBlocking { val scope = CoroutineScope(SupervisorJob()) scope.launch { delay(1000) throw Exception("error") } scope.launch() { delay(2000) pr.. 2024. 2. 3.
2장 코루틴 라이브러리 - Job과 자식 코루틴 기다리기 CoroutineContext에는 Job, CoroutineDispatcher, CoroutineName 이 있다고 이전 장에서 설명했다. 이 중 Job에 대해 더 자세히 알아보자. Job Job은 Coroutine Lifecycle를 관리하며, 부모-자식 관계의 특성을 가지고 있다. 더보기 부모 - 자식 설명 자식은 부모로부터 Context를 상속받는다. 부모는 모든 자식이 작업을 마칠 때 까지 기다린다. 부모 코루틴이 취소되면 자식 코루틴도 취소된다. 자식 코루틴에서 에러가 발생하면 부모 코루틴 또한 에러로 소멸된다. Job Lifecycle 위의 이미지에 대해 알 수 있는 항목은 default 상태인 Active 상태는 Job을 실행한다. 주어진 작업이 끝나면 Compleing 상태에서 자식이 모두.. 2024. 2. 3.
2장 코루틴 라이브러리 - Cancel 기본적인 취소 Job 인터페이스는 취소하게 해주는 cancel 함수를 가지고 있다. 이 함수를 호출하면 다음과 같은 효과가 있다. 호출한 코루틴은 첫 번째 중단점(아래 예제 delay)에서 Job 을 끝낸다. Job이 자식을 가지고 있다면, 그들 또한 취소가 되지만 부모는 취소되지 않는다. Job이 취소되면 취소된 Job은 코루틴의 부모로 사용될 수 없다. Cancelling → Cancelled 상태로 변경된다. fun main() = runBlocking { coroutineScope { val job = launch { repeat(1_000) { delay(100L) println("print : $it") } } delay(1000) job.cancel() job.join() println("S.. 2024. 2. 3.
2장 코루틴 라이브러리 - CoroutineContext CoroutineContext란 CoroutineContext는 코루틴이 실행되는 동안 필요한 모든 정보를 담고 있는 요소이다. 이는 다양한 요소들을 결합하여 코루틴의 동작 방식을 정의한다. CoroutineContext는 일종의 맵과 같이 작동하며, 각 요소는 키로 구분된다. CoroutineContext는 다음과 같은 요소를 포함할 수 있다. Job: 코루틴의 생명주기를 제어하며, 코루틴의 취소와 완료를 추적한다. CoroutineDispatcher: 코루틴이 어느 스레드에서 실행될지 결정한다. 예를 들어, Dispatchers.Main은 메인 스레드에서, Dispatchers.IO는 입출력 작업을 위한 백그라운드 스레드에서 코루틴을 실행하도록 지시한다. CoroutineName: 디버깅을 위해 코루.. 2024. 2. 3.
2장 코루틴 라이브러리 - Coroutine Buidler 개요 모든 중단 함수에서 일반 함수를 호출하는 것은 문제가 없지만, 일반 함수에서 중단 함수를 호출할 수는 없다. 모든 중단 함수는 다른 중단함수에서 호출이 되어야 한다. 중단 함수는 어떻게 시작할 수 있을까? 바로 Coroutine Builder를 통해 시작할 수 있다. launch builder launch builder 가 동작하는 방식은 개념적으로 thread 함수를 사용해서 thread 를 시작하는 것 과 비슷하다. 독립적으로 코루틴이 실행이 되며 아래 예제와 같이 사용할 수 있다. fun main() { GlobalScope.launch { delay(1000L) println("World!") } GlobalScope.launch { delay(1000L) println("World!") }.. 2024. 2. 3.
하나의 프로젝트에서 여러 다른 버전의 앱을 만들어 보자!(1) - BuildType, productFlavors 이 글을 앞서 먼저 예시의 상황을 만들어 보자. 먼저 당신은 서비스를 개발하기 전 다양한 형태의 서비스를 제공하기 위해 설계를 해야한다. 여기서 다양한 형태란 무엇일까? 개발용 debug, 구글 플레이스토어에 올라갈 실제 유저가 사용될 release 두 가지의 빌드 타입 무료버젼과 유료버젼 등 다양한 버젼 딥링크를 활용한 서비스 지원 라이브러리화하여 외부 업체 지원 하나의 프로젝트에서 여러 다른 버전의 앱을 만들어 보자! 시리즈는 위의 다양한 형태에 대해서 작성해보려 한다. 2편 : 하나의 프로젝트에서 여러 다른 버전의 앱을 만들어 보자!(2) - DeepLink 3편 : 하나의 프로젝트에서 여러 다른 버젼의 앱을 만들어 보자!(3) - Link 이번 글은 빌드 타입과 버젼을 지정할 수 있는 방법에 대해.. 2024. 1. 28.
코틀린 코루틴 이해하기 + 코루틴 실전 대입하기 해당 글은 사내에서 세미나를 한 것을 정리한 것이다. 핵심목표 코루틴의 작동 방식에 대해 간략하게 이해하기 기존에 사용하던 코루틴의 적절한 사용 여부 확인하기 잘못 사용된 경우, 그 수정 방법에 대해 고민하기 코루틴을 배워야 하는 이유? 예시를 들어보자. 아래 예시는 AP로부터 뉴스를 가지고 와서 정렬한 다음, 스크린에 띄우는 로직을 구현하는 경우다. fun onCreate() { val news = getNewsFromApi() val sortedNews = news .sortedByDescending{ it.publishedAt } view.showNews(sortedNews) } 위의 예제는 안타깝게도 위와 같은 방식으로 개발할 수 없다. 이유는 안드로이드에서는 하나의 앱에서 뷰를 다루는 스레드가 .. 2024. 1. 23.
OS14 알람 및 리와인더 권한 대응을 해보자 지난 주 월요일 저녁 퇴근 후, 급속도로 올라오는 Firebase CrashReport를 보며 무슨 일이 일어났겠구나. 라고 생각은 했지만 OS 업데이트로 인한 알람 권한 이슈가 올라 올 것이라고는 생각도 못했다... 이번 글은 지난 주에 있었던 일을 공유해보려 한다. 사건의 시작 FireBase CrashReport에 위와 같은 에러가 올라왔고 읽어보면 SecurityException이라고 되어 있는걸 보니 권한 이슈라고는 짐작했다. 그런데 이해가 안되는건 배포는 한참전에 되었는데 이제와서 갑자기??????? 일단 문제를 해결하기 위해 구글링을 해보니 아래와 같이 공식문서를 발견할 수 있었다. 공식문서 위의 내용을 간략하게 요약하면 OS14 부터 알람 권한(SCHEDULE_EXACT_ALARM)은 권한.. 2024. 1. 22.
1장 시퀀스란? 시퀀스란? 코틀린에서 시퀀스(Sequence)는 연속된 요소들로 구성된 데이터 컬렉션이다. 시퀀스는 리스트(List)나 배열(Array)과 같은 컬렉션과 유사한 동작을 수행하지만, 요소에 대한 게으른 연산(lazy evaluation)을 제공하여 효율적인 처리가 가능하다. 시퀀스를 사용하면 필요한 시점에서만 연산이 수행되기 때문에, 대량의 데이터를 다루는 경우에 성능과 메모리 사용량을 효과적으로 개선할 수 있다. 시퀀스 생성하기 시퀀스를 생성하기 위해서는 asSequence() 함수를 사용하여 일반적인 컬렉션을 시퀀스로 변환할 수 있다. 예를 들어, 다음과 같이 리스트를 시퀀스로 변환할 수 있다. val numbers = listOf(1, 2, 3, 4, 5) val sequence = numbers.a.. 2024. 1. 13.