Hiun Kim essays   articles   notes

비동기의 핵심

April 05, 2017 · SEOUL, KOREA

왜 비동기 프로그래밍은 복잡한 것일까? 구현이 아니라 사용측면에서, 어떤 문제가 존재하길래 그것의 복잡성을 abstract(감추기)시킬수 없었을까.

Computer Science의 많은 문제는 optimisation(최적화)라는 측면에서 사람과 기계사이의 간극을 좁혀나가는 방향으로 해결된다. 인터프리터의 성능을 높힌 [JIT])가 그런 예가 되겠다. JIT의 internal implementation(내부 구현)은 복잡하겠지만, 그 복잡성은 JIT의 개발자가 떠앉고, 해당 언어의 사용자는 별 문제 없이 더욱 최적화된 프로그래밍을 할수 있다. 이러한 감추기 방법을 layered abstraction이라 한다. programming languages 맟 computer systems 그리고 computer network역시 layered abstraction과 subroutine과 같은 동일 레벨에서 이루어지는 modular abstraction에 의해서 동작한다. 비단 컴퓨터뿐만 아니라, 항공기라던가 자동차라던가 심지어 회로의 컨덴서와 같은 부품조차도 interface를 가지고, 다른 부품에 연결되서있으며, 자신이 가진 복잡성을 internalise(내제화)시킨다.

본론으로 돌아와서, 내가 의문을 가진것은 왜 이러한 복잡성의 내제화가 비동기 프로그래밍에는 적용이 어렵냐는 것이다. 비동기 프로그래밍은 원래 syntactic elegance를 위한 anonymous function을 staged computation을 위한, 기계를 위한 operation으로 만들었다. Promise와 같은 language constructor가 나와도 이러란 표현의 복잡성은 glorified될뿐, 없어지지는 않는다. 왜 우리는 procedural asynchronous programming(절차적 비동기 프로그래밍)을 할수 없는것일까?

그 원인은 비동기가 효율성을 뽑아내는 원인에 있다. 그 원인은 ‘시간’이다. 비동기는 시간을 overwrapping하여서 효율을 만들어낸다. 아래 코드는 트윗 2개를 가져와 변수에 담고, 이를 출력하는 예제이다. 이 코드를 절차적 비동기 프로그램으로 만드려면 어떻게 해야 할까? 만약 컴파일러가 프로그램일 실행하지 않고,static analysis(정적분석)기능을 가지고 있다면 3번째 console.log문이 1, 2번째 비동기 작업들과 dependency(의존성)을 가지고 있다는 dependency graph를 만들어 낼수 있을것이다. 그렇다면, concurrent(동시)하게, 1, 2번째 비동기 작업을 처리하고 3번째 라인의 console.log를 실행하는 일종의 strategy를 만들수 있을것이다.

var tweet1 = getTweet(1);
var tweet2 = getTweet(2);
console.log(tweet1, tweet2);

그러나, 아쉽게도 이러한 정책은 아래 예제에서 correctness(정확성)을 상실하고 만다.

var profile = twAuth(1);
var tweet = getTweet(1);
console.log(tweet1, tweet2);

아래 예제는 stateful io, 즉 상태를 가진 비동기 작업을 진행하는 예제이다. 1번째 라인에서, 인증을 하고 2번째 라인에서 트윗을 가져온다. 그리고 그것을 출력한다. 프로그램은 이 코드를 자동으로 비동기화 시키겠지만, 그럴경우 에러가 난다. 왜냐하면, 서버에서는 auth를 getTweet의 precondition으로 생각하기 때문이다, 즉 비동기의 대상이 상태를 가진다면(stateful하다면) 절차적 비동기화는 프로그램의 correctness를 깨트리게 된다.

해결방법은 함수 호출에 상태를 가지게하는, 즉 staged computation인데, 이를 구현하는게 역설적으로 Promise이다. chaining을 통해, 순서를 가진 작업들을 하나의 이벤트 흐름으로써 계속 처리하는것이다. 아직 일반화가기는 어렵지만 OAuth라던가 이러한 stateful io와 커뮤니케이션 하는것은 대부분 OOP기반으로 라이브러리에서 상태관리를 해준다. constrain을 만들자면, stateful object를 만들어서 해당 object내의 dependency graph를 만든후, 메서드의 실행을 선택적으로 비동기화 시키면 될것이다.

혹은 비동기로 작업이 되어야할것만 마킹을 따로 해주는 방법도 있다. 근데 그렇게되면 현재의 promise와 형태가 비슷하지게된다.

생각을 더 해보긴 해야겠지만, 문제의 모티베이션은 여전하다. 프로그램이 how가아닌 what을 표현하도록 하고싶다는것이다, 현재의 기계위주의 staged computation은 개선점이 많다고 생각한다.


Node.js Internals »