[완벽가이드]노드와 서버 사이드 자바스크립트

쏘쏘임·2022년 9월 4일
post-thumbnail

완벽가이드 범위 16-7(파일 모듈), 16-8(HTTP 클라이언트와 서버), 16-9(HTTP를 사용하지 않는 네트워크 서버와 클라이언트), 16-10(자식 프로세스), 16-11(워커 스레드)

자바스크립트는 프론트엔드 영역에서 주요하게 다루는 언어지만 노드를 통해 백엔드 영역에서도 충분히 그 힘을 발휘할 수 있습니다. 이번 장은 운영체제, 프로세스와 스레드 등 CS 지식이 요구되는 챕터여서 세부적인 사용 방법 보다는 기본적인 개념을 파악하고 넘어가는데 집중하였습니다.

  • 노드의 분명한 특징: 비동기적인 API를 바탕으로 한 싱글 스레드의 이벤트 기반 병렬 환경

  • 노드: 운영 체제와 연결된 자바스크립트로 자바스크립트 프로그램에서 파일을 읽고 쓰고, 자식 프로세스를 실행하고 네트워크를 통해 통신할 수 있게 한다.

  • 노드의 용도:

    • 배시나 기타 유닉스 셸의 복잡한 문법에서 해방
    • 신뢰할 수 없는 코드를 다루는 웹 브라우저에 적용되는 보안 제한에서 벗어나 신뢰할 수 있는 프로그램을 실행하는 범용 프로그래밍 언어
    • 효율적이고 병렬화된 웹 서버 환경
  • 관련 링크

16.7 파일 작업

fs 모듈은 대부분 유닉스 시스템 함수를 호출하는 저수준 자바스크립트 연결 함수로 이루어져 있습니다. 해당 챕터에서는 파일을 열고, 읽고, 쓰는 방법들과 주의사항, 메타 데이터에 대해 설명하고 있습니다.

대부분 fs.readFile()처럼 비차단적이며 콜백 기반이고 비동기적이지만 다른 변형 역시 존재합니다. 열고, 읽고, 쓰는 함수 등에 대해 다음 세 가지 변형이 존재합니다.

콜백 기반, 동기적, 프라미스

  • fs.open()
  • fs.openSync()
  • fs.promises.open()

주의 사항

  • 끝나면 fs.close()를 반드시 호출해야 합니다. (파일을 한 번에 열어둘 수 있는 개수에 제한이 있으므로)
  • 파일 기록 함수는 파일에 대한 처리가 정말 완료되었는지 보장하지 않습니다. 노드가 운영 체제에 파일 기록을 위임한 시점에서 제어권을 반환하거나, 콜백을 호출하거나, 프라미스를 해석하는 시점에서 데이터를 기록했다고 간주합니다. 즉, 중간에 전기가 나가 파일이 제대로 다 쓰여지지 않더라도 파일 기록 함수는 작업이 완료되었다고 간주할 수 있습니다.

16.8 HTTP 클라이언트와 서버

노드에서 서버와 클라이언트가 어떻게 이벤트 기반으로 통신하는지, 한 땀 한 땀 살펴볼 수 있습니다. (그렇기 때문에 실무에서는 노드 내장 모듈로 서버를 구축하는 일은 없습니다. 백엔드 개발에 필요한 유틸리티를 제공하는 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 => {})

서버(응답)

  • 서버 객체 생성, 포트 번호 listen()으로 주시
  • 요청에 대한 이벤트 주시
  • 응답 바디 만들고 요청헤더 출력하고
// 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번 포트로 서버가 실행되었습니다.')
})

// 응답시 한 땀, 한 땀 상태 코드, 헤더, 바디를 다 써줘야 함

16.9 HTTP를 사용하지 않는 네트워크 서버와 클라이언트

HTTP 통신만 있는게 아니란걸 까먹지 맙시다!

네트워크 소켓

저자는 해당 챕터에서 대표적으로 설명하는 네트워크 소켓에 대해 "네트워크 소켓은 일종의 듀플렉스 스트림이다." (693p) 라고 말하고 있습니다.

해당 챕터에 대한 이해를 위해 간략한 추가 설명을 하자면,

?스트림이란?

  • 데이터,패킷,비트 등의 일련의 연속성을 갖는 흐름을 의미

    • 음성,영상,데이터 등의 작은 조각들이 하나의 줄기를 이루며 전송되는 데이터 열(列)
    • 호스트 상호간 또는 동일호스트 내 프로세스 상호간 통신에서 큐에 의한 메세지 전달방식을 이용한 가상 연결 통로를 의미하기도 함
  • 정보통신기술용어해설 / 세줄코딩:티스토리

?16.4장에 나왔던 스트림 종류?

  • 듀플렉스 스트림:
    지정된 데이터를 반환하는 리더블 스트림과 데이터가 향하는 대상에 대한 작업을 할 수 있는 라이터블 스트림을 객체 하나에 조합

  • 트랜스폼 스트림:
    듀플렉스처럼 읽고 쓸 수 있지만 중요한 차이점은 스트림에 출력된 데이터(보통 다른 형태로 가공된)을 같은 스트림에서 읽을 수 있다는 것 (압축…)

서버 생성 과정

  • net 모듈 불러오기, net.createServer()로 서버 객체 생성, listen()으로 주시할 포트 지정
  • 서버 객체는 connection 이벤트(클라이언트가 포트에 연결되면 발생)를 생성
    • 이벤트 리스너에 전달되는 값이 소켓 객체이며 이는 듀플렉스 스트림이므로 클라이언트에서 데이터를 읽고 쓸 수 있다.
    • 소켓 데이터를 표준 출력에 파이프로 연결
  • TCP 기반 서버 외에도 포트 번호가 아니라 파일시스템 경로로 식별되는 ‘유닉스 도메인 소켓(USD)' 의 프로세스 간 통신도 지원
  • UDP 를 지원하는 dgram 모듈
  • net + 보안(SSL 암호화) ⇒ tls 모듈

16.10 자식 프로세스

로 동시성 높은 서버를 만들 수도 있지만 다른 프로그램을 실행하는 스크립트도 만들 수 있습니다. child_process 모듈을 통해 자식 프로세스를 실행시킬 수 있습니다.

자식 프로세스 생성 방법

const child_process = require("child_process");

let listing = child_process.execSync( "ls -l web/*.html" , {encoding: "utf8"});

왜 굳이 비동기 멀티 스레드 형식으로 사용하지 않고 복사하여 자식 프로세스를 만드는가에 대한 질문에 대한 답은 다음과 같이 추려 보았습니다.

  • 프로세스 생성 자체가 매우 복잡한 자료구조. 새로 만드는 것 보다 복사하는게 효율적임
  • 하나의 메인 포인트를 가지기 위해서
  • 다른 작업을 처리하기 위해(공유하는 것 없이)

자바스크립트를 사용하는 노드는 싱글스레드이지만 비동기를 통해 멀티 스레드처럼 동작할 수 있습니다. 심지어 워커 스레드를 사용하면 진짜 멀티스레드처럼 동작하기도 합니다.

그럼에도 자식 프로세스를 실행해야하는 상황이 있다면 child_process를 통해 사용할 수 있습니다.

16.11 워커 스레드

노드의 동시성 모델은 싱글 스레드이며 이벤트(on) 기반입니다.
하지만 노드 10부터 웹 브라우저의 웹워커를 거의 그대로 반영한 멀티스레드 프로그래밍 모델 역시 지원합니다. 바로 worker_threads

워커 스레드에 대한 개념은 Dev scroll 님의 Worker_Threads 모듈 (멀티 쓰레드 구현) 에서 잘 정리되어 있습니다.

그 중 멀티 스레드와 워커스레드의 차이는 메모리 관리면에서 멀티 스레드임에도 공용 메모리 접근 방지와 같은 고민을 해 줄 필요가 없다는 것입니다.

워커 스레드는 하나의 스레드를 더 생성하지만 스레드 별로 하나의 이벤트 루프, js 엔진 인스턴스, node.js 인스턴스가 있어 서로 영역을 침범하지 않고 메세지를 통해 연락을 주고 받습니다. 따라서 같은 메모리 공간을 실수로 동시 접근 하는 경우는 발생하지 않습니다.

실제로 웹 워커를 사용하는 예시를 찾아보기는 쉽지 않았습니다. 조금이라도 더 개념이 와닿을 수 있도록 성능 최적화에서 웹 워커를 사용해 무거운 로직을 분리한 영상을 첨부하겠습니다.
NHN Cloud 2018 프론트엔드 성능 최적화

요약

범용 프로그래밍 언어로서 노드에서 활용할 수 있는 것들을 살펴보았습니다.

  • 노드는 기본적로 비동기적이고 싱글 스레드이며, 콜백과 이벤트를 기반으로 동시성을 구현하는 노드 API를 가지고있습니다.
  • 운영체제와 연결되어 파일을 읽고, 쓰고, 찾을 수 있습니다.
  • HTTP, HTTPS 뿐만 아니라 네트워크 소켓 등 다양한 통신 기반의 서버를 구축할 수 있습니다.
  • 자식 프로세스를 생성할 수 있습니다.
  • 워커 스레드를 통해 멀티 스레드를 적용할 수 있습니다.
profile
무럭무럭 자라는 주니어 프론트엔드 개발자입니다.

0개의 댓글