안드로이드의 비동기와 Coroutine은 무엇일까?

Enso·2023년 8월 2일

Android

목록 보기
4/5

안드로이드를 작업하다보면 비동기 작업에 대해서 이야기가 많이 나온다.

비동기란 무엇인가?

흔히 우리는 코드를 작성한다면 그 코드를 동기적으로 작동되는 것을 알고 있다.

class main() {
	fun test() {
		var value = 1

    	value = 2 
    	value = 3 
    	value = 4 
    }
}

위 코드를 본다면 우리는 value에 1이 초기화 되고, 2 -> 3 -> 4 순서로 변수가 적용 되는 것을 알고 있다.

위의 코드는 간단하게 설명한 것이고, 만약 [value = N] 코드가 하나의 메서드 즉 비즈니스 로직이라고 한다면 위의 코드가 완료되기 전 까지 아래의 코드는 대기 상태가 된다. 즉 현재 처리하는 작업을 위해 뒤의 코드가 멈춰있다는 것을 뜻한다.

이제 아래 코루틴으로 작성된 코드를 보자.

class main(): AppCompatActivity() {
	fun test() {
		lifecycleScope.launch(Dispatchers.Main) {
			launch {
				repeat(10) {
				Log.d("show value", "change value = $it")
        	        delay(100L)
       	     }
       	 }
       	 launch { Log.d("show value", "value = 2") }
       	 launch { Log.d("show value", "value = 3") }
       	 launch { Log.d("show value", "value = 4") }
        	launch { Log.d("show value", "value = 5") }
    	}
    }
}

비동기를 배제하고 본다면 결과값이 어떻게 나올까?
change value = 0 ... change value = 9
value = 2
value = 3
value = 4
value = 5
이런 형식으로 나와야한다.
(lifecycleScope, launch은 신경쓰지 말자)

하지만 실제 결과는 아래와 같다.
change value = 0
value = 2
value = 3
value = 4
value = 5
change value = 1 ... change value = 9

모든 코드가 비동기로 작업이 진행되어, 순서를 가지고 있지 않다.
(2345가 순서대로 나온건 단지 1줄이기 때문에 처리 속도를 비동기가 따라가지 못한 것일뿐)

잠깐 여기에서 HAERO님의 해답을 보고 비동기에서의 코루틴을 사용하는 예시를 보자.

🤚🏻 가볍게 다시 짚어보기 출처 - HAERO

동기 : 어떤 요청을 보낸 뒤, 그 요청의 결과값을 얻기까지 작업을 멈추는 것
비동기 : 어떤 요청을 보낸 뒤, 그 요청의 결과값이 오기까지 멈추지 않고 또 다른 일을 수행하는 것

만약 우리가 동시성을 확보해서 작업을 멈추지 않고 처리를 하고싶다면 어떻게 할까?
예를 들어서 Remote의 Responce를 받고 [Room에 저장하는 작업][데이터를 가공하는 작업]을 동시에 하고 싶다면 어떻게 해야할까?

RxJava와 Coroutine을 몰랐을 때

	RoomClient.save(data) {
    	~~
    }
    
    DataLogic(data) { convertData -> 
    	~~
    }

Corutine을 이용 했을 때

	CoroutineScope.lauch {
    	RoomClient.save(data)
        DataLogic(data)
    }

차이점이 보이는가? 사실 코드로 보았을 땐 크게 없다.
하지만 Coroutine을 사용하지 않았을 때 RoomClient.save() / DataLogic()의 구현방식은 완전히 다르다. 분명히 메서드 내부에는 백그라운드 혹은 비동기식으로 작동되기 위해서 내부적으로 CallBackHell이 발생 했을꺼라 필자는 그럴꺼라 생각하고 실제로 겪어보기도 했다. 또한 Room의 저장이 끝나고 나서, 결과 값으로 받은 데이터를 이용해서 가공해야된다면? 이런 작업이 여러겹으로 되어있다면 아래와 같은 코드가 될 것이라 생각한다.

	RoomClient.save(data) { roomData -> 
    	DataLogic(roomData) { convertData -> 
        	recyclerView.submit(convertData) {
            	Toast 메세지 출력 
            }
        }
    }

위와 같이 짜여질테고 순서를 바꾸거나 어떤 변경사항이 있을 때 너무나도 보기 힘들 것이다. Kotlin이라 람다식 표현을 헀지만 만약 Java 였다면 더욱 가독성이 떨어졌을꺼다.
위 코드를 코루틴으로 바꾼다면 훨씬 가독성이 좋아진다.

	CoroutineScope.lauch {
    	val roomResult = async { RoomClient.save(data) }.await()
        val convertData = async { DataLogic(roomResult) }.await() 
        
        withContext(Dispatchers.Main) {
        	recyclerView.submit(convertData) {
            	Toast 메세지 출력 
            }
        }
    }

우리가 평소에 짜던 코드 스타일과 비슷하게 결과값을 주고 받을 수 있는 비동기 코드가 완성된다.

코루틴의 장점중 하나인 가독성이 좋아진다를 설명 해보았고, 다음 내용에서는 코루틴이랑 비슷한 역할을 하는 Thread와의 차이점을 설명 해보도록 하겠다.

profile
문제를 먼저 파악하고 대처할 수 있는 개발을 지향합니다.

0개의 댓글