들어가며
예전에 Petpular 프로젝트 때 사용해본 Spring WebClient에서 동기 & 비동기 / 블로킹 & 논블로킹의 개념이 등장했었다.
WebClient는 논블로킹방식으로 작동하고 block() 메소드를 이용해서 블로킹방식으로 바꿔주는데, 이 부분에서 동기 & 비동기 / 블로킹 & 논블로킹 개념에 대한 이해부족으로 고생을 했던 경험이 있었다.
앞으로의 삽질을 방지하기 위해 동기 & 비동기 / 블로킹 & 논블로킹 개념에 대해 스터디 하고자한다.
본격적으로 들어가기 전에 참조한 블로그의 서론을 보자.
동기와 비동기는 프로세스의 수행 순서 보장에 대한 매커니즘이고 블록킹과 논블록킹은 프로세스의 유휴 상태(제어권)에 대한 개념으로 완전한 별개의 개념이라는 것이다.
아무래도 동기와 블록킹, 비동기와 논블록킹의 작동 매커니즘이 더 직관적이기 때문에 많은 사람들이 이 개념들을 같은 것 혹은 비슷한 것으로 오해하고 있는데, 방금 이야기 했듯이 이 두가지 개념은 서로 전혀 다른 곳에 초점을 맞춘 개념들이므로 서로 직접적인 관련은 거의 없다고 봐도 된다. 단지 조합하여 사용되는 것 뿐이다.
그렇다면 각각의 개념을 깊게 이해하려고 하는 것보다 각각의 조합에 집중하자는 결론으로 도달할 수 있겠다.
실제로 구글링을 해보면 각각의 개념은 간단하게 짚고 넘어가되 그 조합에 대해 설명하는 글이 많은걸 볼 수 있다. 초보 개발자 취준생인 나는 글들의 흐름을 따라가며 천천히 개념을 익혀보고자 한다.
동기(Synchronous) & 비동기(Asynchronous)
동기와 비동기는 추상적으로 구분되는 개념이라 정확하게 이거다! 라고 확신을 갖고 말하기는 조금 어렵다. 동기/비동기에 관한 포스팅들을 찾아보면 같은 글을 참고에 참고한 사람을 제외하고는 해석이 묘하게 다른걸 볼 수 있다. 일반적으로 동기는 동시에 일어난다
의 의미로 많이 쓰는 것 같고 상태를 동일하게 만든다
라거나, 특정한 클럭을 정해 통신하는 것
으로 해석하기도 한다. 비동기는 그 반대로 해석되고.
여러가지 해석을 종합해봤을 때, 개인적으로 가장 이해하기 쉬운 해석은 다음과 같았다.
동기(Synchronous)
현재 작업의 응답이 끝나는 시간과 맞춰 동시에 다음 작업을 요청한다
비동기(Asynchronous)
현재 작업의 응답이 끝나지 않은 상태에서 다음 작업을 요청한다
이렇게 보면 이해가 가지만 여기에 다른 쓰레드의 존재가 들어오면 이제 물음표 수백만 개가 머리에 떠오른다. 아마 나중에 이 글을 읽고 똑같이 헷갈려 할 나를 위해 글을 하나 첨부해두겠다.
이 글의 결론은 멀티 쓰레드 != 비동기 이다. 자료들을 많이 읽다보니 포인트가 이상하게 잡힌 듯 하다
동기 비동기의 예시를 한 장으로 설명한 그림을 보고 이 부분은 마무리해보자
블로킹(Blocking) & 논블로킹(Non-Blocking)
블로킹과 논블로킹은 동기/비동기에 비해 명확하다.
위에 들어가며에서 인용한 것처럼 블로킹과 논블로킹은 프로세스의 유휴 상태, '제어권'에 대한 개념에 초점을 맞추고 있다. 호출된 대상이 제어권을 바로 반환하느냐 갖고있느냐에 따라 두가지로 갈린다고 보면 된다.
그림과 설명을 함께 보면 간단하게 이해할 수 있다!
블로킹(Blocking)
호출된 함수가 자신이 할 일을 모두 마칠 때까지 제어권을 계속 가지고서 호출한 함수에게 바로 돌려주지 않는 것
논블로킹(Non-Blocking)
호출된 함수가 자신이 할 일을 채 마치지 않았더라도 바로 제어권을 건네주어(return) 호출한 함수가 다른 일을 진행할 수 있도록 해주는 것
자, 그럼 왜 이렇게 힘들게 두가지 개념을 정리했느냐? 동기 비동기를 공부하다보면 필연적으로 따라오는 블로킹 논블로킹과의 조합때문이다.
위의 개념들이 충분히 헷갈릴만 하고, 혼용해서 사용하기도 하기에 이 차이를 확실히 하고 싶은 수 많은 개발자 분들의 땀과 노력이 있었으니 공부를 안할 수가 없다.
조합해보자
동기/비동기 & 블로킹/논블로킹의 조합에 관한 예시는 정말 많다. 진짜 많다. 우체국 직원, 카페 직원과 손님, 인형눈알 붙이는 회사의 직원과 사장, 핫도그 굽는 로미오와 그걸 먹는 줄리엣, 대표님 개발자 좀 더 뽑아주세요... 등등 재미없는 개념을 최대한 재밌게 설명하려는 예시들이 많다. 그 중에 제일 웃긴 예시 두가지는 링크로 첨부해두겠다.
이 포스팅에서는 조금 덜 다양한 그림 예시로 각 조합을 이해해보기로 한다!
1. 동기 / 블로킹
가장 흔하게 접하는 동기 방식의 예이다.
동기 방식이기 때문에 작업의 흐름도 순차적으로 진행되는 것이 보장되고, 블로킹 방식이기 때문에 어떤 작업이 진행 중일 때는 다른 작업을 동시에 진행할 수 없다.
2. 동기 / 논블로킹
굳이? 싶은 방식이다. 논블로킹 방식이기 때문에 자신의 작업은 계속 할 수 있지만, 제어권을 넘겨준 다른 작업과 응답 타이밍이 맞아야하므로 계속 다른 작업이 끝났는지 조회한다. 이 방식은 동기&블로킹 방식과 마찬가지로 호출한 함수의 작업이 끝나기 전까지는 작업을 끝마칠 수 없다. 그냥 계속 귀찮게 하는거다.
3. 비동기 / 블로킹
이것도 굳이? 싶은 방식이다. 이건 예시를 들어 설명해보자면, A함수가 B함수에게 요청과 함께 제어권을 넘겨준다(블로킹). 하지만 A함수는 비동기 방식이기 때문에 계속 자신의 작업은 수행은 해야하고 제어권은 돌려 받아야하고... 이도저도 못하고 마냥 기다리고 있는 상황이다. 비동기 / 블로킹 방식은 개발자가 의도해서 구현하는 경우보다는 비동기 / 논블로킹 작업을 실행했지만 자기도 모르게 블로킹 작업을 실행했을 때 이런 결과가 나온다고 한다.
+
여기서 개인 프로젝트 때 사용했던 WebClient를 이용한 API호출이 느리거나 타임아웃 에러가 난 이유를 약간 파악했다.
처음에 말했듯이 WebClient는 기본적으로 비동기로 작동하는데, 이걸 사용하는데 동기 / 비동기 & 블락 / 논블락에 대한 이해가 부족해서 아무 생각없이 WebClient 호출을 동기로 만드는 함수를 붙이는 바람에 아주 비효율적인 코드가 되어버린 것이다.
이유를 알았으니 주특기 주간에 고민을 해서 코드 리팩터링을 진행하기로...
같은 문제로 리팩터링 하는 글을 첨부할테니 꼭 보고 수정하자
비효율적인 Blocking 코드를 WebClient를 통해 개선하기
4. 비동기 / 논블로킹
자바에서 사용하고 있는 방식이다. 그냥 보기엔 제일 좋아보이는 방식이다. 자신이 하던 작업을 멈추지도 않고, 다른 함수에게 요청해놓고 그 작업이 끝나면 결과를 받을 수 있기 때문에 효율적인 방식이다. 이 방식은 작업이 대규모이고, 쓰레드가 충분할 때 효과적이다.
마치며
거의 몇개월을 미룬 개념을 이제야 정리했다. 애매하게 알 것 같으면서도 모르겠어서 냅다 던져버린 주제였는데 많이 쓰이진 않아도 알고있으면 꼭 쓸 데가 있을 거라는 확신이 있어서 기를 쓰고 정리했다. 항상 프로젝트에서 왜 자꾸 API호출이 느리게 될까 하는 고민이 많았는데 방법을 몰라서 가슴 한구석에 뭉쳐있던 실타래가 조금씩 풀리는 기분이다..!! 시간이 되면 해결방법을 찾아내야지!
열심히 정리했지만 만약 이 글을 읽는 사람이 있다면 아래의 글을 읽는 걸 더 추천한다. 나보다 더 똑똑하고 경험많은 개발자 분들이 쓴 글이니까...!
읽어보면 좋은 자료들
동기(Synchronous)는 정확히 무엇을 의미하는걸까?
동기 vs 비동기 (feat. blocking vs non-blocking)
'Study > CS' 카테고리의 다른 글
[CS] CI/CD란? (0) | 2022.06.27 |
---|---|
[CS] 브라우저의 동작 원리 (1) | 2022.06.01 |
[CS] API란? (0) | 2022.04.10 |
[CS] Rest API: URL 디자인 가이드 (0) | 2022.02.11 |
[Network] 라우팅(Routing) (0) | 2021.12.19 |