이번 글에서는 저번 Coroutine retry 로직을 구현했던 것을 고차함수로 추출해내는 작업을 해볼 예정이다.
Step1. 초안 작성하기
fun retry(numberOfRetries: Int, block: () -> Unit) {
repeat(numberOfRetries) {
try {
block()
} catch (e: Exception) {
e.printStackTrace()
}
}
block()
}
초안을 작성해보면 위와 같은 코드가 된다.
얼핏 보기엔 문제가 없을 것 같지만, 파라미터 전달될 함수 리턴 타입을 알 수 없기에 Unit 하나로 제한해버리면 안된다.
이럴 때 생각나는 녀석이 하나 있는데 바로 제네릭(Generic)이다.
Step2. 제네릭을 포함하는 함수로 바꾸기
fun <T> retry(numberOfRetries: Int, block: () -> T) {
repeat(numberOfRetries) {
try {
return block()
} catch (e: Exception) {
e.printStackTrace()
}
}
return block()
}
이렇게 코드를 변경해주면 파라미터로 전달될 함수의 자료형을 모두 수용할 수 있게 된다.
Step3. 이제 적용해보기
이전 글을 읽어보지 않은 사람들을 위해 설명하자면 나는 retry logic을 viewModelScope에서 실행하도록 만들어 뒀었다.
그래서 아래와 같은 코드로 진행될 것이다.
viewModelScope.launch {
val numberOfRetries = 2
try {
retry(numberOfRetries) {
loadRecentVersion()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
이렇게 되면 문제없이 끝날 것으로 예상했다.
다만, loadRecentVersion()과 같이 파라미터로 전달될 함수가 suspend fun인 경우에는 어떻게 해야할까?
Step4. Suspend 가능 함수로 변경하기
suspend fun <T> retry(numberOfRetries: Int, block: suspend () -> T) {
repeat(numberOfRetries) {
try {
return block()
} catch (e: Exception) {
e.printStackTrace()
}
}
return block()
}
retry 함수와 파라미터에 전달될 함수에 suspend 키워드를 추가해주었다.
먼저 파라미터만 suspend 키워드를 추가해주면 함수 내부 return 문에서 에러가 나게 된다.
return하게 될 block()은 suspend fun에서만 실행될 수 있다고 말이다.
그리하여 함수 자체에도 suspend 키워드가 추가되었다.
이렇게 되면 이전 코드 예시처럼 viewModelScope에서 문제없이 함수를 실행할 수 있다.
결과적으로 얻은 이득
첫번째로는 당연히 ViewModelScope에서 실행하는 코드 라인이 짧아졌다.
(엄밀히 따지면 현재 해당 함수는 따로 빠져 있으니 코드 전체 길이는 큰 차이가 없다.
그래도 같은 retry logic이 여기저기서 반복될 수록 전체 코드는 짧아진다.)
두번째로는 또 당연하게도 재사용성이 좋아졌다.
고차함수를 적용하며 제네릭을 사용하니
어떠한 작업이라도 같은 함수를 사용하면 똑같이 원하는 수만큼 retry logic을 적용시킬 수 있다.
'Language > Kotlin' 카테고리의 다른 글
CoroutineContext와 CoroutineScope 알아보기 (1) | 2024.09.04 |
---|---|
Coroutines on Main Thread(위험성에 대해 인지하기) (0) | 2024.09.03 |
Implement Coroutines Retry (1) | 2024.06.05 |
Implement Coroutines Timeout (0) | 2024.05.29 |
async CoroutineBuilder Introduction (0) | 2024.05.27 |