Search
▪️

Understanding the Basics

Web Working Flows

User / Client (Browser) → Enters URL → Domain Lookup → Request to the Server → Process with the Logic that programmed → Response to the User / Client (Browser)
Request and Response Transmission through HTTP & HTTPS Protocols
HTTP: A Protocol for Transferring Data which is understood by Browser and Server
HTTPS: HTTP + Data Encryption (during Transmission)

Node.js Core Modules

http: Launch a server, send requests
https: Launch a SSL server
fs: allow to access file system
path: help with constructing paths on any platform
os: help with operating system, relevant information

Creating Server

createServer라는 Method는 Callback Function을 인자로 받으면서 서버를 생성한다.
이 때 인자로 주는 Callback Function의 인자는 두 가지가 있는데, 하나는 Request의 Incoming Message이고, 나머지는 Response이다.
이런 방법은 Node.js에서 흔히 이용되는 Event Driven Architecture이다.
createServer를 통해서 생성된 서버를 변수에 담아 listen()을 하면 Node.js가 즉시 종료되지 않게 하여 계속 실행되고 있는 상태로 두고 서버로 들어오는 Request를 Listen하게 한다. → Ongoing Looping Process

Node.js의 Life Cycle

Start Script by node app.js Commnad
Parse Code, Register Variables & Functions
Event Loop (Keeps on running as along as there are event listeners registered)
Process Exit by process.exit() Method
** Request가 들어오면 서버에 등록된 Method들만 처리하게 된다.
** Node.js는 Single Thread로 JavaScript를 실행하게 된다. 따라서 전체적인 Node.js의 프로세스는 컴퓨터에 있는 하나의 Thread만 사용하게 된다.
그렇다면 두 개의 Requests가 들어와서 Event를 처리해야 한다면?
이런 이벤트들을 처리하는 것은 정말 빠르고 우려한 것과 달리 처리가 된다.
Node.js가 Single Thread로 처리를 하는 것은 Event Loop가 도는 것이 Single Thread인 것이지, 실제로 작업을 할 떄는 운영체제의 힘을 빌려 Multi Threads로 처리한다.

Understanding Requests

일반적으로 Request에서 자주 이용하는 것들은 Request.url, Request.method, Request.headers이다.

Sending Responses

Response를 보내기 위해서 setHeader()로 헤더를 정의하고, Response를 write()하여 보낼 응답을 작성한 뒤, 작성했던 응답을 end()하여 더 이상의 수정이 불가능하게 만들어 Response에 대한 작성을 끝 마친다.

Routing Requests

URL을 Parsing해야 한다.
Parsing된 URL을 토대로 작업 처리를 해준다.

Redirecting Requests

특정 로직을 처리한 후, 다른 페이지로 Redirect하고 싶다면 StatusCode를 설정 후 setHeader()을 통해 Location 인자를 주면 된다.

Parsing Request Bodies

Streams & Buffers
Stream → Request Body Part 1, Request Body Part 2, Request Body Part 3.... → Fully Parsed
이런 흐름이 가능하려면, on()이라는 Method를 이용하여 특정 Event에 대해서 들을 수 있도록 해야 한다.
또한 들은 Event에 대해서 처리한 것들을 Buffer에 기록할 수 있어야 하므로 전역적으로 사용할 수 있는 Buffer Object를 이용한다.

Blocking & Non-Blocking Code

writeFile() Method와 writeFileSync() Method의 차이는 무엇인가...
writeFileSync()는 Block the Execution of Next Code until File is Created (Synchronous Code) 그렇다면 좋은게 아닌가? 라고 할 수 있지만, 다른 유저들로부터 새로이 들어오는 다른 Request에 대해서 처리가 불가능 할 수도 있으므로 좋지 않다. (이는 Node.js가 Single Thread로 Event Loop를 처리하기 때문에 (Event에 대한 작업 처리는 Multi로 하더라도...) Code를 Block하는 방법은 적절하지 않다. 아무리 빨리 끝나는 작업이라고 한 들 말이다.)
반면에 writeFile()은 Code진행을 Block하지 않고 동시에 진행 해준다(Asynchronous Code). 따라서 오류가 났을 때 처리할 Callback Function이 필요하다. 이와 같은 방법은 Asynchronous한 Node.js의 특성 때문에 주로 이용된다.
즉, Node.js는 Non-Blocking Code인 비동기 코드의 사용이 굉장히 중요하다.

Node.js - Looking Behind the Scenes

일반적으로 Node.js는 Single Thread JavaScript이다. 그렇다면 Multiple한 Requests의 처리는 어떻게 이뤄지는가? 새로운 Thread를 할당하지는 않는다. 그렇다면 ?...
긴 시간이 소요되는, 예를 들면 File System을 이용하여 큰 파일을 건드리게 되는 작업을 수행하게 되면 다음 Request는 대기 해야할 수 도 있다. 이 때, 등장하는 것이 Event Loop와 Worker Pool이다.
Event Loop는 프로그램이 실행될 때, Node.js에 의해 자동으로 시작된다. 이 Event Loop는 Event에 대한 Callback들을 처리한다. Event Loop는 Event들에 대해서 호출되는 모든 Callback Function들을 기억하게 되고 (우리가 서버를 생성하면서 Event Callback Function들을 생성했기 때문에) 이 중에서 빨리 끝나는 작업들에 대해서만 Event Loop에서 처리하게 된다 (일종의 Multiplexing).
그렇다면 긴 시간을 요구하는 작업들은 Event Loop에서 처리하지 않으면 어디서에서 처리하는가? → Worker Pool에서 처리한다. (긴 시간을 요구하는 작업들은 Blocking Code냐 Non-Blocking Code냐로 구분 지을 수 있다.)
Worker Pool 역시 프로그램이 실행될 때, Node.js에 의해서 자동으로 생성되는 것이다. 이 Worker Pool은 무거운, 오랜 시간을 필요로 하는 그런 작업들에 대해서 처리하도록 설계되었다. Worker Pool은 JavaScript 코드와 완전히 별개로 떨어져 있기 때문에, 운영체제에 밀접하게 개입하여 Multi Thread를 활용하여 작업할 수 있다. 따라서 그런 무거운 작업들은 우리가 작성한 코드와 별개로 Worker Pool에서 처리되는 것이다.
Event Loop의 처리 과정을 통해서 조금 더 자세히 살펴보자. Event Loop는 특정 순서에 맞게 Callback들을 처리한다.
1.
매 Iteration마다 먼저 setTimeout()이나 setInterval()과 같은 Timer관련 Callback을 먼저 처리한다.
⇒ Timers
2.
이후에, Pending 되었던 Callback들을 처리한다. 이런 Pending Callback들은 대체적으로 I/O를 수행해야 하는 작업들이 대부분이다. (Input & Output, Disk & Network Operations → Blocking Operations) 이런 작업들이 너무 많으면 그냥 Loop의 반복을 진행하고 다음 Loop에 처리한다.
⇒ Pending Callbacks
3.
Pending Callback들의 처리가 끝나면 Poll Phase를 거치게 되는데, Poll Phase에서는 즉시 끝낼 수 있는 작업들을 수행할 수 있게 오래 걸리는 작업들에 대해서는 Pending Callback들로 연기시키는 등 분류의 작업을 거친다. (중간에 Timer관련 Callback을 찾으면 해당 Callback을 수행하고 다시 돌아온다.)
⇒ Poll
4.
setImmediate()이라는 Callback 함수를 실행 시키면서 당장 처리 가능한 Callback들을 수행하게 된다. (setImmediate() Method는 setTimeout()이나 setInterval() Method와 유사하지만, 전형적으로 Callback이 실행된 직후에 바로 실행이 되어 전형적으로 setTimeout() Method보다 1ms 정도 빠르다.)
⇒ Check
5.
Event들의 모든 Close Callback을 수행한다.
⇒ Close Callbacks
Loop를 다 돌고 refs == 0인지 확인 후에, 남아있는 Event가 따로 없다면 process.exit을 통해 종료한다. (refs는 1씩 증가하며 새로운 Callback이 등록될 때마다 증가하게 된다. Node.js에서는 해당 refs라는 카운터를 계속해서 추적한다. 반대로 Event Listener가 필요 없어지면 지우면서 1씩 감소한다.)
Node Program은 처리할 작업이 더 이상 없다면 종료된다는 소리인데, createServer의 경우는 기본적으로 종료되지 않게 되어 있다.

How to Export Node.js?

맨 아래에 module.exports를 통해서 Global Export를 할 수 있다. 이 방법을 이용할 때, Value Assign이 가능하다. 다른 스크립트에서 해당 Module을 import 할 때에는 module.exports에 등록 되었는지 확인 후에 import 하게 된다. (import 시에는 Core 모듈과 다르므로 Relative Path를 통해 수행한다. 확장자는 자동으로 생성하여 붙으므로 생략 가능하다.)
단일 값에 대해서 export 시에는 module.exports = x; 로 처리한다.
여러 값에 대해서 export할 때에는 object 형식으로 module.exports = {x : x} ; 로 처리한다.
여러 값을 할당하는 다른 방법으론, module.exports.x = x; (exports.x도 가능하다.)로 처리할 수 있다.
** Node Module System에서 중요한 것 중에 하나는, module.exports에 등록된 파일들은 사실 Node.js에 의해서 자동으로 Cache 처리 되며, 외부에서 원본 스크립트에 대해서 변경할 수 없게 되어 있다.