Node.js์˜ Reactor ํŒจํ„ด

Node.js์˜ Reactor ํŒจํ„ด์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ด…๋‹ˆ๋‹ค.

Jun 27, 2024

๊น€ํ˜„๊ธฐ

#NODE.JS#SERVER

๋“ค์–ด๊ฐ€๋ฉฐ

Node.js ๋””์ž์ธ ํŒจํ„ด ๋ฐ”์ด๋ธ” ์ด๋ผ๋Š” ์ฑ…์„ ์ฝ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Node.js๋ฅผ ๊นŠ์ด ์ดํ•ดํ•˜๊ณ  ์‹ถ์–ด ์ด ์ฑ…์„ ๊ตฌ์ž…ํ–ˆ์ง€๋งŒ, ํ•œ๋™์•ˆ ์ฝ์ง€ ๋ชปํ•˜๊ณ  ์žˆ์—ˆ๋Š”๋ฐ์š”

๋ธ”๋กœ๊ทธ๋ฅผ ํ†ตํ•ด ์ฑ… ์ดˆ๋ฐ˜๋ถ€์— ๋‚˜์˜ค๋Š” Node.js์˜ ๋ฆฌ์•กํ„ฐ ํŒจํ„ด์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด ๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค.

Node.js๋Š” ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”๊ฐ€?

I/O๋Š” ๋А๋ฆฌ๋‹ค.

์ฑ…์—์„œ I/O๋Š” ์ปดํ“จํ„ฐ์˜ ๊ธฐ๋ณธ์ ์ธ ๋™์ž‘๋“ค ์ค‘์—์„œ ๊ฐ€์žฅ ๋А๋ฆฌ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋А๋ฆฌ๋‹ค๋Š” ๊ฐœ๋…์ด ํ™• ์™€๋‹ฟ์ง€ ์•Š์•„ I/O์— ๋Œ€ํ•ด ์•Œ์•„๋ดค์Šต๋‹ˆ๋‹ค.

  1. ์ปดํ“จํ„ฐ์˜ I/O

์ปดํ“จํ„ฐ๊ฐ€ ์™ธ๋ถ€ ์„ธ๊ณ„์™€ ํ†ต์‹ ํ•˜๋Š” ๋ฐฉ์‹์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ปดํ“จํ„ฐ๋Š” ์ž…๋ ฅ ์žฅ์น˜๋ฅผ ํ†ตํ•ด ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ์ถœ๋ ฅ ์žฅ์น˜๋ฅผ ํ†ตํ•ด ์ •๋ณด๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

์ž…๋ ฅ ์žฅ์น˜๋Š” ํ‚ค๋ณด๋“œ, ๋งˆ์šฐ์Šค, ๋งˆ์ดํฌ, ์นด๋ฉ”๋ผ ๋“ฑ์ด ์žˆ์œผ๋ฉฐ, ์ถœ๋ ฅ ์žฅ์น˜๋Š” ๋ชจ๋‹ˆํ„ฐ, ํ”„๋ฆฐํ„ฐ, ์Šคํ”ผ์ปค ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ์žฅ์น˜๋“ค์€ CPU, RAM์— ๋น„ํ•ด ์†๋„๊ฐ€ ๋А๋ฆฐ๋ฐ, CPU์˜ ์†๋„๋Š” ๊ธฐ๊ฐ€ํ—ค๋ฅด์ธ (GHz) ๋‹จ์œ„๋กœ ์ธก์ •๋˜๊ณ , RAM์€ ๋‚˜๋…ธ์ดˆ(ns) ๋˜๋Š” ๋งˆ์ดํฌ๋กœ์ดˆ(ยตs)์ธ๋ฐ ๋ฐ˜ํ•ด,

I/O ์ž‘์—…์— ์‚ฌ์šฉ๋˜๋Š” ์ž…๋ ฅ, ์ถœ๋ ฅ ์žฅ์น˜, ํ•˜๋“œ ๋“œ๋ผ์ด๋ธŒ, ๋„คํŠธ์›Œํฌ ๋“ฑ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ MB/s ๋˜๋Š” GB/s ๋‹จ์œ„๋กœ ๋‹ค์–‘ํ•˜๊ฒŒ ์ธก์ •๋ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ์ฑ…์—์„œ๋Š” ์ธ๊ฐ„์ด๋ผ๋Š” ์š”์†Œ๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค๊ณ  ํ•˜๋Š”๋ฐ ์‚ฌ๋žŒ์ด ํ•˜๋Š” ๋งˆ์šฐ์Šค ํด๋ฆญ์ฒ˜๋Ÿผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ž…๋ ฅ์ด ์ผ์–ด๋‚˜๋Š” ๋งŽ์€ ์ƒํ™ฉ๋“ค์—์„œ I/O ์†๋„์™€ ๋นˆ๋„๋Š” ๊ธฐ์ˆ ์ ์ธ ์ธก๋ฉด์—๋งŒ ์˜์กดํ•˜์ง€ ์•Š์œผ๋ฉฐ ๋””์Šคํฌ๋‚˜ ๋„คํŠธ์›Œํฌ๋ณด๋‹ค ๋А๋ฆด ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

  1. Node.js์˜ I/O

Node.js์˜ I/O๋Š” ํŒŒ์ผ ์‹œ์Šคํ…œ, ๋„คํŠธ์›Œํฌ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, API ์š”์ฒญ ๋“ฑ๊ณผ์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค

์ด ๋™์ž‘๋“ค์€ ๋””์Šคํฌ, ๋„คํŠธ์›Œํฌ๋“ฑ์— ์ ‘๊ทผํ•ด์•ผ ํ•˜๋ฏ€๋กœ CPU, RAM์— ๋น„ํ•ด ์ ‘๊ทผํ•˜๋Š”๋ฐ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค.

๋ธ”๋กœํ‚น I/O

์ „ํ†ต์ ์ธ ๋ธ”๋กœํ‚น I/O ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ๋Š” I/O๋ฅผ ์š”์ฒญํ•˜๋Š” ํ•จ์ˆ˜์˜ ํ˜ธ์ถœ์€ ์ž‘์—…์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ์Šค๋ ˆ๋“œ์˜ ์‹คํ–‰์„ ์ฐจ๋‹จํ•ฉ๋‹ˆ๋‹ค. ์ฐจ๋‹จ ์‹œ๊ฐ„์€ ์œ„ ์„ค๋ช…ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋””์Šคํฌ ์ ‘๊ทผ, ๋„คํŠธ์›Œํฌ ์ ‘๊ทผ ๋“ฑ์˜ I/O ์ž‘์—…์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค์•ผ ํ•˜๋ฏ€๋กœ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ณ , ์‚ฌ์šฉ์ž ์•ก์…˜์— ์˜ํ•ด ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ๊ฒฝ์šฐ ๋ช‡ ๋ถ„๊นŒ์ง€ ์†Œ์š”๋˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ๋ธ”๋กœํ‚น I/O๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„๋œ ์›น ์„œ๋ฒ„๊ฐ€ ๊ฐ™์€ ์Šค๋ ˆ๋“œ ๋‚ด์—์„œ ์—ฌ๋Ÿฌ ์—ฐ๊ฒฐ์„ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ์€ ๋‹น์—ฐํ•œ ์ผ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ์ „ํ†ต์ ์ธ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์€ ๊ฐ๊ฐ์˜ ๋™์‹œ ์—ฐ๊ฒฐ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ ๊ฐœ๋ณ„์˜ ์Šค๋ ˆ๋“œ ๋˜๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.


๋‹ค์ค‘ ์ปค๋„ฅ์…˜์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ

์œ„ ์ด๋ฏธ์ง€์—์„œ ์ฃผ์˜ ๊นŠ๊ฒŒ ๋ณผ ๊ฒƒ์€ ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๊ธฐ ์œ„ํ•ด ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ์œ ํœด ์‹œ๊ฐ„์„ ๊ฐ–๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

I/O์˜ ์ž‘์—… ๊ฒฐ๊ณผ๋ฅผ ์œ„ํ•ด์„œ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฝค ๋งŽ์ด ๋ธ”๋กœํ‚น๋œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์Šค๋ ˆ๋“œ๋Š” ์ปค๋„ฅ์…˜ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์†Œ๋ชจํ•˜๊ณ  ์ปจํ…์ŠคํŠธ ์ „ํ™˜์„ ์œ ๋ฐœํ•˜์—ฌ ๋Œ€๋ถ€๋ถ„์˜ ์‹œ๊ฐ„ ๋™์•ˆ ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ ์žฅ์‹œ๊ฐ„ ์‹คํ–‰ ์Šค๋ ˆ๋“œ๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋จ์œผ๋กœ์จ ๋ฉ”๋ชจ๋ฆฌ์™€ CPU ์‚ฌ์ดํด์„ ๋‚ญ๋น„ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋…ผ ๋ธ”๋กœํ‚น I/O

๋Œ€๋ถ€๋ถ„์˜ ์ตœ์‹  ์šด์˜์ฒด์ œ๋Š” ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด ๋ธ”๋กœํ‚น I/O ์™ธ์—๋„ ๋…ผ ๋ธ”๋กœํ‚น I/O๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ๋‹ค๋ฅธ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ด ์šด์˜ ๋ชจ๋“œ์—์„œ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์ฝํ˜€์ง€๊ฑฐ๋‚˜ ์“ฐ์—ฌ์ง€๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ํ•ญ์ƒ ์ฆ‰์‹œ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ข…๋ฅ˜์˜ ๋…ผ ๋ธ”๋กœํ‚น I/O๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ํŒจํ„ด์€ ์‹ค์ œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ˜ํ™˜๋  ๋•Œ๊นŒ์ง€ ๋ฃจํ”„ ๋‚ด์—์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ์ ๊ทน์ ์œผ๋กœ ํด๋ง(poll)ํ•˜๋Š” ๊ฒƒ์ด๋ผ๊ณ  ํ•˜๋Š”๋ฐ์š” ์ด๊ฒƒ์„ ๋ฐ”์œ ๋Œ€๊ธฐ(busy-waiting)์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ฑ…์— ์˜ˆ์ œ ์ฝ”๋“œ๊ฐ€ ๋‚˜์˜ค๋Š”๋ฐ ์ด ์ฝ”๋“œ๋ฅผ ๋ณด์‹œ๋ฉด ๋ฐ”์œ ๋Œ€๊ธฐ๊ฐ€ ์–ด๋–ค ๋œป์ธ์ง€ ์ดํ•ดํ•˜์‹ค ์ˆ˜ ์žˆ์„๊ฒ๋‹ˆ๋‹ค.

resources = [socketA, socketB, fileA]
while (!resources.isEmpty()) {
  for (resource of resources) {
    // ์ฝ๊ธฐ๋ฅผ ์‹œ๋„
    data = resource.read()
    if (data === NO_DATA_AVAILABLE) {
      // ์ด ์ˆœ๊ฐ„์—๋Š” ์ฝ์„ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Œ
      continue
    }
    if (data === RESOURCE_CLOSED) {
      // ๋ฆฌ์†Œ์Šค๊ฐ€ ๋‹ซํžˆ๊ณ  ๋ฆฌ์ŠคํŠธ์—์„œ ์‚ญ์ œ
      resources.remove(i)
    } else {
      // ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๊ณ  ์ฒ˜๋ฆฌ
      consumeData(data)
    }
  }
}

์ฝ”๋“œ๋ฅผ ๋ณด์‹œ๋ฉด while์˜ ์กฐ๊ฑด์œผ๋กœ resources๊ฐ€ ๋น„์–ด ์žˆ์ง€ ์•Š์„๋•Œ๊นŒ์ง€ for ๋ฃจํ”„๋ฅผ ๊ณ„์† ๋Œ๋ฉด์„œ ๋ฆฌ์†Œ์Šค๋ฅผ ํด๋งํ•˜๊ณ  ์žˆ๋Š”๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณด๋‹ค์‹œํ”ผ ๊ฐ„๋‹จํ•œ ๊ธฐ๋ฒ•์œผ๋กœ ์„œ๋กœ ๋‹ค๋ฅธ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ฐ™์€ ์Šค๋ ˆ๋“œ ๋‚ด์—์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์—ฌ์ „ํžˆ ํšจ์œจ์ ์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์œ„ ์˜ˆ์ œ์—์„œ ๋ฃจํ”„๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๋ฆฌ์†Œ์Šค๋ฅผ ๋ฐ˜๋ณตํ•˜๋Š” ๋ฐ์— ์†Œ์ค‘ํ•œ CPU๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ํด๋ง ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ์—„์ฒญ๋‚œ ์‹œ๊ฐ„์˜ ๋‚ญ๋น„๋ฅผ ์ดˆ๋ž˜ํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์‹ฑ

๋ฐ”์œ ๋Œ€๊ธฐ(Busy-waiting)๋Š” ๋…ผ ๋ธ”๋กœํ‚น ๋ฆฌ์†Œ์Šค ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ์ด์ƒ์ ์ธ ๊ธฐ๋ฒ•์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋‹คํ–‰ํžˆ๋„, ๋Œ€๋ถ€๋ถ„์˜ ์šด์˜์ฒด์ œ๋Š” ๋…ผ ๋ธ”๋กœํ‚น ๋ฆฌ์†Œ์Šค๋ฅผ ํšจ์œจ์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋ณธ์ ์ธ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ด ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๋™๊ธฐ ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ(Synchronous Event Demultiplexer) ๋˜๋Š” ์ด๋ฒคํŠธ ํ†ต์ง€ ์ธํ„ฐํŽ˜์ด์Šค(Event Notification Interface) ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ฑ…์—์„œ๋„ ์„ค๋ช…ํ•˜์ง€๋งŒ ์ด ์šฉ์–ด๊ฐ€ ์ต์ˆ™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฉ€ํ‹ฐ ํ”Œ๋ ‰์‹ฑ๊ณผ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์‹ฑ์ด ๋ญ”์ง€ ์ฐพ์•„๋ดค์Šต๋‹ˆ๋‹ค.

  1. ๋ฉ€ํ‹ฐ ํ”Œ๋ ‰์‹ฑ(Multiplexing)

์ œ๊ฐ€ ์ œ๋ชฉ์— ๋งํฌํ•œ ์œ„ํ‚ค์™€ ์ฑ…์—์„œ ๋ฉ€ํ‹ฐ ํ”Œ๋ ‰์‹ฑ์€ ํ†ต์‹ ๋ถ„์•ผ, ์ „๊ธฐํ†ต์‹ ๋ถ„์•ผ, ๋„คํŠธ์›Œํฌ๋“ฑ ์—ฌ๋Ÿฌ๋ถ„์•ผ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฐœ๋…์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์š”์•ฝํ•ด๋ณด์ž๋ฉด ํ•˜๋‚˜์˜ ์ž์›์„ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ž‘์—…์ด๋‚˜ ๋ฐ์ดํ„ฐ์— ๋™์‹œ์— ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜๋Š” ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค. ์‰ฝ๊ฒŒ ๋งํ•ด, ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ฑ„๋„์„ ํ•˜๋‚˜์˜ ์ฑ„๋„๋กœ ํ•ฉ์ณ์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

  1. ๋””๋ฉ€ํ‹ฐ ํ”Œ๋ ‰์‹ฑ(Demultiplexing)

๋””๋ฉ€ํ‹ฐ ํ”Œ๋ ‰์‹ฑ์€ ๋ฉ€ํ‹ฐ ํ”Œ๋ ‰์‹ฑ์˜ ๋ฐ˜๋Œ€ ๊ฐœ๋…์ด๊ฒ ์ฃ ? ์›๋ž˜์˜ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ๋‹ค์‹œ ๋ถ„ํ• ๋˜๋Š” ์ž‘์—…์ž…๋‹ˆ๋‹ค.

๋‹ค์‹œ ๋Œ์•„์™€์„œ ๋™๊ธฐ ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ๋ฅผ ์ฑ…์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค๋ช…ํ•˜๋ฉฐ ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

๋™๊ธฐ ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ๋Š” ์—ฌ๋Ÿฌ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ด€์ฐฐํ•˜๊ณ  ์ด ๋ฆฌ์†Œ์Šค๋“ค ์ค‘์— ์ฝ๊ธฐ ๋˜๋Š” ์“ฐ๊ธฐ ์—ฐ๊ฐ„์˜ ์‹คํ–‰์ด ์™„๋ฃŒ๋˜์—ˆ์„๋•Œ ์ƒˆ๋กœ์šด ์ด๋ฒคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ฐพ์„ ์ˆ˜ ์žˆ๋Š” ์ด์ ์€ ๋™๊ธฐ ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ๊ฐ€ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ์ƒˆ๋กœ์šด ์ด๋ฒคํŠธ๊ฐ€ ์žˆ์„ ๋•Œ๊นŒ์ง€ ๋ธ”๋กœํ‚น๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.


while (events = demultiplexer.watch(watchedList)) { // 2
  // ์ด๋ฒคํŠธ ๋ฃจํ”„
  for (event of events) { // 3
    // ๋ธ”๋กœํ‚นํ•˜์ง€ ์•Š์œผ๋ฉฐ ํ•ญ์ƒ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜
    data = event.resource.read()
    if (data === RESOURCE_CLOSED) {
      // ๋ฆฌ์†Œ์Šค๊ฐ€ ๋‹ซํžˆ๊ณ  ๊ด€์ฐฐ๋˜๋Š” ๋ฆฌ์ŠคํŠธ์—์„œ ์‚ญ์ œ
      demultiplexer.unwatch(event.resource)
    } else {
      // ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์œผ๋ฉด ์ฒ˜๋ฆฌ
      consumeData(data)
    }
  }
}

์•„๊นŒ ์ฝ”๋“œ์™€ ๋‹ค๋ฅธ์ ์ด ๋ณด์ด์‹œ๋‚˜์š”? while์˜ ์กฐ๊ฑด์ด ๋ฐ”๋€Œ๊ณ , ์ฝ์„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฃจํ”„๋ฅผ ๋Œ๋ฉฐ(continue๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ) ๊ณ„์†ํ•ด์„œ ์ฒดํฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ฑ…์—์„œ ์ด ์ฝ”๋“œ์— ๋Œ€ํ•œ ์„ค๋ช…์„ ํ•ฉ๋‹ˆ๋‹ค.

  1. ๊ฐ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ(List)์— ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค. ๊ฐ ๋ฆฌ์†Œ์Šค๋ฅผ ํŠน์ • ์—ฐ์‚ฐ๊ณผ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

  2. ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ๊ฐ€ ๊ด€์ฐฐ๋  ๋ฆฌ์†Œ์Šค ๊ทธ๋ฃน๊ณผ ํ•จ๊ป˜ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. demultiplexer.watch()๋Š” ๋™๊ธฐ์‹์œผ๋กœ ๊ด€์ฐฐ๋˜๋Š” ๋ฆฌ์†Œ์Šค๋“ค ์ค‘์—์„œ ์ฝ์„ ์ค€๋น„๊ฐ€ ๋œ ๋ฆฌ์†Œ์Šค๊ฐ€ ์žˆ์„ ๋•Œ๊นŒ์ง€ ๋ธ”๋กœํ‚น๋ฉ๋‹ˆ๋‹ค. ์ค€๋น„๋œ ๋ฆฌ์†Œ์Šค๊ฐ€ ์ƒ๊ธฐ๋ฉด, ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ๋Š” ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ์ƒˆ๋กœ์šด ์ด๋ฒคํŠธ ์„ธํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

  3. ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ์—์„œ ๋ฐ˜ํ™˜๋œ ๊ฐ ์ด๋ฒคํŠธ๊ฐ€ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ์ด ์‹œ์ ์—์„œ ๊ฐ ์ด๋ฒคํŠธ์™€ ๊ด€๋ จ๋œ ๋ฆฌ์†Œ์Šค๋Š” ์ฝ์„ ์ค€๋น„ ๋ฐ ์ฐจ๋‹จ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์ด๋ฒคํŠธ๊ฐ€ ์ฒ˜๋ฆฌ๋˜๊ณ  ๋‚˜๋ฉด, ์ด ํ๋ฆ„์€ ๋‹ค์‹œ ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ๊ฐ€ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅํ•œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ์ „๊นŒ์ง€ ๋ธ”๋กœํ‚น๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์ด๋ฒคํŠธ ๋ฃจํ”„(event loop)๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ํฅ๋ฏธ๋กœ์šด ์ ์€ ์šฐ๋ฆฌ๊ฐ€ ์ด ํŒจํ„ด์„ ์ด์šฉํ•˜๋ฉด ๋ฐ”์œ ๋Œ€๊ธฐ(Busy-waiting) ๊ธฐ์ˆ ์„ ์ด์šฉํ•˜์ง€ ์•Š๊ณ ๋„ ์—ฌ๋Ÿฌ I/O ์ž‘์—…์„ ๋‹จ์ผ ์Šค๋ ˆ๋“œ ๋‚ด์—์„œ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋กœ์จ ์šฐ๋ฆฌ๊ฐ€ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์‹ฑ์— ๋Œ€ํ•ด ๋…ผํ•˜๋Š” ์ด์œ ๊ฐ€ ๋ช…ํ™•ํ•ด์กŒ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜ ๊ทธ๋ฆผ์—์„œ ๋ณด์—ฌ์ฃผ๋“ฏ์ด ์˜ค์ง ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋งŒ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋™์‹œ์  ๋‹ค์ค‘ I/O ์‚ฌ์šฉ ์ž‘์—…์— ๋‚˜์œ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.


๋‹ค์ค‘ ์ปค๋„ฅ์…˜์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋‹จ์ผ ์Šค๋ ˆ๋“œ

๊ทธ๋ฆฌ๊ณ  ์ฑ…์—์„œ๋Š” ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋ฅผ ๊ฐ€์ง„ I/O ๋ชจ๋ธ์ด ๊ฐ€์ง€๋Š” ์žฅ์ ์œผ๋กœ ๋™์‹œ์„ฑ์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ์‹์— ์ด๋กœ์šด ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•˜๋ฉฐ ์ฑ… ํ›„๋ฐ˜๋ถ€์— ์„ค๋ช…ํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.(์ถ”ํ›„ ํฌ์ŠคํŒ…์„ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.)

Reactor ํŒจํ„ด

์ด์ œ ๋“œ๋””์–ด Reactor ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์ฑ… ๋‚ด์šฉ์„ ๋ณผ๊นŒ์š”?

๋ฆฌ์•กํ„ฐ ํŒจํ„ด์˜ ์ด๋ฉด์— ์žˆ๋Š” ์ฃผ๋œ ์•„์ด๋””์–ด๋Š” ๊ฐ I/O ์ž‘์—…์— ์—ฐ๊ด€๋œ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ฐ–๋Š”๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. Node.js์—์„œ์˜ ํ•ธ๋“ค๋Ÿฌ๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•ธ๋“ค๋Ÿฌ๋Š” ์ด๋ฒคํŠธ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  ์ด๋ฒคํŠธ ๋ฃจํ”„์— ์˜ํ•ด ์ฒ˜๋ฆฌ๋˜๋Š” ์ฆ‰์‹œ ํ˜ธ์ถœ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋ฆฌ์•กํ„ฐ ํŒจํ„ด์˜ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.


Reactor ํŒจํ„ด
  1. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ์— ์š”์ฒญ์„ ์ „๋‹ฌํ•จ์œผ๋กœ์จ ์ƒˆ๋กœ์šด I/O ์ž‘์—…์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ž‘์—…์ด ์™„๋ฃŒ๋˜์—ˆ์„ ๋•Œ ํ˜ธ์ถœ๋  ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ์— ์ƒˆ ์š”์ฒญ์„ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์€ ๋…ผ ๋ธ”๋กœํ‚น ํ˜ธ์ถœ์ด๋ฉฐ, ์ œ์–ด๊ถŒ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ์ฆ‰์‹œ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

  2. ์ผ๋ จ์˜ I/O ์ž‘์—…๋“ค์ด ์™„๋ฃŒ๋˜๋ฉด ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ๋Š” ๋Œ€์‘ํ•˜๋Š” ์ด๋ฒคํŠธ ์ž‘์—…๋“ค์„ ์ด๋ฒคํŠธ ํ์— ์ง‘์–ด ๋„ฃ์Šต๋‹ˆ๋‹ค.

  3. ์ด ์‹œ์ ์—์„œ ์ด๋ฒคํŠธ ๋ฃจํ”„๊ฐ€ ์ด๋ฒคํŠธ ํ์˜ ํ•ญ๋ชฉ๋“ค์„ ์ˆœํ™˜ํ•ฉ๋‹ˆ๋‹ค.

  4. ๊ฐ ์ด๋ฒคํŠธ์™€ ๊ด€๋ จ๋œ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

  5. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์˜ ์ผ๋ถ€์ธ ํ•ธ๋“ค๋Ÿฌ์˜ ์‹คํ–‰์ด ์™„๋ฃŒ๋˜๋ฉด ์ œ์–ด๊ถŒ์„ ์ด๋ฒคํŠธ ๋ฃจํ”„์— ๋˜๋Œ๋ ค์ค๋‹ˆ๋‹ค(5a). ํ•ธ๋“ค๋Ÿฌ ์‹คํ–‰ ์ค‘์— ๋‹ค๋ฅธ ๋น„๋™๊ธฐ ์ž‘์—…์„ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ(5b), ์ด๋Š” ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ์— ์ƒˆ๋กœ์šด ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.(1)

์•ž์˜ ๋ธ”๋กœํ‚น I/O, ๋…ผ ๋ธ”๋กœํ‚น I/O, ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ์ „๋ถ€ ์ดํ•ดํ•˜์…จ๊ณ  ๊ทธ๋ฆผ์„ ๋ณด์‹ ๋‹ค๋ฉด Reactor ํŒจํ„ด์„ ์ž˜ ์ดํ•ดํ•˜์‹ค ์ˆ˜ ์žˆ์„๊ฒ๋‹ˆ๋‹ค.

Reactor ํŒจํ„ด์„ ์ดํ•ดํ•  ๋•Œ ์œ ์˜ํ•  ์ 

์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๋ฐฉ์‹(์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ)์ด ๋™๊ธฐ๋ƒ ๋น„๋™๊ธฐ๋ƒ์˜ ์ฐจ์ด๊ฐ€ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—

์ฑ…์—์„œ ์„ค๋ช…ํ•œ ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์‹ฑ๊ณผ ์—ฐ๊ฒฐ์ง€์–ด ReactorํŒจํ„ด์„ ์ดํ•ดํ•  ๋•Œ ์œ ์˜ํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.


๊ตฌ๋ถ„ ๋™๊ธฐ ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์‹ฑ ๋ฆฌ์•กํ„ฐ ํŒจํ„ด
์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๋ฐฉ์‹ ์ˆœ์ฐจ์  ์ฒ˜๋ฆฌ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ
์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์‹คํ–‰ ๋ฐฉ์‹ ์ด๋ฒคํŠธ ๋ฃจํ”„์— ์˜ํ•ด ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰ ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ ๋“ฑ๋ก๋œ ํ•ธ๋“ค๋Ÿฌ ์‹คํ–‰
์žฅ์  ๊ตฌํ˜„์ด ๋น„๊ต์  ๊ฐ„๋‹จ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋กœ ์ธํ•œ ๋†’์€ ์„ฑ๋Šฅ
๋‹จ์  ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ์ˆœ์„œ ๋ณ€๊ฒฝ ์–ด๋ ค์›€ ํ•ธ๋“ค๋Ÿฌ ๊ด€๋ฆฌ ๋ณต์žก์„ฑ ์ฆ๊ฐ€

Libuv, Node.js์˜ I/O ์—”์ง„

๋งˆ์ง€๋ง‰์œผ๋กœ ์ด๋Ÿฐ I/O ์—”์ง„์— ๋Œ€ํ•œ ๊ฐ OS์˜ ์ฐจ์ด๋ฅผ ๊ทน๋ณตํ•˜๊ธฐ ์œ„ํ•ด Node.js๋Š” Libuv๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

๊ฐ ์šด์˜์ฒด์ œ๋Š” Linux์˜ epoll, macOs์˜ kqueue, Window์˜ IOCP(I/O completion port) API์™€ ๊ฐ™์€ ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ๋ฅผ ์œ„ํ•œ ์ž์ฒด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ๊ฐ I/O ์ž‘์—…์€ ๋™์ผํ•œ OS ๋‚ด์—์„œ๋„ ๋ฆฌ์†Œ์Šค ์œ ํ˜•์— ๋”ฐ๋ผ ๋งค์šฐ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด Unix์—์„œ ์ผ๋ฐ˜ ํŒŒ์ผ ์‹œ์Šคํ…œ์€ ๋…ผ ๋ธ”๋กœํ‚น ์ž‘์—…์„ ์ง€์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋…ผ ๋ธ”๋กœํ‚น ๋™์ž‘์„ ์œ„ํ•ด์„œ๋Š” ์ด๋ฒคํŠธ ๋ฃจํ”„ ์™ธ๋ถ€์— ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋กœ ๋‹ค๋ฅธ ์šด์˜์ฒด์ œ ๊ฐ„์˜ ๋ถˆ์ผ์น˜์„ฑ์€ ์ด๋ฒคํŠธ ๋””๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ๋ฅผ ์œ„ํ•ด ๋ณด๋‹ค ๋†’์€ ๋ ˆ๋ฒจ์˜ ์ถ”์ƒํ™”๋ฅผ ํ•„์š”๋กœ ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ์ด์œ ๋กœ Node.js ์ฝ”์–ดํŒ€์ด Node.js๋ฅผ ์ฃผ์š” ์šด์˜์ฒด์ œ์—์„œ ํ˜ธํ™˜๋˜๊ฒŒ ํ•ด์ฃผ๋ฉฐ ์„œ๋กœ ๋‹ค๋ฅธ ๋ฆฌ์†Œ์Šค ์œ ํ˜•์˜ ๋…ผ ๋ธ”๋กœํ‚น ๋™์ž‘์„ ํ‘œ์ค€ํ™”ํ•˜๊ธฐ ์œ„ํ•ด libuv๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” C ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. Libuv๋Š” Node.js์˜ ํ•˜์œ„ ์ˆ˜์ค€์˜ I/O ์—”์ง„์„ ๋Œ€ํ‘œํ•˜๋ฉฐ ์•„๋งˆ๋„ Node.js์˜ ๊ตฌ์„ฑ์š”์†Œ ์ค‘์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•˜๋‹ค๊ณ  ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Libuv๋Š” ๊ธฐ๋ณธ ์‹œ์Šคํ…œ ํ˜ธ์ถœ์„ ์ถ”์ƒํ™”ํ•˜๋Š” ๊ฒƒ ์™ธ์—๋„ ๋ฆฌ์•กํ„ฐ ํŒจํ„ด์„ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์ด๋ฒคํŠธ ๋ฃจํ”„์˜ ์ƒ์„ฑ, ์ด๋ฒคํŠธ ํ์˜ ๊ด€๋ฆฌ, ๋น„๋™๊ธฐ I/O ์ž‘์—…์˜ ์‹คํ–‰ ๋ฐ ๋‹ค๋ฅธ ์œ ํ˜•์˜ ์ž‘์—…์„ ํ์— ๋‹ด๊ธฐ ์œ„ํ•œ API๋“ค์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๋งˆ์น˜๋ฉฐ

์‚ฌ์‹ค ์ €๋Š” ์ด ์ฑ…์˜ ์ดˆํŒ์„ 2020๋…„์— ๊ตฌ์ž…ํ•ด์„œ Reactor ํŒจํ„ด์— ๋Œ€ํ•ด ์ฝ์—ˆ์ง€๋งŒ ์ดํ•ด๊ฐ€ ์ž˜๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

4๋…„์ด๋ž€ ์‹œ๊ฐ„์ด ํ˜๋Ÿฌ ๋‹ค์‹œ ๋ณด๋‹ˆ ์‚ฌ๋ญ‡, ๊ทธ๋•Œ์™€๋Š” ๋‹ค๋ฅธ ๋А๋‚Œ์œผ๋กœ ๋‹ค๊ฐ€์˜ค๋„ค์š”.

์ €์™€ ๊ฐ™์€ ๊ฒฝํ—˜์„ ํ•˜์‹œ๋Š” ๋ถ„๋“ค์ด ๋งŽ์œผ์‹ค ๊ฑฐ๋ผ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ถ”์ฒœ์„ ๋ฐ›์•„์„œ ์ƒ€๊ฑฐ๋‚˜ ์„ ๋ฌผ ๋ฐ›์€ ์ฑ…์„ ์ฝ๋‹ค๊ฐ€ โ€œ์•…! ๋„๋Œ€์ฒด ๋ฌด์Šจ ๋ง์ธ์ง€ ํ•˜๋‚˜๋„ ๋ชจ๋ฅด๊ฒ ์–ด!โ€ ํ•˜๊ณ  ๋ฎ์–ด๋’€๋Š”๋ฐ, ์‹œ๊ฐ„์ด ์ง€๋‚˜ ๋‹ค์‹œ ์ฝ์–ด๋ณด๋ฉด ์ดํ•ด๊ฐ€ ๋˜๋Š” ๊ฒฝ์šฐ ๋ง์ด์ฃ .

์ด๋Ÿฐ ๊ฒฝํ—˜์ด ๋‚ด๊ฐ€ ์„ฑ์žฅํ–ˆ๊ณ  ๋ฐœ์ „ํ–ˆ๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•ด ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ธฐํšŒ๊ฐ€ ์•„๋‹๊นŒ ํ•ฉ๋‹ˆ๋‹ค.

์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋ฌธ์„œ ๋ฐ ์ด๋ฏธ์ง€ ์ถœ์ฒ˜

Grow & Glow ยฉ 2025

Banner images by undraw.co