๋ ธ๋๋ฅผ ์ฌ์ฉํ๋ ํ์ฌ์ ๊ฐ๊ฒ๋์ด์, ๋ ธ๋๋ฅผ ๋ฐฐ์ฐ๊ณ ์๋๋ฐ...
์๋ฐ์คํฌ๋ฆฝํธ๋ ๋ ธ๋๋ ๋ค ์ฒ์ ๋ฐฐ์ฐ๊ณ ์์ด์ ์ด๋ ค์ด ๊ฒ์ด ๋ง์ ๊ฑฐ ๊ฐ๋ค. ํนํ ์ดํดํ๊ธฐ ์ด๋ ค์ด๊ฒ ์ด๋ฒคํธ ๋ฃจํ(Event Loop)์๊ณ , ํต์ฌ์ ์ธ ๋ถ๋ถ์ธ ๊ฒ ๊ฐ๋ค๊ณ ๋๊ผ๋ค. ๋ ธ๋์ ๊ด๋ จ๋ ๋ชจ๋ ์ด์ผ๊ธฐ๊ฐ ์ด๋ฒคํธ ๋ฃจํ๋ก ๋ชจ์ด๋๋ฏํ ๋๋์ ๋ฐ์๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋์ ์ด๊ฑธ ์ดํดํ๋ค๋ฉด ๋ ธ๋์ ๊ตฌ์ฑ ์์์ ๋์ ๋ฐฉ์์ ๋ฌผ๋ก ์ด๊ณ ์ฐ์ ์์๊ฐ ๋๋์ด์ ธ์๋ ๋น๋๊ธฐ์์ ๋ค์ ์ ๊ตฌ๋ถํ๊ณ ์ค์ ํ ์ ์๋ค๊ณ ์๊ฐํ๋ค. ๊ทธ๋ฐ ์ด์ ๋ก ์ด๋ฒ ๊ธ์์ ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ์ค์ฌ์ผ๋ก ๋ ธ๋์ ๊ด๋ จ๋ ์ฌ๋ฌ๊ฐ์ง ์ง์๋ค๋ ํจ๊ป ์ ๋ฆฌํ๋ฉด์ ๊ณต๋ถํด๋ณผ ์๊ฐ์ด๋ค.
๊ณต๋ถ๋ฅผ ํ๋ฉด์ ๊ธ๋ ํจ๊ป ์์ฑํ๊ณ ์๊ธฐ ๋๋ฌธ์, ๋ถ์ ํํ ์ ๋ณด๊ฐ ์์ ์๋ ์๋ค! ๊ทธ๋ฌํ ๋ถ๋ถ๋ค์ ์ค๋ฌด๋ฅผ ๊ฒฝํํ๋ฉฐ ์ฐจ์ฐจ ์์ ํด๋๊ฐ๋ณด๊ฒ ๋ค.
Node.js
๊ธฐ๋ณธ์ ์ผ๋ก ๋ ธ๋๋ ๋จ์ผ ์ค๋ ๋๋ก ๋์ํ๋ค. ๋ฑ ํ๋์ ๋ฉ์ธ ์ค๋ ๋๊ฐ ๋ชจ๋ ๊ฒ์ ์ฒ๋ฆฌํ๋ ๊ตฌ์กฐ์ด๋ค. ์ด๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ธ์ด ์์ฒด๊ฐ ๋จ์ผ ์ค๋ ๋๋ก ๋์ํ๋ ์ธ์ด์ด๊ธฐ ๋๋ฌธ์ธ๋ฐ, ํ์ง๋ง ์น ๋ธ๋ผ์ฐ์ ๋ฅผ ์๊ฐํด๋ณด๋ฉด ์์์ ๋ณด๋ฉด์ ์ฌ๋ฌ ์น ํ์ด์ง๋ฅผ ์ด์ด ํ์ํ ์ ์๋ค. ๋ ธ๋๋ก ์ด๋ฃจ์ด์ง ์๋ฒ๋ ๋์์ ์ฌ๋ฌ ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์๋ค.
์ด๋ ๋ด๋ถ์ ์ผ๋ก ์ด๋ฒคํธ ๋ฃจํ๋ผ๋ ๊ฒ์ด ๋์ํ๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค. ํ์ง๋ง, ์คํ๋ง์์์ฒ๋ผ ์์ฒญ๋ถํฐ ์๋ต๊น์ง ๋ชจ๋ ๊ณผ์ ์ ๋ฉํฐ ์ค๋ ๋๋ก ๋์ํ๋ ๊ฒ์ด ์๋๋ผ ๋ ธ๋๋ I/O ์์ ์ ๋ํด์๋ง ๋ฉํฐ ์ค๋ ๋๋ก ๋์ํ๋ค๋ ์ฐจ์ด๊ฐ ์๋ค. ์ด๋ ๋ ธ๋์ ์ฒ ํ์ ๊ทผ๊ฐ์ด "I/O ์์ ์ด ๋ฌผ๋ฆฌ์ ์ผ๋ก ๋๋ฆฌ๊ณ , ์์ ์๋ฃ๊น์ง ํ๋ก๊ทธ๋จ์ด ๋๊ธฐํ๋๊ฑด ๋ฐ๋ณด๊ฐ์ ์ผ์ด๋ค." ๋ผ๋ ๋ง์ ์๊ธฐ ๋๋ฌธ์ด๋ค. - Ryan Dahl: Node JS
๊ทธ๋์ ๋๋ ธ์ด ๋จ์๋ก ๋๋๋ CPU ์์ ์ ๋ํด์๋ ๋ฉ์ธ ์ค๋ ๋๊ฐ ์ฒ๋ฆฌํ๊ณ , ์ด์ธ์ I/O ์์ ์ ๋ชจ๋ libuv ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด ์ธ๋ถ๋ก ์์ํ๋ค. ์ด๋ libuv๋ ์์ฃผ ์๋ฆฌํ๊ฒ ๋์ํ๋๋ฐ, ๋คํธ์ํฌ ํต์ ๊ฐ์ ์์ ์ ์ด์์ฒด์ ์ ์ปค๋์ ๋น๋๊ธฐ ๊ธฐ๋ฅ์ ํตํด ์ค๋ ๋ ๋ญ๋น์์ด ์ฒ๋ฆฌํ๊ณ , ํ์ผ ์์คํ ์ ๊ทผ์ด๋ ๋ฌด๊ฑฐ์ด ์ํธํ/์์ถ ์์ ๋ฑ๊ณผ ๊ฐ์ด OS๊ฐ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๊ธฐ ์ด๋ ค์ด ์์ ๋ง ๋ด๋ถ์ ์ค๋ ๋ ํ์ ์๋ ๋ค๋ฅธ ์ค๋ ๋์๊ฒ ๋งก๊ฒจ์ ๋ฉ์ธ ์ค๋ ๋๊ฐ ์ฐจ๋จ๋๋ ๊ฒ์ ๋ง๋๋ค.

๊ธฐ๋ณธ์ ์ผ๋ก ์ด๋ฌํ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ณ ์๋ค. Worker Threads ๋ถ๋ถ์ ์ ์ธํ๋ฉด ๋ชจ๋ ์์ญ์ด ๋ฉ์ธ ์ค๋ ๋ ํ๋๋ก ์ฒ๋ฆฌ๋๋ ์ ์ด๋ค. ๊ทผ๋ฐ ์ฌ๊ธฐ์ ์๋ฌธ์ด ๋ค์๋๊ฑด ๋ณดํต ํ๋ก์ธ์ค ํ๋์ ์ฌ๋ฌ ์ค๋ ๋๋ฅผ ์ปจํ ์คํธ ์ค์์นญ ํ๋๋ฐ, ๋ ธ๋๋ ์ด๋ป๊ฒ ๋ฉ์ธ ์ค๋ ๋ ํ๋๋ก V8 ์์ง, Node API๋ฅผ ํตํ V8 <-> libuv ๋ฐ์ธ๋ฉ, libuv ์คํ๊น์ง ์ํํ๋์ง๊ฐ ๊ถ๊ธํ๋ค.
์ด๊ฑด ์ฝ๋๋ฅผ ํ์ธํ๋ ๊ฒ์ผ๋ก ํด์ํ๋ค. ๋ ธ๋์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก v8 ์์ง์ผ๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋๋ฅผ ์ํํ๊ณ , ์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋ ์คํ์ด ๋๋๋ฉด ์ด๋ฒคํธ ๋ฃจํ ์ํ์ ํธ์ถํ๊ณ libuv๋ก ์ ์ด๊ถ์ด ๋์ด๊ฐ๋ค. ๊ทธ๋ผ ์ ์ด๊ถ์ ๊ฐ์ง libuv๊ฐ ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ๋๋ฉด์ ์์ ๋ค์ ์ฒ๋ฆฌํ๊ณ , ๊ฐ ํ์ด์ฆ์ ์ง์ ํ ๋ ํด๋น ํ์ด์ฆ์ ํ์์ ์ฝ๋ฐฑ์ ๊บผ๋ด V8์๊ฒ ์คํ์ ์์ํ๋ค.
์ด๋ฅผ node ๋ด ์ฝ๋๋ก ํ์ธํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ ์์๋ก ์ด์ด์ง๋ค. ์ค์ ์ฝ๋๋ฅผ ํ์ธํ๋ฉด์, AI์ ๋์์ ๋ฐ์ ํต์ฌ ํธ์ถ ํ๋ฆ์ ์ ๋ฆฌํด ๋ณด์์ผ๋ฉฐ ์์ธํ ๋ด์ฉ์ ๊ณต์ node github์์ ํ์ธํ ์ ์๋ค.
main() # node_main.cc
โ→ node::Start() # node.cc
โ→ StartInternal()
โ→ InitializeOncePerProcessInternal() # V8, libuv ์ด๊ธฐํ
โ→ NodeMainInstance ์์ฑ # V8 + libuv ์ฐ๊ฒฐ
โ→ NodeMainInstance::Run() # node_main_instance.cc
NodeMainInstance::Run()
โ→ LoadEnvironment() # app.js ์คํ ๋ด๋น
โ โ→ StartExecution(env, cb)
โ โ→ StartExecution(env, "internal/main/run_main_module")
โ โ→ ExecuteBootstrapper("internal/main/run_main_module")
โ โ→ CompileAndCall()
โ โ→ LoadBuiltinSource() # JS ์์ค์ฝ๋ ์ฝ๊ธฐ
โ โ→ LookupAndCompile() # V8์ผ๋ก ์ปดํ์ผ (JS → ๋ฐ์ดํธ์ฝ๋)
โ โ→ fn->Call() # ← ์ค์ JS ์คํ (app.js)
โ โ→ process, require ๋ฑ ์ฃผ์
โ
โ→ SpinEventLoopInternal() # ์ด๋ฒคํธ ๋ฃจํ ์์
โ→ uv_run() # libuv/core.c
โ→ while(hasPendingTasks) {
uv__run_timers() # Phase 1: Timers
uv__run_pending() # Phase 2: Pending Callbacks
uv__io_poll() # Phase 3: Poll
uv__run_check() # Phase 4: Check (setImmediate)
uv__run_closing_handles() # Phase 5: Close Callbacks
}
๊ทธ๋์ ์ ๋ฆฌํ๋ฉด ์๋ฐ์คํฌ๋ฆฝํธ๋ ํ์ ์คํฌ๋ฆฝํธ๋ก ์์ฑ๋ ์๋ฒ ์ฝ๋๋ฅผ ์คํํ๋ฉด, ๋ ธ๋๋ ์๋ฒ ๊ตฌ์ฑ์ ์ํด ํ์ํ ์ฝ๋๋ค์ ์คํํ๊ณ ์ ์ด๊ถ์ libuv์ ๋๊ฒจ ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ์ํํ๋๋ก ํ๋ค. ์ฌ์ฉ์์ ์์ฒญ๋ ๊ฒฐ๊ตญ ๋คํธ์ํฌ I/O ์์ ์ด๊ธฐ ๋๋ฌธ์ ์ด๋ฒคํธ ๋ฃจํ์์ ์ด๋ฅผ ์บ์นํด v8 ์์ง์๊ฒ ์ํ์ ๋๊ฒจ์ค ์ ์๋ ๊ฒ์ด๋ค. ์ฆ, node๊ฐ ์๋ฒ ์ฌ์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ผ๋ก ์ฌ์ฉ๋ ์ ์๋ ์ด์ ์ ์ด๋ฒคํธ ๋ฃจํ๊ฐ ํต์ฌ์ ์ธ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
ํ์ง๋ง ์ด๋ ๊ฒ I/O๋ฅผ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๋คํด๋ ๋ฉ์ธ ์ค๋ ๋ ํ๋๋ง์ผ๋ก ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ์ํํ๋ ๊ฒ์ ์ฌ๋ฌ ์ค๋ ๋๋ฅผ ์ด์ฉํ๋ ์๋ฐ/์คํ๋ง๋ณด๋ค ์์ฒญ ์ฒ๋ฆฌ์ ๋นํจ์จ์ ์ผ ๊ฒ ๊ฐ๋ค๊ณ ์๊ฐํ๋ค. ์ด์ ๋ํ ์๊ฒฌ๋ ๋ง์ฐฌ๊ฐ์ง๋ก Ryan Dahl: Node JS ์์์์ ์ ์ ์์๋๋ฐ, ์ค๋ ๋์ ์คํ์ด ์์ ์๋ฃ๊น์ง ๋๊ธฐํ๋ ์ํฉ์ด ๋ง์์ ๋ค์ง ์์๊ณ ์ปจํ ์คํธ ์ค์์นญ ๋น์ฉ๊ณผ ์ค๋ ๋๋ก ์ธํ ๋ฉ๋ชจ๋ฆฌ ๋น์ฉ์ด ๋น์ธ๋ค๊ณ ์๊ฐํ๋ค๊ณ ํ๋ค. ๋ํ, ๋ค์ค ์ค๋ ๋ ํ๊ฒฝ์์์ ๋์์ฑ ๋ฌธ์ ๋ฅผ ์ง์ ํ๋ค.
๊ทธ๋์ Ryan Dahl์ ์น ์๋ฒ์ธ Apache์ Nginx๋ฅผ ๋น๊ตํ๋๋ฐ, ์์ ์ ์๋ฃ์์ ์ปค๋ฅ์ ๋ง๋ค ์ค๋ ๋๋ฅผ ์์ฑํ๋ Apache ๋ฐฉ์๋ณด๋ค Nginx์ ์ด๋ฒคํธ ๋๋ฆฌ๋ธ์ด ๋ ์ ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด์๋ ๊ฐ์ ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์๋ ๊ฒ์ ํ์ธ์์ผ์คฌ๋ค. ๋ํ, ๋ญ๊ฐ ๊ทธ๋ํ๋ฅผ ํ๋ ๋ ๋ณด์ฌ์คฌ๋๋ฐ ์์งํ ๋งํ๋ฉด ์์์ ํ์ง ๋๋ฌธ์ ์ดํดํ์ง ๋ชปํ๋ค.

๊ทธ๋์ Node.js์ ์์ฒญ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ์ผ๋ก Nginx์ ์ด๋ฒคํธ ๋๋ฆฌ๋ธ ์ํคํ ์ฒ์ ์ฑํํ๋ค. Nginx์ ์ด๋ฒคํธ ๋๋ฆฌ๋ธ ์ํคํ ์ฒ๋ Node.js์ ์ ์ฌํ๋ค. ๋ง์คํฐ ํ๋ก์ธ์ค๊ฐ ์ค์ ํ์ผ์ด๋ ์์ปค ํ๋ก์ธ์ค ์์ฑ ๋ฐ ๊ด๋ฆฌ๋ฅผ ๋ด๋นํ๊ณ ๊ทธ ์๋ ์์ปค ํ๋ก์ธ์ค๋ค์ด ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ๋๋ฉด์ ๋ ผ๋ธ๋กํน I/O ๋ฐฉ์์ผ๋ก ๋์ํ๋ค. ์์ปค ํ๋ก์ธ์ค ํ๋๊ฐ ์ ํํ Node.js์ ๋์ ๋ฐฉ๋ฒ์ด๋ค.

Nginx๊ฐ ์ด๋ฒคํธ ๋๋ฆฌ๋ธ์ผ๋ก Apache๋ฅผ ์๋ํ ๊ฒ์ฒ๋ผ, Node.js๋ ๊ฐ์ ์๋ฆฌ๋ก ๋์ ์์ฒญ์ ์ฒ๋ฆฌํ๋ค. ๊ทธ๋ ๋ค๋ฉด ์ด ์ด๋ฒคํธ ๋ฃจํ๋ ์ ํํ ์ด๋ป๊ฒ ๋์ํ๋๊ฑธ๊น?
์ด๋ฒคํธ ๋ฃจํ
์์ ์ด๋ฒคํธ ๋ฃจํ๊ฐ ๊ต์ฅํ ์ค์ํ ๊ตฌ์ฑ์์ ์ค ํ๋์์ ์๊ฒ๋์๋ค. ๊ทธ๋์ ์ด๋ฒคํธ ๋ฃจํ๊ฐ ์ ํํ ๋ฌด์์ด๊ณ , ์ด๋ป๊ฒ ๋์ํ๋์ง๋ ๊ณต๋ถํ๋ฉด ์ข์ ๊ฒ ๊ฐ๋ค๊ณ ์๊ฐํ๋ค.
์ด๋ฒคํธ ๋ฃจํ๋ ์ฌ๋ฌ ๋น๋๊ธฐ ์์ ๋ค์ ๊ด๋ฆฌํ๊ธฐ ์ํ ๊ตฌํ์ฒด์ด๋ค. ๋น๋๊ธฐ ์์ ์ข ๋ฅ์ ๋ฐ๋ผ ์๋ฃ๋ ์ฝ๋ฐฑ์ด ์ฌ๋ฌ ํ์ด์ฆ์ ๋๋ ์ ์ฅ๋๊ณ , ์ด๋ฒคํธ ๋ฃจํ๊ฐ ํด๋น ํ์ด์ฆ์ ์ง์ ํ ๋ ์คํ๋๋ค. ์ ์ฅํ๋ค๋ ๋ง์ ๊ฐ ํ์ด์ฆ๊ฐ ์์ ๋ง์ ํ๋ฅผ ํ๋์ฉ ๊ฐ์ง๊ณ ์๋ค๋ ๋ป์ด๋ค.

์ ๊ทธ๋ฆผ์์ ๊ฐ ๋ฐ์ค๊ฐ ์์ ๋งํ 'ํ์ด์ฆ(phase)'์ด๊ณ , ์ด๋ฒคํธ ๋ฃจํ๊ฐ ํน์ ํ์ด์ฆ์ ์ง์ ํ๋ฉด ํด๋น ํ์ด์ฆ์ ํ์์ ์ฝ๋ฐฑ์ ์คํํ์ฌ ํ๊ฐ ์์ง๋๊ฑฐ๋ ์ต๋ ์ฝ๋ฐฑ ์๊ฐ ์คํ๋ ๋๊น์ง ๋ฐ๋ณตํ๋ค. ๊ทธ ํ, ํ๊ฐ ์์ง๋๊ฑฐ๋ ์ฝ๋ฐฑ ํ๋์ ๋๋ฌํ๋ฉด ์ด๋ฒคํธ ๋ฃจํ๋ ๋ค์ ๋จ๊ณ๋ก ์ด๋ํ๋ฉด์ ์งํ๋๋ ๋ฐฉ์์ด๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ํ ๋ฐํด ๋์๋ค๋ฉด, ๋ค์ ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ์์ํ๊ธฐ ์ ์ ๋๊ธฐ ์ค์ธ ๋น๋๊ธฐ I/O๋ ํ์ด๋จธ๊ฐ ์๋์ง ํ์ธํ๋ค. ์๋ฌด๊ฒ๋ ๋จ์์์ง ์๋ค๋ฉด ํ๋ก์ธ์ค๊ฐ ์ข ๋ฃ๋๊ณ , ๋จ์์๋ค๋ฉด ๋ค์ pending callbacks ํ์ด์ฆ๋ถํฐ ์ํ๋ฅผ ์์ํ๋ค. ์ค์ ๋ก ๋ ธ๋ ์ฝ๋#core.c๋ฅผ ํ์ธํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ํ์ด์ฆ๋ฅผ ์งํํ๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
int uv_run(uv_loop_t* loop, uv_run_mode mode) {
// ์ต์ด ํ์ด๋จธ ์คํ
if (mode == UV_RUN_DEFAULT && r != 0 && loop->stop_flag == 0) {
uv__update_time(loop);
uv__run_timers(loop);
}
// ์ด๋ฒคํธ ๋ฃจํ ํ์ด์ฆ ์ํ
while (r != 0 && loop->stop_flag == 0) {
uv__run_pending(loop);
uv__run_idle(loop);
uv__run_prepare(loop);
uv__io_poll(loop, timeout);
uv__run_check(loop);
uv__run_closing_handles(loop);
uv__update_time(loop);
uv__run_timers(loop);
r = uv__loop_alive(loop);
if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
break;
}
/* The if statement lets gcc compile it to a conditional store. Avoids
* dirtying a cache line.
*/
if (loop->stop_flag != 0)
loop->stop_flag = 0;
return r;
}
๋ณดํต ์ด๋ฒคํธ ๋ฃจํ์ ๋ํด์ ๊ตฌ๊ธ ๊ฒ์์ ํด๋ณด๋ฉด, timers ํ์ด์ฆ๋ถํฐ ์์ํ๋ค๋ ๊ฒ์ผ๋ก ์ค๋ช ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค. ์ด๋ ์ ์ฝ๋์์ ๋ณด๋ฏ์ด ์ต์ด ํ์ด๋จธ ํ์ด์ฆ๋ฅผ ๋จผ์ ์ํํ๊ณ ๋ฃจํ์ ๋ค์ด๊ฐ๊ธฐ ๋๋ฌธ์ ํธ์์ timers ํ์ด์ฆ๋ฅผ ๊ฐ์ฅ ์๋จ์ ๋๋ ๊ฒ์ด๋ค. ํ์ง๋ง ์ ํํ๋ ์ง์ ์ timers ํ์ด์ฆ๋ฅผ ์ํํ๊ณ ๋ฃจํ์ ๋ค์ด๊ฐ๋ค.
์ด์ ๊ฐ ํ์ด์ฆ์์ ์ด๋ค ๋น๋๊ธฐ ์์ ์ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ดํ๋์ง ์์๋ณด์.
timers phase
ํด๋น ํ์ด์ฆ๋ setTimeout()๊ณผ setInterval()์ ์ํด ์์ฝ๋ ์ฝ๋ฐฑ์ ์คํํ๋ค. ๋ณดํต ํ์ด๋จธ๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์ํ๋ ๋๋ ์ด ์๊ฐ์ ์ฃผ์ด ์คํํ๊ฒ ๋๋ค.
const timeoutScheduled = Date.now();
setTimeout(() => {
const delay = Date.now() - timeoutScheduled;
console.log(`${delay}ms have passed since I was scheduled`);
}, 100);
ํ์ง๋ง ์ฌ๊ธฐ์ ๋๋ ์ด ์๊ฐ์ ์ ํํ ๊ทธ ์๊ฐ์ด ์๋๋ผ ์ ๊ณต๋ ์ฝ๋ฐฑ์ ์คํํ ์ ์๋ ์๊ณ๊ฐ์ ์ง์ ํ๋ ๊ฒ์ด๋ค. ์๊ณ๊ฐ์ ์ ํํ ์คํ ์๊ฐ์ด ์๋๋ผ ์คํ์ ์ํ ์ต์ ๋๊ธฐ ์๊ฐ์ ์๋ฏธํ๋ค. ์ฆ, 100ms๋ก ์ค์ ํ ์ ์ฝ๋๋ '์ ์ด๋ 100ms๋ ๊ธฐ๋ค๋ ธ๋ค๊ฐ ์คํํด๋ผ'๋ผ๋ ์๋ฏธ๊ฐ ๋๋ค.

๊ทธ๋์ ์ด๋ฅผ ์ฌ๋ฌ๋ฒ ์คํํด๋ณด๋ฉด ํญ์ ๊ฐ์ ์๊ฐ์ ๊ธฐ๋ค๋ฆฌ๊ณ ์คํ๋์ง๋ ์๋๋ค. ๊ทธ๋๋ ์ง์ ๋ ์๊ฐ์ด ์ง๋๋ฉด ๊ฐ๋ฅํ ํ ๋นจ๋ฆฌ ์คํ๋๋๋ก ํ์ง๋ง, ์ด์์ฒด์ ์ค์ผ์ค๋ง์ด๋ ๋ค๋ฅธ ์ฝ๋ฐฑ ์คํ์ผ๋ก ์ธํด ์ง์ฐ๋ ์ ์๋ค. ์๋ฅผ ๋ค์ด 1์ด์ ์๊ณ๊ฐ์ ๊ฐ์ง ํ์์์์ ์์ฝํด๋๊ณ , ๋ฉ์ธ ์ค๋ ๋๋ฅผ ๊ฐ์ ๋ก 3์ด๊ฐ ๋ฐ์๊ฒ ๋ง๋๋ ๋ค์ ์์ ๋ฅผ ๋ณด์.
const start = Date.now();
// 1์ด(1000ms) ๋ค์ ์คํํ๋ผ๊ณ ์์ฝ!
setTimeout(() => {
console.log(`ํ์ด๋จธ ์คํ๋จ! ์ง์ฐ ์๊ฐ: ${Date.now() - start}ms`);
}, 1000);
// ๋ฉ์ธ ์ค๋ ๋๋ฅผ ๊ฐ์ ๋ก 3์ด ๋์ ๋ฐ์๊ฒ ๋ง๋ฆ
const blockUntil = Date.now() + 3000;
while (Date.now() < blockUntil) {
// 3์ด ๋์ V8 ์์ง์ ์ด ๋ฃจํ์ ๊ฐํ์ ๋ค๋ฅธ ์ผ์ ๋ชป ํจ
}
console.log("๋ฉ์ธ ์ค๋ ๋ ์์
๋!");

๊ฒฐ๊ณผ๋ฅผ ํ์ธํด๋ณด๋ฉด 3์ด๊ฐ ์ง๋ ํ์ ํ์ด๋จธ ์ฝ๋ฐฑ ํจ์๊ฐ ์คํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ๋ํ, Node๋ ๊ฒฐ๊ตญ ์ด์์ฒด์ ์์์ ๋์๊ฐ๋ ํ๋์ ํ๋ก๊ทธ๋จ์ด๋ค. ๊ทธ๋์ ๋ฉ์ธ ์ค๋ ๋๊ฐ ํ ๋น์ด์๋ค๊ณ ํด๋ CPU์ ์ปจํ ์คํธ ์ค์์นญ์ ๋ฐ๋ผ Node์ ๋ฆ๊ฒ ํ ๋น๋๋ค๋ฉด ๊ทธ๋งํผ ํ์ด๋จธ ์คํ๋ ์ง์ฐ๋๋ค๋ ํน์ง์ ๊ฐ์ง๋ค.
์ข ๋ ์ ํํ ์ดํดํ๊ธฐ์ํด ๋ด๋ถ ์ฝ๋๋ฅผ ํตํด ๋ถ์ํด๋ณด์.
int uv_run(uv_loop_t* loop, uv_run_mode mode) {
// ...
uv__update_time(loop);
uv__run_timers(loop);
// ...
}
์๊น ์ด๋ฒคํธ ๋ฃจํ ์ฝ๋์์ ํ์ธํ ์ ์๋๋ฐ, timers ํ์ด์ฆ๋ ํญ์ `uv__update_time`์ด๋ผ๋ ๋ฉ์๋์ ํจ๊ป ์ํ๋๋ค. `uv__update_time`์ ์ด๋ฒคํธ ๋ฃจํ์ ์๊ฐ์ ์ ๋ฐ์ดํธ ํ๋ ๋ฉ์๋๋ก timers ํ์ด์ฆ๊ฐ ๋ง๋ฃ๋ ์๊ฐ์ ๊ฒ์ฆํ๋๋ฐ ๊ธฐ์ค์ด ๋๋ ์๊ฐ์ผ๋ก ํ์ฉํ๋ค.
`uv__run_timers` ๋ฉ์๋ ๋ด๋ถ๋ฅผ ํ์ธํ๋ฉด,
void uv__run_timers(uv_loop_t* loop) {
struct heap_node* heap_node;
uv_timer_t* handle;
struct uv__queue* queue_node;
struct uv__queue ready_queue;
uv__queue_init(&ready_queue);
for (;;) {
heap_node = heap_min(timer_heap(loop));
if (heap_node == NULL)
break;
handle = container_of(heap_node, uv_timer_t, node.heap);
if (handle->timeout > loop->time)
break;
uv_timer_stop(handle);
uv__queue_insert_tail(&ready_queue, &handle->node.queue);
}
while (!uv__queue_empty(&ready_queue)) {
// ... ์ฝ๋ฐฑ ํจ์ ์คํ
handle->timer_cb(handle);
}
}
๋จผ์ heap_min์ ํตํด ํ์์ ๋ฐ์ดํฐ๋ฅผ ๊บผ๋ด์ค๋ ๊ฒ์ ์ ์ ์๋ค. ์ด๋ timers ํ์ด์ฆ๊ฐ ์ฌ์ค์ ํ๊ฐ ์๋๋ผ ์ต์ ํ(min heap) ์ ํตํด ํ์์์ ๊ด๋ จ ์ ๋ณด๋ฅผ ๋ณด๊ดํ๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ด ์ ๋ณด๋ค์๋ ๋ง๋ฃ ์๊ฐ๊ณผ ์ฝ๋ฐฑ ํจ์๊ฐ ๋ํ์ ์ผ๋ก ํฌํจ๋๋ค. ๊ทธ๋์ ๋ง๋ฃ ์๊ฐ์ ๊ธฐ์ค์ผ๋ก ์ต์ ํ์ ์ ์ฅํ๋ ๊ฒ์ด๋ค.
๋ง๋ฃ ์๊ฐ์ ๊ธฐ์ค์ผ๋ก ์ต์ ํ์ ์ ์ฅํ๊ธฐ์ ํ์์ ๋ฐ์ดํฐ๋ฅผ ๊บผ๋์ ๋ ํด๋น ๋ฐ์ดํฐ์ ๋ง๋ฃ ์๊ฐ๋ง์ผ๋ก ์ข ๋ฃ ์กฐ๊ฑด์ ํ๋จํ ์ ์๋ค.
int uv_timer_stop(uv_timer_t* handle) {
if (uv__is_active(handle)) {
heap_remove(timer_heap(handle->loop),
(struct heap_node*) &handle->node.heap,
timer_less_than);
uv__handle_stop(handle);
} else {
uv__queue_remove(&handle->node.queue);
}
// ...
return 0;
}
๋ง์ฝ ์ต์ ํ์์ ๊บผ๋ธ ๋ฐ์ดํฐ์ ๋ง๋ฃ ์๊ฐ์ด ์์ง ๋๋ฌํ์ง ์์๋ค๋ฉด, ์ดํ ์กด์ฌํ๋ ๋ฐ์ดํฐ๋ค๋ ์์ง ๋๋ฌํ์ง ์์์ํ ๋ ํ์ด๋จธ ํ์ด์ฆ๋ฅผ ์ข ๋ฃํ๋ค. ํ์ง๋ง, ๋ง๋ฃ ์๊ฐ์ ๋๋ฌํ๋ค๋ฉด ํด๋น ๋ฐ์ดํฐ๋ฅผ ํ์์ ์ ๊ฑฐํ๊ณ ์ฝ๋ฐฑ ํจ์๋ฅผ ์คํํ๋ค. ๊ทธ ํ ๋ค์ ์ต์ ํ์์ ๋ฐ์ดํฐ๋ฅผ ๊บผ๋ด ๋ง๋ฃ ์๊ฐ์ด ์ ์ ํ์ง ์๊ฑฐ๋, ํ์ด ๋น ๋๊น์ง ๋ฐ๋ณตํ๋ ๊ฒ์ด๋ค. ํ์ง๋ง timers ํ์ด์ฆ๋ ์ฝ๋ฐฑ ํ๋์ ์ํฅ์ ๋ฐ์ผ๋ฏ๋ก ๋ค์ ๋ฐ์ดํฐ๊ฐ ๋ง๋ฃ ์๊ฐ์ด ์ง๋ฌ๋คํด๋ ๋ค์ ๋ฃจํ๊น์ง ์ง์ฐ๋ ์ ์๋ค.
์ ๋ฆฌํ๋ฉด,
- ๋ ธ๋์์ ํ์ด๋จธ๋ ์๊ณ๊ฐ์ ์ค์ ํ๋ ๊ฒ์ด๋ฉฐ, ์ ํํ ์๊ฐ์ ๋ณด์ฅํ์ง ์๋๋ค.
- ํ์ด๋จธ ์ฝ๋ฐฑ ํจ์๋ ํ๊ฐ ์๋ ์ต์ ํ์ ์ ์ฅํ์ฌ ๊ด๋ฆฌํ๋ค.
pending callbacks
์ด ํ์ด์ฆ๋ ์ ํ๋ฆฌ์ผ์ด์ ๋ ๋ฒจ์ ์ผ๋ฐ์ ์ธ I/O ์ฝ๋ฐฑ๋ณด๋ค๋ ์์คํ ๋ ๋ฒจ์ ๋์ ์๋ฃ ์ฝ๋ฐฑ์ด ์คํ๋๋ ๊ณณ์ด๋ค. ์๋ฅผ ๋ค์ด, TCP ์์ผ์ด ์ฐ๊ฒฐ์ ์๋ํ๋ค๊ฐ ECONNREFUSED๋ฅผ ๋ฐ์์ ๋, ์ผ๋ถ ๋ฆฌ๋ ์ค ์์คํ ์์๋ ์ด ์๋ฌ๋ฅผ ์ฆ์ ๋ณด๊ณ ํ์ง ์๊ณ ์ฐ๊ธฐ(pending)ํ๋ค๊ฐ ์ด ํ์ด์ฆ์์ ์ฒ๋ฆฌํ๋ค.
์ผ๋ฐ์ ์ธ ์น ์๋ฒ ๋ก์ง์ ์์ฑํ ๋๋ ์ง์ ๋ง์ฃผ์น ์ผ์ด ๊ฑฐ์ ์๋ ๋ด๋ถ์ ์ธ ํ์ด๋ฏ๋ก, "์ด์์ฒด์ ์์คํ ๋ ๋ฒจ์ ์๋ฌ๋ ์ฐ๊ธฐ๋ ์ฝ๋ฐฑ๋ค์ ์ฒ๋ฆฌํ๋ ๋จ๊ณ" ์ ๋๋ก ์๊ณ ์์ผ๋ฉด ๋ ๊ฒ ๊ฐ๋ค.
idle, prepare phase
Node์ ๋ด๋ถ์ ์ผ๋ก๋ง ์ฌ์ฉ๋๋ค. ๊ณต์ ๋ฌธ์์๋ ๋ฑํ ์ค๋ช ์ด ์๋ค.
poll phase
poll phase์์ ๋ฉ์ธ ์ค๋ ๋๋ ๋ ๊ฐ์ง ์๋ฌด๋ฅผ ์ํํ๋ค.
- ๊ณ์ฐ: ์๋ก์ด I/O ์ด๋ฒคํธ๋ฅผ ์ํด ์ด์์ฒด์ ์ปค๋์๊ฒ ์ผ๋งํผ ๋๊ธฐ๋ฅผ ์์ฒญํ (Timeout) ๊ฒ์ธ๊ฐ?
- ์คํ: poll ํ์ ์๋ ์ด๋ฒคํธ ์คํ
๊ณ์ฐ
์ด๋ poll phase์ ํ๊ฐ ๋น์ด์์ ๋ ๋ฐ์ํ๋ค. ๋น์ด์์ผ๋ฉด ํ ์ผ์ด ์๋ค๋ ๋ป์ด๋ฏ๋ก ๋ ๊ฐ์ง๋ก ๋ถ๊ธฐํ๊ฒ ๋๋๋ฐ, setImmediate()๋ก ์์ฝ๋ ์ฝ๋๊ฐ ์๋ค๋ฉด ๋ ์ด์ ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ poll phase๋ฅผ ์ข ๋ฃ ํ ๋ค์ ๋จ๊ณ์ธ check phase๋ก ๋์ด๊ฐ๋ค. ํ์ง๋ง, ์์ฝ๋ ์ฝ๋๊ฐ ์๋ค๋ฉด ์๋ก์ด I/O ์ด๋ฒคํธ๊ฐ ์ฌ ๋๊น์ง ๋ฌดํํ ๋๊ธฐํ๋ค.
๊ทธ๋ ๋ค๊ณ ๋ง๋ฅ ๋ฌด์์ ๋๊ธฐํ์ง๋ ์๋๋ค. ์๋ํ๋ฉด ์์ฝ๋ ํ์ด๋จธ์ ๋ง๋ฃ ์๊ฐ์ด ๋์์ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋์ poll ํ๊ฐ ๋น์ด์ ๋๊ธฐํด์ผ ํ ๋, timers phase์ ์ต์ ํ์ ๋จผ์ ํ์ธํ๊ณ ํ์ด๋จธ๊ฐ ์๋ค๋ฉด ํด๋น ๋ง๋ฃ ์๊ฐ์ ๊ธฐ์ค์ผ๋ก wait timeout์ ์ค์ ํ๋ค.
๊ทธ๋ฌ๋ฉด poll phase๋ ์ด์์ฒด์ ์ปค๋์๊ฒ ์๋ก์ด I/O ์์ฒญ์ด ์ค๊ฑฐ๋, ์ค์ ํ ํ์์์ ์๊ฐ์ด ์ง๋๋ฉด ๊นจ์๋ฌ๋ผ๊ณ ์์ํ๋ค. ๋ง์ฝ ์ฌ๊ธฐ์ ํ์ด๋จธ๊ฐ ๋ง๋ฃ๋๋ฉด ์ปค๋์ ๋ค์ ๋ฉ์ธ ์ค๋ ๋๋ฅผ ๊นจ์ฐ๊ณ , poll phase ์ข ๋ฃ ํ ๋๋จธ์ง phase๋ฅผ ๋ง์ ์ํํ ํ์ timers phase์์ ํ์ด๋จธ ์ฝ๋ฐฑ ํจ์๋ฅผ ์ํํ๋ค.
int uv_run(uv_loop_t* loop, uv_run_mode mode) {
// ...
while (r != 0 && loop->stop_flag == 0) {
// ...
timeout = 0;
if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT)
timeout = uv__backend_timeout(loop);
// ...
uv__io_poll(loop, timeout);
// ...
}
}
static int uv__backend_timeout(const uv_loop_t* loop) {
if (loop->stop_flag == 0 // ๋ฃจํ ์ข
๋ฃ ์์ฒญ ์์
&& (uv__has_active_handles(loop) || uv__has_active_reqs(loop)) // ์ฒ๋ฆฌํ ์์
์ด ๋จ์์์
&& uv__queue_empty(&loop->pending_queue) // pending ํ๊ฐ ๋น์ด์์
&& uv__queue_empty(&loop->idle_handles) // idle ํธ๋ค ์์
&& (loop->flags & UV_LOOP_REAP_CHILDREN) == 0 // ์ข
๋ฃ๋ ์์ ํ๋ก์ธ์ค ์์
&& loop->closing_handles == NULL // ๋ซ์ ํธ๋ค ์์
)
return uv__next_timeout(loop);
return 0;
}
int uv__next_timeout(const uv_loop_t* loop) {
const struct heap_node* heap_node;
const uv_timer_t* handle;
uint64_t diff;
heap_node = heap_min(timer_heap(loop));
if (heap_node == NULL)
return -1; /* block indefinitely */
handle = container_of(heap_node, uv_timer_t, node.heap);
if (handle->timeout <= loop->time)
return 0;
diff = handle->timeout - loop->time;
if (diff > INT_MAX)
diff = INT_MAX;
return (int) diff;
}
์ฝ๋๋ฅผ ๋ณด๋ฉด, ์ฌ๋ฌ ์กฐ๊ฑด์ ๊ฒ์ฌํ๊ณ ํ์ด๋จธ์ ์ต์ ํ์ ๊ธฐ์ค์ผ๋ก timeout ๊ฐ์ ์ค์ ํ ํ์ poll phase๋ฅผ ํธ์ถํ๋ ๊ฒ์ ์ ์ ์๋ค.
์คํ
์คํ์ ๋จ์ํ ํ๊ฐ ๋น์ด์์ง ์์ ๊ฒฝ์ฐ์ด๋ฏ๋ก ๋ฉ์ธ ์ค๋ ๋๋ ํ์ ์์ธ ์์ ๋ค์ ์์๋๋ก ๊บผ๋ด ์ฒ๋ฆฌํ๋ค. ์ด๋ ์ฒ๋ฆฌ๋๋ ์์ ๋ค์๋ ํํ ์๋ 'HTTP ์์ฒญ ์ ์', 'DB ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ ๋ฐํ', 'ํ์ผ ์ฝ๊ธฐ ์๋ฃ' ๋ฑ ๋ฐฑ์๋ ์๋ฒ๊ฐ ์ฒ๋ฆฌํ๋ ๋๋ถ๋ถ์ ๋น๋๊ธฐ I/O ๊ฒฐ๊ณผ๋ฌผ๋ค์ด๋ค.
๊ทธ๋ฌํ ๋น๋๊ธฐ I/O ์ค ํ์ผ I/O์ฒ๋ผ OS๊ฐ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๊ธฐ ์ด๋ ค์ด ์์ ์ worker thread(libuv Thread Pool)๊ฐ ์ฒ๋ฆฌํ๊ณ , ์๋ฃ๋๋ฉด ๋ฉ์ธ ์ค๋ ๋์ ์ ํธ๋ฅผ ๋ณด๋ด ์ฝ๋ฐฑ์ ์คํํ๋ค.
Linux ๊ธฐ์ค์ผ๋ก ์ข ๋ ์ดํด๋ณด๊ธฐ
- ์ฝ๋๋ฅผ ๋ถ์ํ๊ธด ํ์ผ๋, ๋ถ์ ํํ ์ ๋ณด์ผ ์ ์์.
- ์ฝ๋๋ฅผ ๋จผ์ ์ ๋ฆฌํ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ๋ค.
[ํ์ผ I/O ํ๋ฆ]
1. JS ์ฝ๋ ์คํ
fs.readFile('file.txt', callback)
โ→ uv__work_submit()
โ→ ๊ธ๋ก๋ฒ wq์ uv__work ์ฝ์
โ→ uv_cond_signal() → idle worker thread ๊นจ์
# deps/uv/src/threadpool.c > uv__work_submit()
2. worker thread
while (uv__queue_empty(&wq)) uv_cond_wait() // ๊ธ๋ก๋ฒ wq ๋๊ธฐ
โ→ q = uv__queue_head(&wq) // ์์
๊บผ๋
โ→ w = uv__queue_data(q, struct uv__work, wq)
โ→ w->work(w) // ํ์ผ ์ฝ๊ธฐ ์คํ (blocking)
โ→ uv__queue_insert_tail(&w->loop->wq, &w->wq) // loop->wq์ ๊ฒฐ๊ณผ ์ฝ์
โ→ uv_async_send(&w->loop->wq_async) // ๋ฉ์ธ ์ค๋ ๋์ ์ ํธ
# deps/uv/src/threadpool.c > worker()
3. uv_async_send()
โ→ uv__async_send()
โ→ Linux: write(eventfd, &val, 8) // eventfd์ write
โ→ ๊ทธ ์ธ: write(async_wfd, "", 1) // pipe์ write
โ→ ๋ฉ์ธ ์ค๋ ๋ epoll_wait ๊นจ์ด๋จ
# deps/uv/src/unix/async.c > uv_async_send(), uv__async_send()
4. ๋ฉ์ธ ์ค๋ ๋
wq_async ํธ๋ค๋ฌ ํธ์ถ
โ→ uv__queue_move(&loop->wq, &wq) // loop->wq๋ฅผ ๋ก์ปฌ๋ก ์ด๋
โ→ while (!uv__queue_empty(&wq))
โ→ w->done(w, err) // JS ์ฝ๋ฐฑ ์คํ
# deps/uv/src/threadpool.c > uv__work_done()
[๋คํธ์ํฌ I/O ํ๋ฆ]
1. JS ์ฝ๋ ์คํ
http.createServer().listen(3000)
โ→ watcher_queue์ FD + ์ฝ๋ฐฑ ๋ฑ๋ก
# deps/uv/src/unix/core.c
2. poll phase ์ง์
โ→ watcher_queue ์ํ
โ→ epoll_ctl(epfd, EPOLL_CTL_ADD, fd, ...) // ์ปค๋์ FD ๊ฐ์ ๋ฑ๋ก
โ→ epoll_wait(epfd, events, timeout) // ๋๊ธฐ
# deps/uv/src/unix/linux/epoll.c > uv__io_poll()
3. ์ปค๋: FD ์ด๋ฒคํธ ๊ฐ์ง
โ→ epoll_wait ๋ฐํ
โ→ pe->fd๋ก loop->watchers[fd] ์ฐพ๊ธฐ
โ→ w->cb(loop, w, pe->events) // JS ์ฝ๋ฐฑ ์คํ
# deps/uv/src/unix/linux/epoll.c > uv__io_poll()
[์ด๊ธฐํ]
heap_init(&loop->timer_heap) // timers ์ต์ ํ
uv__queue_init(&loop->wq) // ํ์ผ I/O ์๋ฃ ํ
uv__queue_init(&loop->watcher_queue) // ๋คํธ์ํฌ I/O ๊ฐ์ ํ
uv__queue_init(&loop->pending_queue) // pending callbacks ํ
uv_async_init(loop, &loop->wq_async, uv__work_done) // worker ์๋ฃ ํธ๋ค๋ฌ ๋ฑ๋ก
# deps/uv/src/unix/core.c > uv_loop_init()
์ฌ๊ธฐ์ poll phase๋ wq(work queue)์ watcher_queue๋ฅผ ํ์ฉํ๋ค. ์ฌ๊ธฐ์ ๋คํธ์ํฌ๋ฅผ ํ๋๋ ํ์ง ์๋๋์ ๋ฐ๋ผ์ ์ฌ์ฉ๋๋ ํ์ ๋ฉ์ปค๋์ฆ์ด ๋ค๋ฅด๋ค. wq์ ๊ฒฝ์ฐ์๋ worker thread(libuv Thread Pool)๋ฅผ ์ด์ฉํ ์์ ์ ๊ฒฐ๊ณผ๊ฐ ๋ด๊ธฐ๋ ํ์ด๋ค. ๊ทธ๋์ ํ์ผ ์ฝ๊ธฐ์ฒ๋ผ ์์ผ์ ๊ฐ์(epoll_ctl)๋ก ์ฒ๋ฆฌ๋์ง ์๋ ์์ ์ ๊ฒฝ์ฐ์ worker thread๊ฐ ์ด๋ฅผ ์ฒ๋ฆฌํ๊ณ ํด๋น ํ์ ์ฝ๋ฐฑ ํจ์๋ฅผ ์ฝ์ ํ๋ค.
watcher_queue๋ ์์ผ์ ํตํด ๋คํธ์ํฌ๋ฅผ ํ๊ณ '์ด๋ค ํ์ผ ๋์คํฌ๋ฆฝํฐ(File Descriptor)์ ์ด๋ฒคํธ๋ฅผ ๊ฐ์ํด์ผ ํ๋์ง'์ ๋ํ ๋ฑ๋ก ์์ฒญ์ด ๋ด๊ธด๋ค. poll phase ์ง์ ์ ์ด ํ๋ฅผ ์ํํ๋ฉฐ ์ปค๋์ ๊ฐ์ ๋ฑ๋ก(epoll_ctl)์ ์์ฒญํ๊ณ , ์ดํ epoll_wait์ผ๋ก ๋๊ธฐํ๋ค๊ฐ ์ปค๋์ด ์ด๋ฒคํธ๋ฅผ ๊ฐ์งํ๋ฉด ์ฝ๋ฐฑ์ ์คํํ๋ค.
์ค์ ๋ก ํ๊ฐ ๋น์ด์์ง ์์ ๊ฒฝ์ฐ๋ wq์ ์๋ ์์ ๊ณผ watcher_queue๋ก ๊ฐ์๋ฅผ ์์ฒญํ epoll์ด ์ด๋ฒคํธ๋ฅผ ๊ฐ์งํ์ ๋๋ฅผ ์๋ฏธํ๋ ๊ฒ์ด๋ค.
๋คํธ์ํฌ I/O์ watcher_queue
watcher_queue์๋ ์์ผ์ ํตํด ๋คํธ์ํฌ๋ฅผ ํ๊ณ '์ด๋ค ํ์ผ ๋์คํฌ๋ฆฝํฐ(File Descriptor)์ ์ด๋ฒคํธ๋ฅผ ๊ฐ์ํด์ผ ํ๋์ง'์ ๋ํ ๋ฑ๋ก ์์ฒญ์ด ์์๋ก ๋ด๊ธด๋ค.
๊ฐ๋จํ๊ฒ ์๋ฅผ ๋ค์ด DB์ ์ฟผ๋ฆฌ๋ฅผ ์ํํ๋ ๋น๋๊ธฐ ์์ฒญ์ ๋ณด๋๋ค๊ณ ํ์. ๊ทธ๋ฌ๋ฉด ํด๋น DB์ ํต์ ์ ๋ด๋นํ๋ ๋คํธ์ํฌ ์์ผ(ํ์ผ ๋์คํฌ๋ฆฝํฐ)์ ์ด๋ฒคํธ๋ฅผ ๊ฐ์ํ๊ธฐ ์ํด ์ด์ ๊ด๋ จ๋ ์ ๋ณด๋ค์ด watcher_queue์ ๋ฑ๋ก๋๋ค.
๊ทธ ํ poll phase์ ์ง์ ํ์ฌ poll ํ๊ฐ ๋น์ด์์ ๋, ๋ฉ์ธ ์ค๋ ๋๋ ๋๊ธฐ ์ง์ ์ watcher_queue์ ๋ด๊ธด ์ ๋ณด๋ค์ ๊บผ๋ด ์ด์์ฒด์ ์๊ฒ ํด๋น ์์ผ๋ค์ ์ํ ๋ณํ๋ฅผ ๊ฐ์ง(epoll_ctl)ํด๋ฌ๋ผ๊ณ ์์ฒญํ๋ค. ์ดํ ์์ ๊ณ์ฐ ๋จ๊ณ์์ ์ค์ ํ timeout๋งํผ ๋๊ธฐํ๋ค๊ฐ ์ปค๋์ด DB๋ก๋ถํฐ ๋ฐ์ดํฐ๊ฐ ๋์ฐฉํ๋ค๋ ์ ํธ๋ฅผ ์ฃผ๋ฉด, ๋ฉ์ธ ์ค๋ ๋๋ ํด๋น ์ด๋ฒคํธ์ ๋งคํ๋ ์ฝ๋ฐฑ ํจ์๋ฅผ V8 ์์ง์ผ๋ก ๊ณง์ฅ ์คํํ๋ ํ๋ฆ์ด๋ค.
ํ์ผ I/O์ wq(work queue)
๋ฐ๋ฉด์ ํ์ผ ์ฝ๊ธฐ์ ๊ฐ์ด ์ด์์ฒด์ ์ ์์ผ์ผ๋ก ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ์ด๋ ค์ด ์์ ์ worker thread(libuv Thread Pool)๋ฅผ ์ด์ฉํ๋ค. ๋ค์ ์์๋ฅผ ๋ณด์.
const fs = require('fs');
fs.readFile('any-file.txt', () => {
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
});
// ํด๋น ์ฝ๋์ ์คํ ๊ฒฐ๊ณผ๋
/*
immediate
timeout
*/
/*
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
ํด๋น ์ฝ๋์ ์คํ๊ฒฐ๊ณผ๋
timeout
immediate
immediate
timeout
๋ ์ค ์ด๋ ๊ฒ์ด ๋์ฌ์ง ํ์ ํ ์ ์๋ค.
setTimeout(..., 0);์ ์ฌ์ค 0ms๊ฐ ์๋๋ผ ๋
ธ๋์์ ์ ํ ์ต์๊ฐ์ธ 1ms๋ก ์ค์ ๋๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋์ ๋
ธ๋๊ฐ ์์๋๋ฉด์ ๋ ๋ฉ์๋๋ฅผ ์คํํ๊ณ ์ด๋ฒคํธ ๋ฃจํ์ ๋ค์ด๊ฐ๋ค. ํ์ง๋ง ๊ฐ์ฅ ์ฒ์ timers phase๋ฅผ ๊ฒ์ฌํ์ ๋ 1ms๊ฐ ์ง๋์ง ์์ ์ํฉ์ด๋ฉด ๋น ๋ฅด๊ฒ ๋ค์ phase๋ก ๋์ด๊ฐ๋ ๊ณผ์ ์ ๊ฑฐ์ณ check phase์ ๋๋ฌํ์ฌ immediate๊ฐ ๋จผ์ ์ถ๋ ฅ๋ ์๋ ์๊ณ ,
ํน์ ์ง์ฐ์ผ๋ก ์ธํด timers phase์์ 1ms๊ฐ ์ง๋ ์ํฉ์ด๋ผ timeout์ด ๋จผ์ ์ถ๋ ฅ๋ ์๋ ์๋ค.
*/
ํด๋น ์ฝ๋๋ฅผ ์คํํ๋ฉด fs.readFile ๊ตฌ๋ฌธ์ ์ฝ๊ณ worker thread(libuv Thread Pool)์๊ฒ ํด๋น ์์ ์ ์์ํ๋ค. ๊ทธ๋ฆฌ๊ณ poll phase์ ์ง์ ํ๋ฉด ํ์ผ ์ฝ๊ธฐ๊ฐ ๋๋๋ ๋์ ๋๊ธฐ ์ํ์ ๋ค์ด๊ฐ๋ค. (๋ง์ฝ ์์ฝ๋ ํ์ด๋จธ๊ฐ ์์๋ค๋ฉด ํ์ด๋จธ ๋ง๋ฃ ์๊ฐ๊น์ง๋ง ๋๊ธฐํ๋ค.)
์ด๋ ๋ ธ๋๋ ๋ด๋ถ์ ์ผ๋ก ์ด๋ฒคํธ ๋ฃจํ์ worker thread๋ผ๋ฆฌ ํต์ ํ ์ ์๋ ํ์ดํ(async_wfd)๋ฅผ ์ด์ด๋๊ณ ๊ฐ์ํ๊ณ ์๋ค. ์๊ฐ์ด ์ง๋๊ณ worker thread์์ ํด๋น ์์ ์ ๋ชจ๋ ์ฒ๋ฆฌํ๊ณ , ์์ ๊ฒฐ๊ณผ์ ์ฝ๋ฐฑ ํจ์๋ฅผ wq์ ์ฝ์ ํ๊ณ ๋ด๋ถ ํ์ดํ๋ฅผ ํตํด ๋ฉ์ธ ์ค๋ ๋์๊ฒ ์ ํธ๋ฅผ ๋ณด๋ธ๋ค. ๊ทธ๋ฌ๋ฉด ๋น๋ก์ ๋ฉ์ธ ์ค๋ ๋๋ ํด๋น ํ(wq)์์ ์์ ์ ๊บผ๋ด ์ฝ๋ฐฑ ํจ์๋ฅผ ์ํํ๋ ํ๋ฆ์ด๋ค.
uv__queue_insert_tail(&w->loop->wq, &w->wq);
uv_async_send(&w->loop->wq_async);
์ด๋ ๊ฒ ๋ค๋ฅธ ํ๋ก ๋์ํ๋ ์ด์ ๋ ๋ฆฌ๋ ์ค ์์ฒด์์ ํ์ผ์ ๋ํ epoll์ ์ง์ํ๊ณ ์์ง ์๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋์ wq๋ผ๋ ํ๋ฅผ ํตํด ์์ ์ ์ฒ๋ฆฌํ๋ค. ํ์ง๋ง, ์๋์ฐ์ ๊ฒฝ์ฐ์๋ ๋ฆฌ๋ ์ค์ฒ๋ผ watcher_queue, wq ๊ฐ์ ๊ฐ๋ ์ด ์๋ค. ์ด๋ IOCP(I/O completion Port)๋ผ๋ ๋น๋๊ธฐ I/O ์ปค๋ ๋ชจ๋ธ์ ์ ๊ณตํ๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋ฅ ๊ฐ๋จํ ์ด์์ฒด์ ์์ฒด์์ ๋ค ์ฒ๋ฆฌํด์ฃผ๋ ํ๋ฆ์ด๋ค. ๋๋ถ๋ถ ๋ฆฌ๋ ์ค, ๋งฅ ๊ธฐ๋ฐ์ผ๋ก ๊ฐ๋ฐ, ์ด์ํ๊ฒ ๋๋ ์๋์ฐ๋ ๋ฐ๋ก ์ฝ๋๋ฅผ ํ๋ณด๊ฑฐ๋ ํ์ง ์์๋ค.
์ ๋ฆฌํ๋ฉด,
- ํ์ ๋ฐ์ดํฐ๊ฐ ์์ ๋๋ ์ฝ๋ฐฑ ํจ์๋ฅผ ์์๋๋ก ๊บผ๋ด ๋ฉ์ธ ์ค๋ ๋์์ ์ฒ๋ฆฌํ๋ค.
- ํ๊ฐ ๋น์ด์์ ๋๋
- watcher_queue์ ์์ฌ์๋ ํ์ผ ๋์คํฌ๋ฆฝํฐ๋ฅผ ๊บผ๋ด ์ปค๋์ ๊ฐ์๋ฅผ ๋ฑ๋กํ๋ค.
- ๋ค์ phase์์ ์ฒ๋ฆฌํ ์์ ์ด ์๋ค๋ฉด, timer phase์ ํ์ด๋จธ ๊ธฐ์ค์ผ๋ก timeout์ ์ค์ ํ์ฌ ๋๊ธฐ๋ฅผ ์์ฒญํ๋ค.
- ๋ค์ phase์์ ์ฒ๋ฆฌํ ์์ ์ด ์๋ค๋ฉด ๋ฐ๋ก ๋ค์ phase๋ก ๋์ด๊ฐ๋ค.
- ๋ ๋ค ์๋ค๋ฉด ์๋ก์ด I/O ์ด๋ฒคํธ๊ฐ ๋ค์ด์ฌ ๋๊น์ง ๋ฌดํ์ ๋๊ธฐํ๋ค.
check phase
ํด๋น ํ์ด์ฆ์์๋ ๋ค๋ฅธ ์์ ์ ์ผ์ ์์ด, ์ค์ง setImmediate()๋ก ๋ฑ๋กํ ์ฝ๋ฐฑ ํจ์๋ค๋ง ์ฒ๋ฆฌํ๋ ์ ์ฉ ๊ณต๊ฐ์ด๋ค. ๋ฑํ ์ค๋ช ํ ๋ด์ฉ์ด ์๋ค.
close callbacks phase
socket.on('close', cb)์ ๊ฐ์ด close, destory ์ด๋ฒคํธ ํ์ ์ฝ๋ฐฑ ์ฒ๋ฆฌ
์ฌ๊ธฐ๊น์ง๊ฐ ์ฌ์ค์ ์ด๋ฒคํธ ๋ฃจํ ํ์ด์ฆ์ ๋์ด๋ค. ํ์ง๋ง ์ด๋ฒคํธ ๋ฃจํ์ ์ผ๋ถ๋ ์๋์ง๋ง ๋ ธ๋์ ๋ฃจํ ๊ณผ์ ์์ ์ค์ํ ์ญํ ์ ํ๋ ์น๊ตฌ๊ฐ ์๋๋ฐ ๋ฐ๋ก nextTickQueue, microTaskQueue์ด๋ค.
nextTickQueue, microTaskQueue
process.nextTick() ์ ์ฝ๋ฐฑ๋ค์ด ์ ์ฅ๋๋ nextTickQueue์ Promise, queueMicrotask() ๋ฑ์ ์ฝ๋ฐฑ์ด ์ ์ฅ๋๋ microTaskQueue๋ ์ด๋ฒคํธ ๋ฃจํ์ ์ผ๋ถ๋ ์๋๋ค. ํ์ง๋ง, ์ด๋ฒคํธ ๋ฃจํ์ ํ์ด์ฆ์์ ํ์ฌ ์คํ ์ค์ธ ์์ (์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋)์ด ๋๋ ์งํ์ ๊ฐ์ฅ ์ฐ์ ์ ์ผ๋ก ํ์ธ๋๊ณ ์คํ๋๋ ํ์ด๋ค.
๊ทธ๋์ ๋ ํ๋ libuv๊ฐ ์๋๋ผ Node.js์ ํฌํจ๋๋ ๋ถ๋ถ์ด๋ค. ๋ ๊ฐ์ ํ ๋ชจ๋ ์์ ํ ์คํ๋๋ค๋ ํน์ง์ ๊ฐ์ง๊ณ ์์ง๋ง, ๋ ์ฌ์ด์๋ ์ฐ์ ์์๊ฐ ์กด์ฌํ๋ค. 1์์๋ nextTickQueue์ด๊ณ 2์์๋ microTaskQueue์ด๋ค.
์ข ๋, ์์ธํ๊ฒ ๋งํ์๋ฉด ํ์ด์ฆ์์ ์ฝ๋ฐฑ ํจ์๋ฅผ ์คํํ ๋๋ง๋ค ๋ ํ์ ๋ค์ด์๋ ์์ ์ด ์คํ๋๊ณ ์๋ ํ์ด์ฆ์ ๋ค์ ์์ ์ ์ํํ๋ค.

ํด๋น ๊ทธ๋ฆผ์ผ๋ก ์์ฝ์ด ๊ฐ๋ฅํ ๊ฑฐ ๊ฐ๋ค. Node.js ๋ด๋ถ์ธ C/C++์์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ฐฑ์ ์ํํ๊ณ , ๊ทธ ์ฝ๋ฐฑ์ด ๋๋์ ๋ค์ Node.js์ธ C/C++๋ก ๋์๊ฐ๋ ๊ฒฝ๊ณ์์ nextTick์ ํ์ธํ๊ณ ๋ ํ๊ฐ ๋ชจ๋ ๋น์ด์ง๋๊น์ง ๋จ์ ์ฝ๋ฐฑ์ ์คํํ๋ค.
node v11 ์ด์ ์๋ ํ์ด์ฆ ์ฌ์ด์ ์๋ ์ 'ํฑ(Tick)' ์ฆ, ๊ฐ ํ์ด์ฆ๋ฅผ ๋์ด๊ฐ๋ ๊ทธ ์ฌ์ด์ nextTick์ ํ์ธํ๊ณ ๋ ํ๊ฐ ๋น๋๊น์ง ์ํํ๋ ๋ฐฉ์์ผ๋ก ๋์ํ์ง๋ง ๋ธ๋ผ์ฐ์ ์์ ํธํ์ฑ์ ์ํด node v11๋ถํฐ๋ ์ฝ๋ฐฑ ์คํ ์งํ๋ก ๋ณ๊ฒฝ๋์๋ค๊ณ ํ๋ค. - New Changes to the Timers and Microtasks in Node v11.0.0 ( and above), MacroTask and MicroTask execution order
์ฌ๊ธฐ์ ์ฃผ์ํ ์ ์ nextTickQueue, microTaskQueue ๋ชจ๋ ๋น ๋๊น์ง ์ํ์ ํ๊ธฐ ๋๋ฌธ์ ์ฌ๊ท์ ์ธ process.nextTick() ํธ์ถ๋ก ์ธํด I/O ๊ธฐ์์ํ์ ๋น ์ง ์ ์์ด poll phase๋ก ๋๋ฌํ์ง ๋ชปํ ์ ์๋ค.
์ ํ์ํ๊ฑธ๊น?
๊ณต์ ๋ฌธ์์์ ํด๋น ๊ธฐ๋ฅ์ด Node.js์ ํฌํจ๋์ด์ผ ํ๋ ์ด์ ๋ฅผ ์ค๋ช ํด์ฃผ์๋ค. ์ด๋ ์ผ๋ถ API๊ฐ ๋ฐ๋์ ๋น๋๊ธฐ์ ์ด์ด์ผ ํ๋ค๋ ์ค๊ณ ์ฒ ํ ๋๋ฌธ์ด๋ผ๊ณ ํ๋ค. ์ด๊ฒ ์ ์ฌ์ ์ธ ๋ฌธ์ ๋ฅผ ์ด๋ํ๋ ์ํฉ์ผ๋ก ๋ค์ ์ฝ๋๋ฅผ ์ ์ํ๋ค.
let bar = null;
// this has an asynchronous signature, but calls callback synchronously
function someAsyncApiCall(callback) {
callback();
}
// the callback is called before `someAsyncApiCall` completes.
someAsyncApiCall(() => {
// since someAsyncApiCall hasn't completed, bar hasn't been assigned any value
console.log('bar', bar); // null
});
bar = 1;
ํด๋น ์ฝ๋์์ ์ฌ์ฉ์๊ฐ someAsyncApiCall() ๋ฉ์๋๋ฅผ ํธ์ถํ ๋ ๋น๋๊ธฐ๋ฅผ ๊ธฐ๋ํ์ง๋ง, ์ค์ ๋ก๋ ๋๊ธฐ์ ์ผ๋ก ํธ์ถ๋๋ค. ์๋ํ๋ฉด someAsyncApiCall()๋ฉ์๋ ํธ์ถ ๊ตฌ๋ฌธ์์ ์ฝ๋ฐฑ์ ๋ฑ๋กํ์ง๋ง, ๋ด๋ถ์์ ๊ทธ๋๋ก callback()์ ํธ์ถํ๊ธฐ ๋๋ฌธ์ ์ฝ์คํ ์์ฒด๊ฐ someAsyncApiCall() - callback() ์์ผ๋ก ๋ฑ๋ก๋๊ณ ํธ์ถ๋๋ค. ๊ทธ๋์ bar = 1; ์ฝ๋์ ์ ๊ทผํ๊ธฐ ์ ์ ์ด๋ฏธ bar๋ฅผ ์ฐธ์กฐํ์ฌ ์ฝ์์ ์ถ๋ ฅํ๊ธฐ์ ์์ธกํ์ง ๋ชปํ ์์ธ ์ํฉ์ด ๋ฐ์ํ๋ค.
์ด๋ฌํ ์ํฉ์ ๋ฐฉ์งํ๊ธฐ ์ํด์ ๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํด์ผ ํ๋ค.
let bar = null;
function someAsyncApiCall(callback) {
process.nextTick(callback);
}
someAsyncApiCall(() => {
console.log('bar', bar); // 1
});
bar = 1;
process.nextTick()์ ์ฝ๋ฐฑ์ ๋ฃ์ด, ํด๋น ์๋ฐ ์คํฌ๋ฆฝํธ์ ์คํ์ด ๋ชจ๋ ๋๋ ํ์ callback์ด ์คํ๋๋ ๊ฒ์ ๋ณด์ฅํ ์ ์๊ณ , ์ถ๋ ฅ ๊ฒฐ๊ณผ ๋ํ bar = 1; ์ดํ์ด๊ธฐ์ 1์ด ์ถ๋ ฅ๋จ์ ํ์ธํ ์ ์๋ค.
๊ทผ๋ฐ ์์งํ ์ด ์์ ๋ง์ผ๋ก๋ ์ ์ฌ์ฉํด์ผ ํ๋์ง ์ดํดํ๊ธฐ ์ด๋ ค์ ๋ค. ๋๊ตฐ๋ค๋ ๋ ธ๋๋ก ๊ฐ๋ฐ์ ํด๋ณด์ง๋ ์์์ ๋ ๊ทธ๋ฌ๋ ๊ฑฐ ๊ฐ๋ค. ๊ทธ๋์ AI๋ฅผ ํ์ฉํ์ฌ ํด๋น ๊ธฐ๋ฅ์ด ํ์ํ ์ค๋ฌด์ ์ธ ์ด์ ์ ๋ํด์ ๋ฌผ์ด๋ดค๋ค. ๊ทธ๋ฌ์ 2๊ฐ์ ์์ ๋ฅผ ํตํด ํด๋น ๊ธฐ๋ฅ์ ์กด์ฌ ์ด์ ์ ๋ํด์ ์ค๋ช ํด์ฃผ์๋ค.
์บ์ ์ฌ์ฉ์ ๋ํ ๋ฌธ์
์ ์ ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ getUser()๋ผ๋ ํจ์๋ฅผ ๋ง๋ค์๋ค๊ณ ๊ฐ์ ํด๋ณด์.
function getUser(id, callback) {
if (cache[id]) {
callback(cache[id]);
} else {
fetchFromDB(id, callback);
}
}
์ฒ์ ์์ฒญ์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์์ผ ํ๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ก ์๊ฐ์ด ๊ฑธ๋ฆฐ๋ค. ํ์ง๋ง ํ ๋ฒ ๊ฐ์ ธ์จ ์ ์ ์ ๋ณด์ ๋ํด์๋ ๋ฉ๋ชจ๋ฆฌ(์บ์)์ ์ ์ฅํด๋๊ณ , ๋ค์๋ฒ ์์ฒญ ๋๋ DB ์ ๊ทผ์์ด ์บ์์์ ๋๊ธฐ์ ์ผ๋ก ์ฆ์ ๊บผ๋ธ๋ค.
๊ทธ๋ฆฌ๊ณ ๋์ ํด๋ผ์ด์ธํธ ์ฝ๋์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋์ ๋ก๋ฉ ์ํ๋ฅผ ํํํ๊ธฐ ์ํด isLoading์ด๋ผ๋ ์ํ ๋ณ์๋ก ์ด๋ฅผ ์ฌ์ฉํ๋ค๊ณ ํด๋ณด์.
let isLoading = true; // 1. ๋ก๋ฉ ์์
getUser(1, (data) => {
console.log('๋ฐ์ดํฐ ๋์ฐฉ:', data);
isLoading = false; // 3. ๋ฐ์ดํฐ๊ฐ ์ค๋ฉด ๋ก๋ฉ ๋!
});
// 2. (๊ฐ๋ฐ์์ ์๋: ํจ์ ํธ์ถ ์งํ์ ๋ก๋ฉ ์ํ๋ฅผ ํ์คํ ์ผ๋ )
isLoading = true;
์ฒซ ๋ฒ์งธ ํธ์ถ ์์๋ DB์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋๋ฐ ์๊ฐ์ด ๊ฑธ๋ฆฌ๋ฏ๋ก ์ฝ๋๋ ์๋๋ก ์ญ ๋ด๋ ค๊ฐ์ isLoading = true; ๊ตฌ๋ฌธ์ด ๋จผ์ ์คํ๋๋ค. ๋์ค์ DB์์ ๋ฐ์ดํฐ๊ฐ ์ค๋ฉด ์ฝ๋ฐฑ์ด ์คํ๋์ด isLoading = false; ๊ตฌ๋ฌธ์ด ์คํ๋๋ฉด์ ๋ก๋ฉ ๋๋๋ฉฐ ์์๋๋ก ํ๋ฌ๊ฐ๋ค.
ํ์ง๋ง ๋ ๋ฒ์งธ ํธ์ถ๋ถํฐ๋ ์กฐ๊ธ ์ด์ํ๊ฒ ๋์ํ๋๋ฐ, ๋ ๋ฒ์งธ ํธ์ถ๋ถํฐ๋ ์บ์์ ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ๋ ๊ณง๋ฐ๋ก ์ฝ๋ฐฑ ํจ์๋ฅผ ํธ์ถํ๋ค. ๊ทธ๋์ isLoading = false; ๊ตฌ๋ฌธ์ด ์คํ๋๋ฉด์ ๋ก๋ฉ์ด ๋๋๋ค. ํ์ง๋ง ๊ณง๋ฐ๋ก getUser() ๋ฉ์๋ ์ดํ isLoading = true;๊ฐ ์คํ๋๋ฉด์ ๋ค์ ๋ก๋ฉ ์ํ๋ก ๋ค์ด๊ฐ๋ ๋ฌธ์ ๊ฐ ์๊ธด๋ค.
๊ทธ๋์ ์์ ์ฝ๋์์ ์บ์๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋๋ผ๋ ์ฝ๋ฐฑ์ ์คํ์ ๋ค๋ก ๋ฏธ๋ฃจ์ด์ผ ํ๋ค. ์ด๋ process.nextTick()์ ํ์ฉํ์ฌ ๋ค์๊ณผ ๊ฐ์ด ํด๊ฒฐํ ์ ์๋ค.
function getUser(id, callback) {
if (cache[id]) {
process.nextTick(() => callback(cache[id]));
} else {
fetchFromDB(id, callback);
}
}
์ด๋ฒคํธ ๋ฆฌ์ค๋ ๋ฌธ์
๋ ๋ฒ์งธ ์์ ๋ 8080 ํฌํธ๋ก ์๋ฒ๋ฅผ ์ด๊ณ (listen), ์๋ฒ๊ฐ ์ค๋น๋์๋ค๋ ์ด๋ฒคํธ(listening)๋ฅผ ๊ฐ์งํ๋ ์ฝ๋์ด๋ค.
const server = net.createServer(() => {}).listen(8080);
server.on('listening', () => {});
listen(8080)์ ํธ์ถํ๋ฉด Node.js ๋ด๋ถ์ ์ผ๋ก ํฌํธ ๋ฐ์ธ๋ฉ ์์ ์ด ์ฆ์, ๋๊ธฐ์ ์ผ๋ก ์๋ฃ๋๋ค. ๋ง์ฝ ํฌํธ๊ฐ ๋ฐ์ธ๋ฉ๋์๋ง์ ์๋ฒ๊ฐ ์ค๋น๋์๋ค๋ผ๋ 'listening' ์ด๋ฒคํธ๋ฅผ ๊ทธ ์ฆ์(๋๊ธฐ์ ์ผ๋ก) ํฐ๋จ๋ ค ๋ฒ๋ฆฐ๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
์ปดํจํฐ๋ ์์ง ๋ค์ ์ค์ธ server.on(...) ์ฝ๋๋ฅผ ์ฝ๊ธฐ๋ ์ ์ด๋ค. ์ฆ, ์ด๋ฒคํธ๋ฅผ ๋ฐ์ ๋ฆฌ์ค๋๊ฐ ์์ง ์ฅ์ฐฉ๋์ง๋ ์์๋๋ฐ ์ด๋ฒคํธ๊ฐ ๋จผ์ ๋ฐ์ฌ๋์ด ํ๊ณต์ผ๋ก ์ฆ๋ฐํด ๋ฌด์๋๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Node.js ๊ฐ๋ฐ์๋ค์ ๋ด๋ถ์ ์ผ๋ก ํฌํธ ๋ฐ์ธ๋ฉ์ ์ฆ์ ์ํํ๋, "์ด๋ฒคํธ๋ฅผ ํฐ๋จ๋ฆฌ๋ ์์ (emit)"๋ง process.nextTick()์ผ๋ก ๊ฐ์ธ์ ๋ค๋ก ๋ฏธ๋ค๋์๋ค. ๋๋ถ์ ์ฌ์ฉ์๊ฐ ์์ฑํ ์คํฌ๋ฆฝํธ๊ฐ ๋๊น์ง ๋ชจ๋ ์คํ๋์ด ์์ ๊ธฐ(.on)๊ฐ ๋ฌด์ฌํ ์ฅ์ฐฉ๋ ์ ์๋ ์๊ฐ์ ๋ฒ์ด์ฃผ๋ฉฐ, ์ดํ์ ์์ ํ๊ฒ ์ด๋ฒคํธ ๋ฆฌ์ค๋๊ฐ ๋์ํ ์ ์๋๋ก ๋ณด์ฅํด ์ค๋ค.
์ด์ธ์๋ ๋ง์ ์์ ๊ฐ ์๊ฒ ์ง๋ง, ๋ ธ๋๋ก ์ง์ ๊ฐ๋ฐ๊น์ง ํด๋ณธ ์ํ๋ ์๋๋ผ์ ๋ค๋ฅธ ์์ ๋ฅผ ๋ด๊ธฐ์ ์ด๋ ค์ ๋ค. ์ค๋ฌด ๊ฒฝํ์ด ์์ด๋ฉด ์ด ๋ํ ์ถ๊ฐํด๋ณผ ์ ์์ ๊ฒ ๊ฐ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ ธ๋ ๊ณต์๋ฌธ์์์๋ ์ฌ๋งํ๋ฉด ๋ชจ๋ ์ํฉ์์ setImmediate()๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํ๋ค๊ณ ํ๋ค. ๊ทธ๊ฒ ๊ฐ๋ฐ์๋ค์ด ์ดํดํ๊ธฐ ๋ ์ฝ๊ธฐ ๋๋ฌธ์ด๋ผ๊ณ ํ๋ค. ๋ํ, ์ฌ๋ฐ๋๊ฑด process.nextTick()๋ ์ฝ๋ฐฑ ํจ์ ์ดํ ๊ณง์ฅ ์คํ๋๊ณ , setImmediate()๋ check phase์ ์ด๋ฅด๋ฌ์์ผ ์คํ๋๋ฏ๋ก ๋์ ์ด๋ฆ์ ๋ฐ๊ฟ์ผ ํ๋๊ฒ ๋ง์ง๋ง ๋ฐ๊พธ๋ ์๊ฐ ์๋ฐฑ๋ง๊ฐ์ npm ํจํค์ง๊ฐ ์ฅ์ ๋ฅผ ์ผ์ผํค๋ ๊ทธ๋๋ก ๋๋ ๊ฒ์ด๋ผ๊ณ ํ๋ค.
๊ฒฐ๋ก

๋ ธ๋์ ์ ์ฒด ํ๋ฆ์ ์ ๋ฆฌํ๋ฉด ๊ทธ๋ฆผ๊ณผ ๊ฐ๋ค.
Node.js๋ V8์ด ์ฑ ์ฝ๋๋ฅผ ์คํํ ๋ค ์ ์ด๊ถ์ libuv์ ๋๊ฒจ ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ์์ํ๋ค. ๋ฃจํ๋ timers → pending callbacks → idle/prepare → poll → check → close callbacks ์์ผ๋ก ํ์ด์ฆ๋ฅผ ์ํํ๋ฉฐ, ๊ฐ ์ฝ๋ฐฑ ์คํ ์งํ๋ง๋ค nextTickQueue์ microTaskQueue๋ฅผ ์ฐ์ ์์งํ๋ค. I/O๋ ๋คํธ์ํฌ์ ๊ฒฝ์ฐ OS ์ปค๋(epoll)์ด, ํ์ผ ์์คํ ์ libuv Thread Pool์ด ๋ด๋นํ๊ณ , ์๋ฃ ์ ํธ๋ฅผ ๋ฉ์ธ ์ค๋ ๋์ ๋ณด๋ด poll ํ์ด์ฆ์์ ์ฝ๋ฐฑ์ ์คํํ๋ค.
์ฌ๊ธฐ๊น์ง๊ฐ Node.js์ ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ๊ณต๋ถํ๋ฉฐ ์ ๋ฆฌํ ๋ด์ฉ์ด๋ค. ๋น๋๊ธฐ I/O์ ๊ฐ๋ ฅํ ์ด์ ๋ ์์ ์ด๋ฒคํธ ๋ฃจํ๊ฐ ๊ธฐ๋ฐ์ ํ์คํ ์ก๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋์ Node.js ํน์ฑ์ I/O ์์ ๋ง ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๋ฏ๋ก CPU Boundํ ๋ก์ง์ ๋ํด์๋ ๋นํจ์จ์ ์ผ๋ก ๋์ํ๋ค.
๋ด๋ถ C/C++ ๊ตฌ์กฐ๊น์ง ํ๊ณ ๋ค๋ค ๋ณด๋ Node.js๊ฐ ์ ์ด๋ ๊ฒ ์ค๊ณ๋์๋์ง, ๊ทธ ์ฒ ํ์ ์ด์ ๋ฅผ ์ง์ ํ์ธํ ์ ์์๋ค. ๋จ์ํ "๋ ธ๋๋ ์ฑ๊ธ ์ค๋ ๋๋ค.", "๋น๋๊ธฐ๋ผ์ ๋น ๋ฅด๋ค"๋ ๋ง์ด libuv์ epoll, Thread Pool, ์ด๋ฒคํธ ๋ฃจํ ํ์ด์ฆ ์ค๊ณ ์ ๋ฐ์ ๊ฑธ์ณ ์ด๋ป๊ฒ ๊ตฌํ๋์ด ์๋์ง ์ดํดํ๊ณ ๋๋ ๋ ๋ช ํํ๊ฒ ๋ณด์ธ๋ค. ์ด์ ๋ ๊ฒฝํ์ผ๋ก ์ข ์ฑ์๋ณผ ์ฐจ๋ก์ธ ๊ฒ ๊ฐ๋ค.
'๊ฐ๋ฐ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| MySQL์์ ACID๋ฅผ ๋ณด์ฅํ๋ ๋ฐฉ๋ฒ (2) | 2026.05.05 |
|---|---|
| MySQL๊ณผ MongoDB ๊ณต๊ฐ ์ธ๋ฑ์ค์ ์ฐจ์ด (1) | 2026.04.06 |
| RPC(Remote Procedure Call)๋ ๋ฌด์์ผ๊น? (0) | 2026.03.20 |
| Fielding์ด ๋งํ๋ ์ง์ง REST API (0) | 2026.03.20 |
| 1๊ฐ์ ์ฌ๊ณ ๊ฐ ์์ ๋ ๋์์ 100๋ช ์ ์ฌ์ฉ์๊ฐ ์์ฒญํ๋ฉด? (2) | 2026.03.17 |