1장 노드 시작하기
1. 핵심 개념 이해하기
서버/ 자바스크립트 런타임/ 이벤트 기반/ 논블로킹 I/O/ 싱글 스레드
Node.js는 Chrome V8 Javascript 엔진으로 빌드된 자바스크립트 런타임
서버
네트워크 ~> 클라이언트에 정보나 서비스를 제공하는 컴퓨터 또는 프로그램
클라이언트
- 요청을 보내는 주체
- ex. 브라우저, 데스크톱 프로그램, 모바일 앱, 다른 서버에 요청을 보내는 서버
웹 사이트 방문
- 주소창에 웹사이트 주소 입력(요청)
- 브라우저) 주소에 해당하는 컴퓨터 위치 파악
- 컴퓨터로부터 웹 사이트 페이지 받아와 요청자의 브라우저(클라이언트)에 띄움(응답)
자바스크립트 런타임
런타임: 특정 언어로 만든 프로그램들을 실행할 수 있는 환경
=> 노드: 자바스크립트 프로그램을 컴퓨터에서 실행할 수 있음 = 자바스크립트 프로그램 실행기
(기존) 브라우저: 자바스크립트 런타임 내장
-> 자바스크립트 프로그램을 웹 브라우저 위에서만 실행할 수 있음
+ 브라우저 외 실행 환경에서 자바스크립트 실행 속도 문제
=> (해결) 2008년 구글 V8 엔진 사용해 크롬 출시
노드: V8 + libuv 라이브러리 사용 <- C와 C++로 구현
libuv 라이브러리: 이벤트 기반, 논블로킹 I/O 모델 구현
노드 외의 런타임: 번(https://bun.sh)/ 디노(https://deno.land)
이벤트 기반(event-driven)
- 이벤트가 발생할 때 미리 지정해둔 작업을 수행하는 방식
이벤트: 클릭이나 네트워크 요청 등 - 특정 이벤트가 발생할 때 무엇을 할지 미리 등록해둬야 함
= 이벤트 리스너(event-listener)에 콜백(callback) 함수를 등록- ex. 버튼을 클릭할 때 경고창을 띄우도록 설정하는 것
- 클릭 이벤트 리스너에 경고창을 띄우는 콜백 함수를 등록
-> 클릭 이벤트가 발생할 때마다 콜백 함수 실행돼 경고창 뜸
- 클릭 이벤트 리스너에 경고창을 띄우는 콜백 함수를 등록
- ex. 버튼을 클릭할 때 경고창을 띄우도록 설정하는 것
- 이벤트 발생 -> 이벤트 리스너에 등록해둔 콜백 함수 호출
/ 발생한 이벤트 X or 이벤트 다 처리 -> 다음 이벤트가 발생할 때까지 대기
여러 이벤트가 동시 발생 -> 어떤 순서로 콜백 함수 호출할지 이벤트 루프(event loop)가 결정
- 자바스크립트 코드의 맨 위부터 한 줄씩 실행
- 함수 호출 부분 발견 -> 호출한 함수를 호출 스택(call stack)에 넣음
function first() {
second();
console.log('첫 번째');
}
function third() {
second();
console.log('첫 번째');
}
function third() {
console.log('첫 번째');
}
first();
anonymous 함수: 처음 실행 시의 전역 콘텍스트(global context)
콘텍스트: 함수가 호출되었을 때 생성되는 환경
자바스크립트 코드: 실행 시 기본적으로 전역 콘텍스트 안에서 돌아감
function run() {
console.log('3초 후 실행');
}
console.log('시작');
setTimeout(run, 3000);
console.log('끝');
- 이벤트 루프
- 이벤트 발생 시 호출할 콜백 함수 관리, 호출된 콜백 함수의 실행 순서 결정
- 노드가 종료될 때까지 이벤트 처리를 위한 작업을 반복 => 루프(loop)
- 백그라운드
- `setTimeout` 같은 타이머나 이벤트 리스너들이 대기하는 곳
- 자바스크립트가 아닌 다른 언어로 작성된 프로그램, 여러 작업이 동시에 실행될 수 있
- 테스트 큐
- 이벤트 발생 후, 백그라운드) 이벤트 리스너의 콜백 함수 -> 태스트 큐
- = 콜백 큐: 정해진 순서대로 콜백들이 줄 서 있음
- 콜백들은 보통 완료된 순서대로 줄을 서 있지만, 특정한 경우 순서가 바뀌기도 함
- 호출 스택에 쌓임
- 전역 콘텍스트 anonymous가 호출 스택에 들어감
- `setTimeout`이 호출 스택에 들어감
- 호출 스택에 들어간 순서와 반대로 실행
- `setTimout` 실행 -> 타이머와 함께 run 콜백을 백그라운드에 보내고 `setTimeout`은 호출 스택에서 빠짐
- anoymous가 호출 스택에서 빠짐
- 백그라운드) 3초를 센 후(= 맡겨진 작업 완료) run 함수 -> 태스크 큐
- 이벤트 루프) 호출 스택 비어 있으면 태스트 큐에서 하나씩 함수 가져와 호출 스택에 넣고 실행
- 이벤트 루프) 정해진 규칙 따라 콜백 함수들을 호출 스택으로 부름
- 호출 스택) anonymous까지 실행 완료 -> 호출 스택 비어 있는 상황
- run 콜백을 태스크 큐에서 꺼내 호출 스택으로 올림
- 호출 스택으로 올려진 run 실행, 실행 완료 후 호출 스택에서 제거
- 이벤트 루프) 태스트 큐에 콜백 함수가 들어올 때까지 계속 대기
IF. 호출 스택) 함수 ↑ -> 3초 지나도 run 함수 실행 X
=> 이벤트 루프) 호출 스택이 비어 있을 때만 태스트 큐에 있는 run 함수 호출
-> `setTimeout`의 시간이 정확하지 않을 수 있음
논블로킹 I/O
- 논블로킹(non-blocking)
- 이전 작업이 완료될 때까지 대기하지 않고 다음 작업을 수행하는 것
- 같은 작업을 더 ↓ 시간에 처리 (작업들이 모두 동시에 처리될 수 있는 작업 전제)
- 블로킹(blocking): 이전 작업이 끝나야만 다음 작업을 수행하는 것
// 블로킹 방식
function longRunningTask() {
// 오래 걸리는 작업
console.log('작업 끝');
}
console.log('시작');
longRunningTask();
console.log('다음 작업');
// 시작 -> 작업 끝 -> 다음 작업
// setTimeout
console.log('시작');
setTimeout(longRunningTask, 0);
console.log('다음 작업');
// 시작 -> 다음 작업 -> 작업 끝
`setTimeout(콜백, 0)`
- 코드를 논블로킹으로 만들기 위해 사용하는 기법 중 하나
- `setTimeout`의 콜백 함수인 `longRunnigTask` -> 태스크 큐 => 순서대로 실행 X
= 다음 작업이 먼저 실행된 후, 오래 걸리는 작업 완료 - 밀리초 0으로 설정 -> 바로 실행 X => 기본적인 지연 시간 O (HTML5 브라우저: 4ms/ 노드: 1ms)
=> 오래 걸리는 작업 처리 -> 논블로킹 ~> 실행 순서 변경 -> 간단한 작업들이 대기하는 상황 막을 수 있음
논블로킹 != 동시
동시성: 동시 처리가 가능한 작업 -> 논블로킹 처리해야 얻을 수 있음
싱글 스레드
스레드 하나 -> 자바스크립트 코드 동시에 실행 X
- 프로세스
- 운영체제에서 할당하는 작업의 단위
- 노드나 웹 브라우저 같은 프로그램은 개별적인 프로세스
- 프로세스 간 메모리 등 자원 공유 X
- 스레드
- 프로세스 내에서 실행되는 흐름의 단위
- 프로세스는 스레드 여러 개 생성해 여러 작업 동시에 처리 O
- 부모 프로세스의 자원 공유
- 같은 주소의 메모리에 접근 가능 -> 데이터 공유할 수 있음
노드 실행 -> 프로세스 생성 -> 내부적으로 스레드 여러 개 생성
But, 직접 제어할 수 있는 스레드는 1개 => 노드가 싱글 스레드라고 여겨짐
노드가 싱글 스레드로 동작하지 X 경우
- 스레드 풀(Thread Pool)
- 특정 동작을 수행할 때 스스로 멀티 스레드 사용
- ex. 암호화, 파일 입출력, 압축 등
- 워커 스레드(Worker Threads)
- 노드 12버전에서 안정화된 기능, 노드에서 멀티 스레드 사용할 수 있음
- 직접 다수의 스레드 다룰 수 있고 CPU 작업(연산 ↑ 작업) 시 사용
Q: 여러 개의 일 동시에 처리할 수 있음 -> 멀티 스레드가 싱글 스레드보다 좋다?
A: 꼭 그런 것은 아님
한 음식점에 점원 1명/ 손님 여러 명
- 싱글 스레드(점원), 블로킹 모델
- 점원 한 명이 주문 받아 주방에 넘기고, 주방에서 요리 나오면 손님에게 서빙 -> 다음 손님의 주문 받음
- => 다음 손님은 이전 손님의 요리 나올 때까지 아무것도 하지 못하고 기다림
- (-) 비효율
- 싱글 스레드, 논블로킹 모델
- 점원이 한 손님 주문 받아 주방에 주문 내역 넘긴 뒤 다음 손님 주문 받음
- 요리 끝나기까지 기다리는 대신, 주문이 들어왔다는 것만 주방에 계속 알려줌
- 주방에서 요리 완료되면 완료된 순서대로 손님에게 서빙
- => 요리의 특성(블로킹/논블로킹) -> 완료된 순서 다름 => 주문 들어온 순서 != 서빙하는 순서
- (+) 점원 한 명이지만 혼자 ↑ 일 처리 O
- (-) 점원 한 명이 아파서 쓰러지면 큰 문제
- (-) 요리 하는데 걸리는 시간 ↑(CPU ↑ 쓰는 작업) -> 주문이 ↑ 들어왔을 때 버거울 수 있음
멀티 스레드
- 손님 한 명 올 때마다 점원 한 명씩 붙어 주문 받고 서빙
- (+) 점원 한 명에게 문제 생겨도 다른 점원으로 대체
- (-) 손님 수 ↑ -> 점원 수 ↑/ 손님 수 ↓ -> 노는 점원 있음
=> 점원 새로 고용하거나 기존 직원 해고하는 데 비용 발생
=> 점원 여러 명(멀티 스레드) + 논블로킹 방식으로 주문 받으면 더 좋지만 프로그래밍 어려움
-> 멀티 프로세싱 방식 대신 사용
멀티 스레딩 | 멀티 프로세싱 |
하나의 프로세스 안에서 여러 개의 스레드 사용 | 여러 개의 프로세스 사용 |
CPU 작업 ↑ -> 사용 | I/O 요청 ↑ -> 사용 |
프로그래밍 어려움 | 비교적 쉬움 |
2. 서버로서의 노드
싱글 스레드 + 논블로킹 모델 사용
- (+) 서버) I/O 요청 ↑ 발생 -> I/O 처리를 잘하는 노드를 서버로 사용하면 좋음
- libuv 라이브러리 사용: I/O 작업 논블로킹 방식을 처리 -> 스레드 하나가 ↑ 수의 I/O 혼자 감당 O
- (+) 자바스크립트를 언어로 사용 -> 개발 생산성 ↑
- (+) 웹 서버 내장 -> 입문자 쉽게 접근
웹 서버: 아파치(Apahce), nginx, IIS/ 웹 애플리케이션 서버(WAS): 톰캣(Tomcat)
=> 개수 ↑ 크기 ↓ 데이터 실시간으로 주고받는 데 적합
-> 네트워크, 데이터베이스, 디스크 작업 같은 I/O에 특화
ex. 실시간 채팅 애플리케이션, 주식 차트, JSON 데이터를 제공하는 API 서버
- (-) CPU 부하가 큰 작업에 적합 X
- 작성한 코드 모두 스레드 하나에서 처리 => 코드가 CPU 연산 ↑ 요구 -> 감당하기 어려움
- (-) 에러 제대로 처리 X -> 하나뿐인 스레드가 죽어 서버 전체 멈춤
- (-) Go처럼 비동기에 강점을 보이는 언어 or nginx처럼 정적 파일 제공, 로드 밸런싱에 특화된 웹 서버에 비해 속도 ↓
멀티 스레드 프로그래밍 -> 난이도 ↑ + C, C++, Rust, Go에 비해 속도 ↓
=> CPU ↑ 사용하는 작업(이미지, 비디오 처리, 대규모 데이터 처리)에 적합 X
장점 | 단점 |
멀티 스레드 방식에 비해 ↓ 컴퓨터 자원 사용 | 기본적으로 싱글 스레드 -> CPU 코어를 하나만 사용 |
I/O 작업 ↑ 서버로 적합 | CPU 작업 ↑ 서버로는 부적합 |
멀티 스레드 방식보다 쉬움 | 하나뿐인 스레드가 멈추지 않도록 관리 필요 |
웹 서버 내장 | 서버 규모 ↑ -> 서버 관리 어려움 |
자바스크립트 사용 | 어중간한 성능 |
JSON 형식과 쉽게 호환 |
3. 서버 외의 노드
웹, 모바일, 데스크톱 애플리케이션 개발, 서버
- 웹 프레임워크
- 앵귤러(Angular) - 구글) 프런트엔드 앱
- 리액트(React) - 페이스북
- 뷰(Vue)
- 모바일
- 리액트 네이티브(React Native) - 페이스북, 인스타그램, 핀터레스트, 월마트, 테슬라 등
- 데스크톱 애플리케이션
- 일렉트론(Electron) - Atom, Slack, Discord, Visual Studio Code
출처
'한국경제신문 with toss bank > BE' 카테고리의 다른 글
[Node.js 교과서 - 개정 3판] 4장 http 모듈로 서버 만들기 (0) | 2024.08.19 |
---|