프로그래밍 언어의 기초 편이 어느덧 한 자락을 마쳤습니다.
이제 드디어 기초에서 응용으로 가는 장벽, 비동기(asynchronous)에 관한 이야기를 해 보겠습니다.
비동기? 왜 필요하지?
자, 특이한 말이 나왔으니 바로 뜻부터 알고 가야겠죠?
여러 번 인용한 표준국어대사전(https://stdict.korean.go.kr)에서 찾아봅시다.
『정보·통신』 앞에서 행해진 사상(事象)이나 연산이 완료되었다는 신호를 받고 비로소 특정한 사상이나 연산이 시작되는 방식.
네. 말을 좀 바꾸면, "앞에서 뭔가 완료되었다는 '이벤트'를 받아서 '액션'을 시작하는 것"을 의미하는 말이라고 합니다.
중요한 것은, '완료되었다는 이벤트'가 되겠죠.
무슨 말이냐 하면, 내가 어떤 오브젝트에 액션을 하게 시켰는데, 이게 바로 끝나는 게 아니라, 언제 끝날지 모르는 액션인 겁니다.
그러면 우리는 이런 경우 선택을 해야 합니다.
- 저 액션이 다 끝날 때까지 하염없이 기다릴 것인가?
- 일단 다른 걸 하고 있다가 저 액션이 끝나면 그때 뭔가를 할 것인가?
우리가 지금까지 배운 프로그래밍 방법론에 의하면, 후자처럼 할 수 있는 방법은 없습니다.
지시한 액션을 순서대로 하거나, 계속 반복을 하던가 하는 것 밖에 없기 때문에, 하나의 액션이 끝날 때까지 무조건 기다려야 하죠.
해서 오래 걸리는 액션을 중간에 넣게 되면 그 프로그램은 특정한 액션을 하게 되는 순간 그 액션이 끝날 때까지 멈춘 것처럼 보이게 됩니다.
게임을 할 때 화면에 그릴 그림들을 불러오는 동안 화면에 아무것도 그려지지 않고 이전 화면이 보이는 상태를 생각하시면 됩니다.
이러면, 게임이 문제가 있어서 멈춘 건지 실제로 뭔가를 불러오는 중인지 알 수가 없겠죠?
해서, 우리는 게임이나 앱 초반에 대기하는 동안 흔히 말하는 '로딩 화면'이라는 것을 보게 됩니다.
뭔가를 빙글빙글 돌리면서 '잠시만 기다려주세요' 하는 그런 화면 말이죠.
이때 실제로 그 화면에선 아무것도 하지 않는 것이 아니라, 뒤에서 잠시 후 쓸 리소스들을 읽어 들이는 액션을 하는 중인 것입니다.
그리고 '다 끝났다는 이벤트를 받으면', '로딩 화면을 멈추는 액션을 하고', '다음 화면을 그리는 액션을 하는' 것이죠.
여러분이 흔히 접하는 비동기 플로우의 한 예가 되겠습니다.
왜 어려운가?
이전에 잠시 비동기가 나왔을 때 말씀드린 적이 있었지만,
제가 프로그래밍을 본격적으로 배웠던 90년대 초만 해도 컴퓨터 환경은 지금과 많이 달랐습니다.
인터넷과 같은 네트워크 환경은 굉장히 초보적이어서 고려할 필요가 거의 없었습니다.
CPU의 코어도 아직 1개의 코어만을 사용하던 시기였고,
CPU를 보조하던 co-processor라는 녀석이 막 나오기 시작하던 시기였습니다.
따라서, 프로그램이 고려해야 할 컴퓨터는 보통의 경우 프로그램이 작동하는 로컬 컴퓨터 1대만 생각하면 되었습니다.
그리고, 프로그램 작동 중에 끼어드는 작업이라고 할 것은 키보드 입력과 그 당시 마악 쓰기 시작했던(!) 마우스 정도가 다였습니다.
개인 레벨에서 공부하는 프로그래밍이라고 하는 것에서, 비동기 프로그래밍이라고 하는 것은 저 너머 이야기였습니다.
위에서 말씀드린 키보드와 마우스의 처리도, 프로그램 시작과 함께 무한 반복을 돌면서 입력을 기다리고,
루프가 돌았을 때 키 값이 들어왔냐 안 들어왔냐를 확인한 뒤, 정해진 키 값이 없으면 다시 반복을 돌리는 것이었기 때문에,
여러분이 생각하는 비동기 프로그래밍과는 차이가 있습니다.
그리고, 지금까지 말씀드린 프로그래밍의 개념들은, 대부분 이런 수준의 컴퓨터 프로그램을 만들던 시점에 만들어진 것들입니다.
이제는 사실 필수가 되어버린, 네트워크와 모바일 환경
여러분들이 사실 가장 많이 프로그래밍을 배우시기 위해 공부하는 '시스템'은 웹입니다.
정확히는 자바스크립트를 이용한 프레임워크를 배우시죠.
'프로그래밍 언어'는 파이썬이고요.
그리고, 요즘 데스크톱용 앱보다 모바일 앱들을 많이 사용하기 때문에, 아예 안드로이드나 iOS 앱을 만드는 쪽도 많이 하십니다.
모바일 환경은 당연히 통신을 기반으로 데이터를 주고받기 때문에, 데스크톱 환경보다도 더욱 네트워크 환경에 지배를 받습니다.
일부 앱들은 로컬 환경에서도 작동할 수 있게 만듭니다만 (계산기 ㅋ)
네트워크 상태에 굉장히 민감하게 작동하고, 그 상태 변동에 따른 대비를 확실하게 하지 않으면 안 됩니다.
여기에서 정말 필요한 기술이 바로 앞에서 말씀드린 비동기 프로그래밍 되겠습니다.
'화면을 그릴 데이터를 주세요' 하고 요청하면 네트워크를 통해서 그 데이터가 올 테니까, 기다려야 하는데요.
그게 언제 올지 여러분들은 알 수 있겠습니까?
"안테나가 하나 뜰 때?"
프로그래밍 언어를 배우는 방법은 이제 바뀌어야 하지 않을까?
프로그래밍 언어를 공부하기 위해 몇몇 책을 열어보시면, 아마도 대부분 순서가 비슷한 것을 발견하실 수 있을 겁니다.
변수 개념과 타입을 먼저 설명하고, 연산과 대입, 그리고 반복과 조건문, 그다음 함수...
거의 비슷할 겁니다. 다 이유가 있습니다.
컴퓨터에 어떤 값을 입력하기 위해서는 메모리의 개념을 이해해야 하기 때문에 제일 먼저 변수가 나오는 것이고,
용량을 얼마나 차지하는지 알아야 하기 때문에 타입이,
그리고 어떻게 저장하는지 알아야 하기 때문에 연산과 대입이 (사실 할당이라고 말씀드렸었습니다),
어떻게 저 메모리를 변경하며 프로그램을 구성하는지 알아야 하기 때문에 반복과 조건문이,
길게 늘어나는 프로그램을 최적화하기 위해 함수 개념이 나오는 것입니다.
이 정도 개념을 알고 나면, 로컬에서 그럴듯한 무언가를 만들 준비가 다 되었기 때문에 비로소 예제를 만들기 시작합니다.
하지만, 이걸로 정말 괜찮은 걸까요?
이전과 달리 프로그래머가 메모리를 직접 제어해야 하는 언어는 그리 많지 않습니다. C와 C++ 정도?
어지간하면 가비지 컬렉터(Garbage Collector)라는 메모리 정리 도구가 대신 관리해 주는 언어들이 주류를 차지합니다.
메모리 1~2바이트 잘못 할당하면 OS가 터지던(...) 예전과 달리,
공부할 때 쓰는 예제 프로그램에서 100킬로바이트 할당하는 정도로는 컴퓨터는 '응 그래'하는 시대가 되어버렸습니다(...)
옛날과 달리, 이제는 어지간한 프로그래밍 언어도, 그리고 그를 기반으로 한 프레임워크/라이브러리 들도 모두 비동기를 지원합니다.
그리고 이제는 네트워크뿐만 아니라, 로컬에서도 일자 흐름으로 프로그램을 구성하기는 어려운 흐름으로 가고 있습니다.
그렇다면 언어나 프레임워크를 공부할 때, 흔히 말하는 쌍팔년도 순서로(...) 공부하는 것이 과연 맞나? 하는 생각이 듭니다.
제가 엔트리에서 오브젝트와 이벤트/액션부터 순서를 잡은 게 바로 그런 흐름 때문입니다. 교육용 프로그램도 그런 생각이잖아요?
물론 저런 걸 몰라도 되느냐? 하는 건 아닙니다. 저런 게 나중에 가면 다 필요합니다.
어느 수준에 다가가면 1킬로바이트 차이로 최적이 갈라지는 순간은 오죠.
하지만, 프로그램 기초 걸음마를 하는(...) 시점부터 저런 순서로 배워야 하는가? 에는 저는 좀 회의적입니다.
우리가 5살 아이한테 방정식의 기본 원리를 가르치지는 않잖아요?
조금 생각해 볼 문제가 아닌가 싶습니다.