- Published on
비동기 논 블로킹 I/O 모델에 대해
- Authors
- Name
- 심성헌 (SeongHeon Sim)
들어가기에 앞서
얼마 전 국비 지원 학원을 수료했다. 우리 조는 LMS을 기반으로 한 퀴즈 플랫폼을 개발했는데, 유형 별 퀴즈에 대한 CRUD를 어떻게 처리해야할까 고민을 많이 했다. 그러면서 자의적, 타의적으로 Ajax 를 이용해서 비동기로 퀴즈 CRUD 를 구현할 수밖에 없었는데 JSON이나 RestController에서 JSON 객체를 RequestBody로 받아야 하는 이유에 대한 이해가 부족한 상태로 억지로 구현했다.
그러한 이유로 이번 학원에서의 마지막 프로젝트 때문에 시작한 Node.js 공부하려고 한다.
Node.js 란 무엇인가?
Node.js 는 해당 문서에서
Chrome V8 Javascript 엔진으로 빌드된 Javascript 런타임
이라고 한다. 여기서 런타임이라는 것은 뭘까?
Runtime 이란?
특정 언어로 만든 프로그램들을 실행할 수 있는 환경을 뜻한다. 즉, Node.js 는 Javascript 프로그램을 컴퓨터에서 실행할 수 있다. Javascript 는 기존에 웹 브라우저에 종속되어 실행되었고, 이를 브라우저 밖으로 꺼내려는 노력은 많았으나 잘 이뤄지지 않았다.
하지만 Google에서 V8 엔진을 개발했고, 이를 이용하여 Chrome 브라우저를 출시하면서 Javascript 를 브라우저 밖에서도 사용할 수 있게 되었다.
Node.js 는 V8 엔진과 더불어 libuv라는 비동기 I/O 라이브러리를 사용하는데, V8 엔진과 libuv 라이브러리는 C와 C++로 구현되어 있기 때문에 내가 코딩한 자바스크립트 코드는 알아서 V8 혹은 libuv에 연결하여 변환해준다. 여기서 libuv 라이브러리는 이벤트 기반, 논 블로킹 I/O 모델을 구현하고 있다.
비동기 논 블로킹 I/O 모델이 뭘까?
여기서 I/O 는 Input / Output이라는 것은 알겠는데, 논 블로킹? 수업 시간에 몇 번 들었던 기억은 있지만 정확히 어떤 것을 의미하는지 떠오르지 않는다. 그래서 검색을 해보기로 한다.
검색을 해보니 강사님께서 설명해주셨던 비유들이 어렴풋 기억나기 시작했다.
집안일을 하는데 빨래 1시간, 설거지 20분, 바닥 청소 10분이 걸린다고 하면 블로킹은 작업의 순서대로 하나 끝나면 다음 일을 시작하는 방식으로 집안일을 다 끝내는데 총 1시간 30분이 걸린다. 반면 논 블로킹은 빨래의 경우 내가 세탁기에 빨랫감만 넣어 놓고 세탁을 돌리면 세탁기가 돌아가는 사이에 나머지 설거지와 바닥 청소를 마무리할 수 있다. 즉, 한 번에 두 가지 일을 할 수 있기 때문에 작업의 효율이 올라간다.
어느 블로거 분의 전문적인 말씀을 가지고 오자면,
- 블로킹 : 애플리케이션 실행 시 운영체제 대기 큐에 들어가면서 요청에 대한 System Call 이 완료된 후에 응답을 보낸다.
- 논 블로킹 : 애플리케이션 실행 시 운영체제 대기 큐에 들어가지 않고, 실행 여부와 관계없이 바로 응답을 보낸다. 바로 응답하기 힘든 경우, 에러를 반환하는데 정상 데이터를 받을 때까지 계속해서 요청을 다시 보낸다.
그렇다면 libuv는 비동기 논 블로킹 I/O 모델을 구현했다고 하는데, 위의 설명이 동기 I/O 모델에 대한 설명이니, 비동기 I/O 모델에 대해서도 알아봐야 할 것 같다.
열심히 검색해본 결과 비동기라는 것은 논 브로킹에서 제기된 문제를 해결하기 위해 I/O 통지 모델이 고안되었고, 이 모델을 동기형 통지 모델 / 비동기형 통지 모델로 나누었는데,
위 블로거 분의 말씀을 가지고 오면,
- 동기 : System Call을 기다리지만, 시스템의 반환을 기다리는 동안 대기 큐에 머무는 것이 필수는 아니다. (블로킹의 경우 필수로 머물러야 한다.)
- 비동기 : System Call 을 기다리지 않는다. 요청에 대해 처리 완료 여부에 관계없이 응답하고, 다음 코드를 실행한다. 이후에 운영체제에서 처리 완료 여부를 알려주고 응답한다.
위 블로거 분의 게시글을 꼼꼼히 읽어봤지만 정확히 이해가 되지 않는데, 나름대로 정리를 해보자면
동기 모델과 비동기 모델에서도 블로킹 / 논블로킹으로 나뉘는데,
- 동기 블로킹
- 앞전의 일이 전부 수행될 때까지 기다린다. 앞의 일이 수행 완료되어야지 그다음의 일이 수행된다.
- 한 작업 당 한 번의 사용자 - 커널 사이의 문맥 교환이 발생한다. (정확히 이해하지 못했다.)
- 동비 논 블로킹
- 동기 블로킹을 개선한 방법이지만 논 블로킹의 경우 정상 데이터가 올 때까지 지속적으로 System Call을 요청한다.
- 이러한 문제로 인해 I/O 지연을 발생시킨다. (레이턴시, Latency)
- 비동기 블로킹
- I/O 는 논 블로킹, 알림을 블로킹으로 하는 방식으로 select()라는 시스템 함수 호출이 애플리케이션을 Block(정지) 시킨다.
- select() 함수는 I/O 설명자에서 반응이 있나 확인하고, 한 개가 아닌 여러 개의 I/O 설명자에 대한 알림 기능을 수행할 수 있는 것이 특징이지만 여전히 비효율적이다.
- 비동기 논 블로킹
- System Call 이 즉시 I/O 개시 여부를 반환한다.
- 애플리케이션은 원래 목적의 일을 수행하고, I/O 는 백그라운드에서 실행한다.
- I/O 응답(Response)이 도착하면 신호(Signal)나 스레드 기반 콜백(callback)으로 I/O 전달(Transaction)을 완료한다.
- 단일 프로세스가 여러 개의 I/O 요청이 예상되는 환경 속에서 컴퓨터 연산이나 I/O 작업을 병렬 수행하는 기능은 처리 속도와 I/O 속도 사이에 틈을 유발한다.
처음 시작을 이 정도로 마무리하며
이렇게 Node.js 교과서의 완전 앞부분, 개념을 톺아보며 가장 이해가 안 되었던 논 블로킹 I/O 모델에 대해 얕게 훑어보았다.
이 부분을 제대로 이해하려면 스레드가 무엇인지, Node.js 는 스레드를 어떻게 제어하는지에 대해 더 공부해야 할 것 같다.
그리고 책을 읽고, 개념들을 정리하면서 느낀 것은 하나를 공부하면 그를 더 깊이 이해하기 위해 다른 하나를 새롭게 배워야 한다는 것이다.
이런 점들이 너무 재밌다.
소설책을 읽는 것 같다. 챕터 1에서는 주인공의 이야기가 챕터 2에서는 주인공을 키워준 사람에 대한 이야기가 나오는 것처럼 말이다.
마치.. 객체 지향 프로그래밍 같다고 해야 할까? (맞는 비유인지는 모르겠다.. 아니라면 저에게 돌을 던져주시고 피드백 부탁드립니다..!)
어쨌든, 첫 공부는 새벽에 시작해서 오래 하지 못했지만 내일도 즐겁게 공부할 수 있기를 다짐하며 아디오스!