node.js 혹은 CPS 단상

지난 주에 둘째 태어나서 병원에 있는 동안 SNS와 블로고스피어를 스쳐지나간 글 중 제일 눈에 띄는 부분이 “CPS와 node.js” 관련된 내용이었다.

iPhone은 정리에는 그다지 좋은 기기라고 못하겠다. 내가 생각하던 문맥을 저장하기엔 내 활용 방법의 문제거나 기기가 부족하거나; 여하튼 기억나는 글들을 여기 정리하자면 (twitter는 정리가 불가능하니 제외),

웹 서버 아키텍처와 프로그래밍 모델의 진화 http://ricanet.com/new/view.php?id=blog/110903 (node.js의 I/O 모델을 점검하기에 좋은 글)

Node.js의 소개글 들에 대한 유감 http://himskim.egloos.com/3810574

이에 대한 홍민희 님의 글 http://blog.dahlia.kr/post/18300740247

그리고 다시 (홍민희 님의) 비동기 I/O 프로그래밍 모델 관련 글 http://blog.dahlia.kr/post/18355002657

 

이에 대한 내 생각을 정리하자면:

1. node.js는 hype이라 생각한다. node.js 광신도(…)들이 말하는 장점은 이미 오래된 얘기고 (이벤트 기반의 비동기 I/O 모델, 단일 이벤트 루프와 그로 인한 싱글 스레드의 단순함), 이런 시도를 하는게 node.js만 있는 것도 아니다. 다만 세상에 JavaScript 프로그래머가 많기 때문에 그렇게 느껴지는 것 뿐이지. 서버 혹은 백엔드 프로그래밍에선 너무 오래된 — (게임) 서버 프로그래밍을 주로 하던 이들 사이엔 이게 왜 이슈가 되는지 궁금해하는 사람들이 나온다 — 이슈라서 말이지.

다음 언어들로도 `흔히’ CPS 스타일의 프로그래밍을 할 수 있다. 그리고 이 언어들 모두에서 node.js에서 `하면 안된다’라고 주장하는 한 이벤트에서 긴 계산을 하는 일도 실행 모델에 따라선 해도 된다. 그리고 이 언어들에선 `흔히 하는 일’에 불과하다.

Go: goroutine 자체가 일종의 CPS 스타일 프로그래밍이다. 그리고 일반적인 이벤트 큐 처럼 쓸 수 있는 channel 개념 역시 존재한다. 그리고 이 두 개념을 최대한 활용해서 만든 언어가 Go다. 자세한 내용은 내 과거 포스팅을 참조하자 (https://rein.kr/blog/archives/tags/go) 개인적으론 이런 류의 I/O 비동기 / 멀티플렉싱을 생각하고 짠다면 Go가 가장 깔끔한 프로그램이 나온다고 생각한다. ((이 언어를 만든 회사가 Google이란 생각을 하면 더 그렇다))

C#: C#의 익명 함수 (delegate라고 부르던데) 역시 이런 CPS 스타일로 프로그래밍 할 수 있다. Nexon의 `마비노기 영웅전’과 `마비노기 2’도 이런 개념을 활용해서 서버를 구현했다. 2011년 NDC 세션의 내용을 정리해놓은 것처럼 (https://rein.kr/blog/archives/2696https://rein.kr/blog/archives/2671) 이걸가지고도 JS에서 흔히 하듯이 성공/실패 처리를 cps로 작성한다.

C++: C++11의 lambda 혹은 그 이전의 std::function ((혹은 그 이전 구현체인 std::tr1::function, 혹은 이거랑 별반 다를게 없는 boost::function을 생각하면 더 이전부터)) 을 이용하면 역시 CPS 형태의 코드를 작성할 수 있다. 내가 작성한 게임 서버 라이브러리 역시 이를 이용한 CPS 스타일의 코드가 포함되어 있다.

 

2. node.js가 단일 스레드, 즉 단일 문맥만으로 처리하기 때문에, computation-intensive한 작업은 처리할 수 없다. 그래서 어쩔 수 없이 백그라운드 태스크 큐 등을 써야하고, 이로 인해 생기는 지연 시간(latency) 문제는 극복할 수 없다.
게임 서버에서도 node.js처럼 (혹은 그보다 낫게)

  • 복수의 스레드가 네트워크 이벤트(read/write)를 비동기로 처리하고,
  • 네트워크 이벤트를 서버 메인 스레드(그러니까 논리적으론 단일 스레드 프로그램)로 전달하고,
  • 서버 메인스레드가 내부 로직을 처리해서 다시 네트워크로 보내는

구조를 사용한 서버들이 있다. 다만 이 경우엔 계산 량이 많은 경우 지연 시간이 길어진다. 그것도 게임 상에서 상호 연관이 없어 보이는 부분들 사이에 영향을 주는 형태로 -_-;
게임 서버는 웹과는 좀 다르게 persistent storage에 다 저장하지도 못하고, 각 연결 간에 상호 작용도 많아서 어쩔 수 없이 생기는 문제이긴하지만 맨 첫 글에서 얘기하는 것 처럼 node.js가 `유니크’한 것은 아니다. JavaScript로 서버 프로그래밍을 진행한다는 것은 꽤 참신하지만 말이다.

그리고 서버 메인스레드가 1개가 아니라 복수개 일 수도 있고 — 내가 본 코드에선 이게 주류다 — 걔 중에도 IO 스레드가 full swing을 하돼 non-blocking한 알고리즘을 이용해서 복수의 큐를 사용하고, 블럭킹을 제거한 구현체도 있다.

 

3. 프로그래밍 모델은 CPS는 `쉽게 만들어낼 수 있지만’, 프로그래머에겐 꽤 골치거리라 생각한다. 홍민희 님의 말대로 가장 쉬운 형태는 co-routine일 것이라 생각한다. CPS처럼 이전 문맥에 대해 생각할 필요도 없고, 불필요한 nesting (node.js말하는 거임) 도 필요치 않다. 다만 이 경우에는 언어적인 수준에서 지원하지 않으면 안된다. 불행히도 내가 사용하는 주요 언어인 C/C++의 경우 co-routine을 사용할 수단은 OS fiber수준 정돈데, 이 경우엔 다른 동기화 객체들을 쓸 때, 동기화 단위가 스레드라서 fiber끼리 switching할 때 지옥을 보게 된다. 하지만 node.js 정도의 프로그래밍 모델에선 이게 가장 간편할 것이다. 각 연결 별 문맥끼리 상호작용이 적다면 더더욱 말이다.

Jinuk Kim
Jinuk Kim

SW Engineer / gamer / bookworm / atheist / feminist

Articles: 935

11 Comments

  1. Goroutine을 CPS로 보는 것은 지나치게 넓게 보는 것이 아닐까요? Continuation-passing Style의 정의에 따라 달라질 수 있겠지만 일반적으로 말하는 CPS에는 반하는 디자인이라고 생각합니다. 고루틴에서는 여분의 명시적인 continuation을 위한 인자와 거기에 필요한 함수가 없습니다. Go의 채널과 고루틴 때문에 Go언어에서 CPS를 제거할 수 있었다고 보는게 맞지 않을까요?

    PS: 개인적으로는 고 언어의 워크 큐 기반 채널 시스템은 맘에 안듭니다.

    • 달리나음: 내 좀 넓게 봐서 그렇게 적었습니다. 명시적인 continuation도 필요없고, (어떤 의미에선) co-routine에 더 가까운 점도 있죠. 말씀하신대로 CPS는 아닌것 같다고 해도 딱히 반론할 수는 없네요(…….).

      ps. channel 시스템이 좀 단순(?)하긴 하지만 어느 정도 조합해서 쓸 수 있으니 전 C가 좋은 정도로는 맘에 듭니다(…). 완결된건 아니지만 쓸만한 물건이라고 생각합니다.

      ps2. 감사합니다!

      • summerlight, 달리나음 / CSP의 개념 자체는 굉장히 오래된거네요. Hoare가 제안한 거라니;; 제가 생각한 “CPS를 좀더 일반적으로 할 수 있는 변형”이 아니라 Dalinaum님 말처럼 다른 프로그래밍 방식으로 생각하는게 맞겠네요.

  2. 주요 이슈는 CPS 부분인 것 같지만;; ㅋ node.js를 이용해 서버를 짜고 있는 사람 입장으로 몇자 적어봅니다. 사용하면서 느낀 node.js 의 가장 큰 장점은 JSON 입니다. 자바 스크립트는 그것만으로도 큰 장점이 됩니다. node.js를 통해 구현하는데 가장 큰 단점은 implicit expression을 예측하기 어렵다는겁니다. 예를 들어 실수로 function() – Number 형태의 expression을 한다고 했을 때 function뒤에 괄호를 붙이지 않을 경우 결과는 NaN이 되어 엉뚱한 에러를 뱉고 죽습니다-_- 이로 인해 좀 더 정교하고 복잡한 컨텐츠를 구현해야 할 경우 정적 분석 툴이 필요합니다. 하지만 제대로 된 툴은 아직 존재하지 않는다는 게 함정-_- 저는 그것들을 경험으로 극복하고 있는데…

    • 아마 네가 continuation이 좀 더 나은, 그러니까 코루틴 기반으로 짰던 걸 생각하면 CPS가 그렇게 편하진 않지 않냐? 예를 들어 루아정도만 해도…

      • 솔직히 불편하죠. 그냥 ‘적응’하면 큰 불편함은 아니라는 얘기-_- yield, resume만 있어도 참 좋아질텐데 안 나오겠죠(…)

Leave a Reply