완벽가이드 범위 16-7(파일 모듈), 16-8(HTTP 클라이언트와 서버), 16-9(HTTP를 사용하지 않는 네트워크 서버와 클라이언트), 16-10(자식 프로세스), 16-11(워커 스레드)
자바스크립트는 프론트엔드 영역에서 주요하게 다루는 언어지만 노드를 통해 백엔드 영역에서도 충분히 그 힘을 발휘할 수 있습니다. 이번 장은 운영체제, 프로세스와 스레드 등 CS 지식이 요구되는 챕터여서 세부적인 사용 방법 보다는 기본적인 개념을 파악하고 넘어가는데 집중하였습니다.
노드의 분명한 특징: 비동기적인 API를 바탕으로 한 싱글 스레드의 이벤트 기반 병렬 환경
노드: 운영 체제와 연결된 자바스크립트로 자바스크립트 프로그램에서 파일을 읽고 쓰고, 자식 프로세스를 실행하고 네트워크를 통해 통신할 수 있게 한다.
노드의 용도:
관련 링크
fs 모듈은 대부분 유닉스 시스템 함수를 호출하는 저수준 자바스크립트 연결 함수로 이루어져 있습니다. 해당 챕터에서는 파일을 열고, 읽고, 쓰는 방법들과 주의사항, 메타 데이터에 대해 설명하고 있습니다.
대부분 fs.readFile()처럼 비차단적이며 콜백 기반이고 비동기적이지만 다른 변형 역시 존재합니다. 열고, 읽고, 쓰는 함수 등에 대해 다음 세 가지 변형이 존재합니다.
콜백 기반, 동기적, 프라미스
주의 사항
노드에서 서버와 클라이언트가 어떻게 이벤트 기반으로 통신하는지, 한 땀 한 땀 살펴볼 수 있습니다. (그렇기 때문에 실무에서는 노드 내장 모듈로 서버를 구축하는 일은 없습니다. 백엔드 개발에 필요한 유틸리티를 제공하는 express 나 axios 같은걸 씁니다.)
노드의 http, https, http2 모듈은 HTTP 프로토콜의 모든 기능을 갖추었지만 비교적 저수준으로 구현되었습니다.
// http모듈을 추출합니다.
const http = require('http')
const requestOptions = {
method: 'POST',
host: host,
path: endpoint,
headers: {
"Content-Type": "application/json",
// ...
}
}
// port를 지정하고 auth 처리도 해줍니다.
// requestOptions.port, requestOptions.auth
let request = https.request(requestOptions);
request.write(bodyText);
request.end();
request.on("error", (e) => reject(e));
request.on("response", response => {})
// http모듈을 추출합니다.
const http = require('http')
// 웹 서버를 생성합니다.
const server = http.createServer()
//server 객체에 이벤트를 연결합니다.
server.on('request', function (code) {
console.log('Request Event')
})
server.on('connection', function (code) {
console.log('Connection Event')
})
// 웹 서버를 3001 포트로 실행합니다.
server.listen(3001, function(){
console.log('3001번 포트로 서버가 실행되었습니다.')
})
// 응답시 한 땀, 한 땀 상태 코드, 헤더, 바디를 다 써줘야 함
HTTP 통신만 있는게 아니란걸 까먹지 맙시다!
저자는 해당 챕터에서 대표적으로 설명하는 네트워크 소켓에 대해 "네트워크 소켓은 일종의 듀플렉스 스트림이다." (693p) 라고 말하고 있습니다.
해당 챕터에 대한 이해를 위해 간략한 추가 설명을 하자면,
?스트림이란?
데이터,패킷,비트 등의 일련의 연속성을 갖는 흐름을 의미
?16.4장에 나왔던 스트림 종류?
듀플렉스 스트림:
지정된 데이터를 반환하는 리더블 스트림과 데이터가 향하는 대상에 대한 작업을 할 수 있는 라이터블 스트림을 객체 하나에 조합
트랜스폼 스트림:
듀플렉스처럼 읽고 쓸 수 있지만 중요한 차이점은 스트림에 출력된 데이터(보통 다른 형태로 가공된)을 같은 스트림에서 읽을 수 있다는 것 (압축…)
로 동시성 높은 서버를 만들 수도 있지만 다른 프로그램을 실행하는 스크립트도 만들 수 있습니다. child_process 모듈을 통해 자식 프로세스를 실행시킬 수 있습니다.
자식 프로세스 생성 방법
const child_process = require("child_process");
let listing = child_process.execSync( "ls -l web/*.html" , {encoding: "utf8"});
왜 굳이 비동기 멀티 스레드 형식으로 사용하지 않고 복사하여 자식 프로세스를 만드는가에 대한 질문에 대한 답은 다음과 같이 추려 보았습니다.
자바스크립트를 사용하는 노드는 싱글스레드이지만 비동기를 통해 멀티 스레드처럼 동작할 수 있습니다. 심지어 워커 스레드를 사용하면 진짜 멀티스레드처럼 동작하기도 합니다.
그럼에도 자식 프로세스를 실행해야하는 상황이 있다면 child_process를 통해 사용할 수 있습니다.
노드의 동시성 모델은 싱글 스레드이며 이벤트(on) 기반입니다.
하지만 노드 10부터 웹 브라우저의 웹워커를 거의 그대로 반영한 멀티스레드 프로그래밍 모델 역시 지원합니다. 바로worker_threads
워커 스레드에 대한 개념은 Dev scroll 님의 Worker_Threads 모듈 (멀티 쓰레드 구현) 에서 잘 정리되어 있습니다.
그 중 멀티 스레드와 워커스레드의 차이는 메모리 관리면에서 멀티 스레드임에도 공용 메모리 접근 방지와 같은 고민을 해 줄 필요가 없다는 것입니다.
워커 스레드는 하나의 스레드를 더 생성하지만 스레드 별로 하나의 이벤트 루프, js 엔진 인스턴스, node.js 인스턴스가 있어 서로 영역을 침범하지 않고 메세지를 통해 연락을 주고 받습니다. 따라서 같은 메모리 공간을 실수로 동시 접근 하는 경우는 발생하지 않습니다.
실제로 웹 워커를 사용하는 예시를 찾아보기는 쉽지 않았습니다. 조금이라도 더 개념이 와닿을 수 있도록 성능 최적화에서 웹 워커를 사용해 무거운 로직을 분리한 영상을 첨부하겠습니다.
NHN Cloud 2018 프론트엔드 성능 최적화
범용 프로그래밍 언어로서 노드에서 활용할 수 있는 것들을 살펴보았습니다.