열네 번째 세션 - 어쩔비동기~⏳ 저쩔블락~🚫 안물콜백~📞 안궁코루틴~⏯
seminar지난 11일에는 힘겨웠던 비동기 세미나가 진행되었습니다!
어려운 내용임에도 집중해서 잘 들어주셔서 정말 감사합니다~!
그러면 어떤 내용이었는지 다시 한 번만 돌아볼까요?!
Blocking? Synchronous..?
정말 헷갈리지만 중요한 개념들입니다!
Blocking vs Non-Blocking
제어권을 언제 반납하느냐로 구분합니다. 함수 안의 모든 작업이 끝난 뒤에야 caller에게 제어권을 반납하면 Blocking, 호출이 완료된 뒤에 바로 반납하면 Non-Blocking 입니다.
scanf()나 readLine() 등 콘솔에서 입력을 받는 함수들은 Blocking 하다고 할 수가 있습니다!
Synchronous vs Asynchronous
사실 이에 대한 명확히 통일된 설명이 있지는 않습니다. (설명하는 사람마다 다르더라구요..) 저는 가장 간단한 설명을 제시하겠습니다! 앞의 함수가 완료되어야 뒤의 함수를 실행할 수 있으면 Synchronous합니다. 반대로 Asynchronous 하다면 앞의 함수가 완료되지 않아도 뒤의 함수를 실행할 수 있습니다. 즉, Synchronous는 함수를 호출한 순서대로 완료도 이루어지지만 Asynchronous는 완료되는 순서가 호출한 순서와 다를 수 있습니다!
왜 동기 말고 비동기인가
호출한 순서대로 함수가 완료되면 간단한데 왜 비동기를 쓰라고 하는 걸까요?
왼쪽의 synchronous processing에서는 process B를 호출하고 아무것도 하지 않고 기다렸다가 응답이 오면 B를 끝내고 A를 시작합니다.
반면에 오른쪽의 asynchronous processing을 보면 B를 호출하고 process A 를 진행시키다가 B 응답을 받습니다.
비동기에서 더 많은 작업을 할 여지가 있다는 걸 확인할 수 있습니다. 우리가 만드는 프로그램들은 할 일이 매우 많기 때문에 이것이 효율과 성능의 차이로 이어지게 됩니다!
안드로이드의 사례
할 일이 많은 프로그램의 대표적인 사례인 안드로이드를 보겠습니다.
Main Thread (== UI Thread)
안드로이드의 메인 쓰레드는 UI 쓰레드입니다. 이 쓰레드의 임무는
- 화면 그리기 (1초에 60번 이상!!)
- 사용자와 상호작용 하기 (터치 입력 받기)
- 우리가 작성한 코드 대부분을 실행시키기
가 되겠습니다.
참고!
- Process : 실행 중인 프로그램
- Thread : 프로세스를 실행하는 단위
Main Thread가 블락되면..
우리가 이제 앱이나 웹을 개발하다 보면 오래 걸리는 Blocking 작업들을 해야 됩니다.
- 백엔드의 api를 호출하기
- DB 입출력하기
- 큰 파일 읽기
등이 해당되죠!
이러한 작업들은 응답이 다 올 때 까지, 입출력이 끝날 때까지 제어권을 붙들고 있습니다.
안드로이드의 메인 쓰레드는 16ms마다 UI를 그려야 되는데 제어권이 제 시간에 돌아오지 않는다면 어떻게 될까요?
위에서 본 것 처럼
- 화면을 그리지 못하고!!
- 사용자와 상호작용하지 못하게 됩니다!!
ANR (Android Not Responding)
안드로이드는 이렇게 Ui Thread가 5초 이상 블락되면 Application Not Responding이라는 에러 창을 띄웁니다. 어플을 종료라도 할 수 있게 해야 사용자의 화난 마음을 조금이나마 가라앉힐 수 있기 때문입니다.
답은 비동기다!
이런 문제를 어떻게 해결해야 할까요? 우선 Blocking 하지 않아야 제어권이 메인 쓰레드에 돌아오고 ui를 그릴 수 있을 것입니다.
사실 이것만으로는 충분하지 않습니다! 돌아온 제어권을 가지고 앞의 함수가 끝났는지에 상관없이 다음 함수를 실행해야 하니까 Asynchronous 하게 되어야 합니다.
Non-Blocking과 Async는 함께 가야 하는 것이죠.
이제 Async 프로그래밍이라고 하면 Non-Blocking을 깔고 간다고 보시면 됩니다!
이제 Async의 방법들을 몇가지 알아볼 텐데, 우리가 하려던 작업 (api 호출, db 입출력, 파일 읽기 등) 의 결과물들을 어떻게 처리하고 활용할지에 주목해주시기 바랍니다.
Async #1 - Threading
Threading을 통한 Async 방법은 blocking 작업을 다른 쓰레드에서 실행시켜서 block하지 않도록 하는 것입니다.
그러나 많은 단점이 존재합니다.
- 쓰레드 전환을 위한 Context Switching의 비용
- 쓰레드는 무한하지 않아서 병목 현상을 초래할 수 있다.
- 항상 이용가능하지 않다. (JS는 싱글 쓰레드이다)
- Race Condition을 피하기가 쉽지 않다.
참고!
- Context Switching : 프로세스 또는 쓰레드의 전환 과정. 쓰레드의 경우 registers, stack pointers, program counters을 변경해야 함
- Race Condition : 같은 데이터에 동시에 접근, 변경하는 상황. 데이터 불일치를 초래할 수 있음
Async #2 - Callback
두 번째 Async 방법은 callback입니다.** A 함수의 파라미터로 B 함수를 전달**하여 A가 종료되면 B가 호출되도록 하는 것입니다. A가 api 호출이라면 B는 응답받은 데이터로 ui를 업데이트하는 작업이 되겠죠?!
단점
- 콜백 지옥에 빠질 수 있다. (콜백 안에 콜백 안에 콜백…)
- 에러 처리가 쉽지 않다. (외부로 전달하기 까다롭다)
참고!
- JS의 콜백 동작에는 Event Loop가 사용되는데, A함수가 실행된 뒤 Web API가 B를 Task Queue에 넣고 Event Loop는 당장 실행할 함수가 있는지 확인하여 없을 때 Task Queue 안의 함수를 꺼내 실행합니다.
Async #3 - Promise (Future)
같은 개념이 JS에서는 Promise로, JAVA에서는 Future로 사용되는데요, 미래의 어느 시점에 Promise(Future) 객체를 반환할 것을 약속합니다. 콜백의 단점(실행 순서가 보장되지 않음, 콜백 지옥, 에러 처리)을 보완 하기 때문에 콜백의 대안으로 많이 사용되고 있습니다.
async function fetchUser() // Promise를 반환
// 네트워크 요청을 해서 사용자를 받아온다
return 사용자;
}
// 민선님 방식
// fetchUser를 호출, 완료될 때까지 기다려서 user에 결과를 저장
const user = awiat fetchUser()
console.log(user) // user 출력
웹신 송민선 코어멤버에 따르면 위와 같이 많이 작성한다고 하네요~
비동기 코드를 작성하고 결과를 사용하는것이 점점 간편해지고 있습니다!
Async #4 - Reactive Extensions (Rx)
이름에서 추측할 수 있듯이 데이터의 변화에 반응하여 무언가를 할 수가 있습니다. 이를 위해 observable streams(관찰 가능한 데이터 흐름)의 개념을 사용합니다. 안드로이드에서 주류로 사용되는 방식 중 하나가 RxJava입니다! 오버라이드를 통해 에러 처리를 쉽게 할 수가 있다는 장점이 있습니다.
단점은 synchronous 코드를 쓰던 방식에서 큰 변화가 필요하다는 것을 꼽을 수가 있습니다.
Async #5 - Coroutines
마지막으로는 코루틴이 있습니다! 안드로이드에서 Rx와 쌍벽을 이루고 있는 비동기 처리 방식이죠. 코루틴에는 suspendable 함수라는 것이 있는데요. 실행이 중간에 멈추거나 재개될 수 있는 함수입니다. 이거 왠지.. 쓰레드 같지 않나요?? 그렇지만 쓰레드보다 훨씬 적은 비용으로 전환이 이루어지기 때문에 Light-weight Thread라고 부른답니다!
이렇게 코드 실행을 잘게 쪼갬으로써 한꺼번에 여러 요청을 동시에, 비동기적으로 처리할 수 있게 됩니다. 코루틴이 정말 편리한 점은 blocking, synchronous 코드를 작성하듯이 작성하고 suspend 키워드만 붙이면 된다는 것입니다. 아래 코드를 보실까요? suspend 함수인 fetchDocs는 get을 result에 받아올 때까지 기다렸다가 show로 보여주는 동작을 합니다. 이렇게 써도 되나 싶은 생각이 들지만 그래도 됩니다!
그리고 Main이나 IO 쓰레드를 지정해서 함수를 실행시킬 수가 있습니다.
마무리
앞으로 솔루션 챌린지를 진행하면서 여러 호출을 보내고 또 받아와야 할 텐데요. 제가 지금 간단하게나마 소개한 비동기가 필요한 이유와 비동기 처리 방식들이 작은 도움이 되기를 바라겠습니다!
감사합니다!