원문 및 출처 : Armin Ronacher's Thoughts and Writings 의 Playground Wisdom: Threads Beat Async/Await
Translated with O1/4o
몇 년 전, async/await 기반 시스템에서 겪었던 어려움과 이들이 역압(back pressure)을 제대로 지원하지 못하는 것 같다는 이야기를 쓴 적이 있습니다. 그로부터 몇 년이 지난 지금, 이 문제가 크게 해소된 것 같지는 않습니다만, 제 사고방식과 이해가 조금 더 발전했을지도 모르겠습니다. 이제 저는 async/await가 사실상 대부분의 언어에서 좋지 않은 추상화라고 확신하고, 대신 스레드가 더 나은 방향이라는 생각을 하게 되었습니다.
이번 글에서는 저보다 먼저 이 분야를 파고들었던 여러 뛰어난 분들의 주장을 다시 다뤄보려 합니다. 새로운 내용은 없고, 단지 이 문제를 처음 접할 독자들에게 닿기를 바랄 뿐입니다. 특히 아래 세 작품(글/발표)은 꼭 살펴보셨으면 합니다.
프로그래머로서 우리는 “이게 당연히 이렇게 작동하겠지”라고 너무 익숙해져 있어서, 자유롭게 사고하는 데 걸림돌이 되는 암묵적 가정을 갖고 있습니다. 이를 보여줄 코드를 하나 보겠습니다.
def move_mouse():
while mouse.x < 200:
mouse.x += 5
sleep(10)
def move_cat():
while cat.x < 200:
cat.x += 10
sleep(10)
move_mouse()
move_cat()
이 코드를 보고 “쥐(mouse)와 고양이(cat)가 동시에 움직이나요, 아니면 순차적으로 움직이나요?”라고 물으면, 대부분의 프로그래머는 “순차적으로 움직인다”고 답할 겁니다. Python과 스레드, 스케줄링 개념 등을 알고 있으니까요. 그런데 Scratch에 익숙한 아이들에게 같은 질문을 하면, 고양이와 쥐가 동시에 움직인다고 생각할 가능성이 높습니다.
이유는, Scratch로 프로그래밍을 배우면 원시적인 액터(actor) 모델을 접하기 때문입니다. 쥐와 고양이는 모두 액터이고, 단지 Scratch에서는 액터를 “스프라이트(sprite)”라고 부를 뿐입니다. 스크린 위의 스프라이트마다 논리를 붙이면, 이들이 모두 동시에 실행됩니다. 심지어 서로 메시지를 주고받을 수도 있습니다.
이 점이 의미심장하다고 생각하는 이유는, Scratch가 매우 단순하고 아이들에게 프로그래밍을 가르치려고 만든 시스템이면서, 동시에 액터 모델을 기본으로 제공한다는 사실 때문입니다. 만약 전통적인 Python이나 C# 같은 언어의 입문서를 보면 스레드 같은 개념은 책 말미에나 언급됩니다. 게다가 그 설명조차 상당히 복잡하고 무섭게 느껴집니다. 심지어 액터 패턴은 훨씬 전문적인 책에서나 대규모 시스템의 복잡성을 예로 들며 다루곤 합니다.
여기서 더 주목해야 할 점은, Scratch가 스레드, 모나드, async/await, 스케줄러에 대해 전혀 언급하지 않는다는 것입니다. 프로그래머 입장에서는 단순히 “메시지 전달” 같은 기본적인 문법이 더해진 명령형(비주얼적인 형태이긴 하지만) 언어로 보입니다. 동시성이 자연스럽게 녹아있어서 아이도 쉽게 프로그래밍할 수 있고, 전혀 두려워할 필요가 없습니다.