Skip to main content

๐Ÿš€ async/await ๋งˆ์Šคํ„ฐํ•˜๊ธฐ

์ด ๋ฌธ์„œ๋Š” Node.js ํ™˜๊ฒฝ์—์„œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ Promise์™€ async/await์˜ ํ•ต์‹ฌ ๋™์ž‘ ๋ฐฉ์‹๊ณผ ๋™๊ธฐ ๋ฐฉ์‹๊ณผ์˜ ๊ทผ๋ณธ์ ์ธ ์ฐจ์ด์ ์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ๋…ผ๋ธ”๋กœํ‚น(Non-Blocking) I/O์˜ ์›๋ฆฌ๋ฅผ ๊นŠ์ด ์žˆ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

1. ๋™๊ธฐ(Synchronous) ์ฒ˜๋ฆฌ ๋ฐฉ์‹: ๋ธ”๋กœํ‚น (Blocking)โ€‹

๊ฐœ๋…: ์ฝ”๋“œ๊ฐ€ ์ž‘์„ฑ๋œ ์ˆœ์„œ๋Œ€๋กœ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์ž‘์—…๋งŒ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ํŠน์ • ์ž‘์—…(ํŠนํžˆ ํŒŒ์ผ ์ฝ๊ธฐ/์“ฐ๊ธฐ, ๋„คํŠธ์›Œํฌ ์š”์ฒญ ๋“ฑ I/O ์ž‘์—…)์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋‹ค์Œ ์ฝ”๋“œ์˜ ์‹คํ–‰์ด ์ค‘๋‹จ๋˜๋Š” ๋ธ”๋กœํ‚น(Blocking) ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

  • Node.js ์˜ˆ์‹œ: fs.readFileSync, fs.writeFileSync ๋“ฑ ํ•จ์ˆ˜๋ช… ๋์— Sync๊ฐ€ ๋ถ™์€ ํ•จ์ˆ˜๋“ค์ด ๋Œ€ํ‘œ์ ์ž…๋‹ˆ๋‹ค.

๋™์ž‘ ์›๋ฆฌ:

  • ๋™๊ธฐ ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด, Node.js์˜ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ํ•ด๋‹น ์ž‘์—…์ด ์™„์ „ํžˆ ๋๋‚  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ ์ƒํƒœ์— ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค.
  • ์ด ์‹œ๊ฐ„ ๋™์•ˆ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ธ”๋กœํ‚น๋˜์–ด ์ด๋ฒคํŠธ ๋ฃจํ”„๋Š” ๋‹ค๋ฅธ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์œผ๋ฉฐ, setTimeout, setInterval์˜ ์ฝœ๋ฐฑ ์‹คํ–‰, ๋„คํŠธ์›Œํฌ ์‘๋‹ต ์ฒ˜๋ฆฌ ๋“ฑ์ด ๋ชจ๋‘ ์ง€์—ฐ๋ฉ๋‹ˆ๋‹ค.
danger

๋™์‹œ์— ์—ฌ๋Ÿฌ ์š”์ฒญ์„ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ์„œ๋ฒ„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ™˜๊ฒฝ์—์„œ ๋™๊ธฐ ๋ธ”๋กœํ‚น ๋ฐฉ์‹์€ ์‹ฌ๊ฐํ•œ ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹จ์ผ ์š”์ฒญ ์ฒ˜๋ฆฌ ์ค‘ ๋‹ค๋ฅธ ๋ชจ๋“  ์š”์ฒญ์˜ ์‘๋‹ต์ด ์ง€์—ฐ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์˜ˆ์‹œ ์ฝ”๋“œ:

const fs = require('fs');

console.log("์ž‘์—… ์‹œ์ž‘");
// ๋‹ค์Œ ๋ผ์ธ์—์„œ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ํŒŒ์ผ ์ฝ๊ธฐ ์ž‘์—… ๋™์•ˆ ๋ฉˆ์ถฅ๋‹ˆ๋‹ค.
try {
const data = fs.readFileSync('large-file.txt', 'utf8');
console.log("ํŒŒ์ผ ์ฝ๊ธฐ ์™„๋ฃŒ:", data.substring(0, 50) + "..."); // ์œ„์˜ ์ž‘์—…์ด ๋๋‚˜์•ผ ์‹คํ–‰๋จ
} catch (err) {
console.error("ํŒŒ์ผ ์ฝ๊ธฐ ์ค‘ ์˜ค๋ฅ˜:", err);
}
console.log("๋‹ค๋ฅธ ์ž‘์—… ์‹œ์ž‘"); // ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋จ

2. ๋น„๋™๊ธฐ(Asynchronous) ์ฒ˜๋ฆฌ ๋ฐฉ์‹: ๋…ผ๋ธ”๋กœํ‚น (Non-Blocking)โ€‹

๊ฐœ๋…: ํŠน์ • ์ž‘์—…(ํŠนํžˆ I/O)์„ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์ˆ˜ํ–‰ํ•˜๋„๋ก ์š”์ฒญํ•œ ํ›„, ๊ทธ ์ž‘์—…์˜ ์™„๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ์ฆ‰์‹œ ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ž‘์—…์ด ์™„๋ฃŒ๋˜๋ฉด ๋‚˜์ค‘์— ์ฝœ๋ฐฑ ํ•จ์ˆ˜, Promise ๋“ฑ์„ ํ†ตํ•ด ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋…ผ๋ธ”๋กœํ‚น(Non-Blocking) ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

  • Node.js ๊ธฐ๋ฐ˜ ๊ธฐ์ˆ : ์ด๋ฒคํŠธ ๋ฃจํ”„(Event Loop), ์ฝœ๋ฐฑ ํ•จ์ˆ˜(Callback Functions), ํ”„๋กœ๋ฏธ์Šค(Promise) ๊ฐ์ฒด

๋™์ž‘ ์›๋ฆฌ:

  • ๋น„๋™๊ธฐ ํ•จ์ˆ˜(์˜ˆ: fs.readFile)๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด, Node.js๋Š” ํ•ด๋‹น ์ž‘์—…์„ ์šด์˜์ฒด์ œ(๋˜๋Š” libuv์˜ ์Šค๋ ˆ๋“œ ํ’€)์— ์œ„์ž„ํ•˜๊ณ  ์ฆ‰์‹œ ๋‹ค์Œ ์ฝ”๋“œ๋กœ ๋„˜์–ด๊ฐ‘๋‹ˆ๋‹ค.
  • ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ๋ฉˆ์ถ”์ง€ ์•Š๊ณ  ๊ณ„์†ํ•ด์„œ ๋‹ค๋ฅธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜, ์ด๋ฒคํŠธ ๋ฃจํ”„๋ฅผ ํ†ตํ•ด ๋‹ค๋ฅธ ์ด๋ฒคํŠธ(์˜ˆ: ํƒ€์ด๋จธ, ๋„คํŠธ์›Œํฌ ์š”์ฒญ)๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…์ด ์™„๋ฃŒ๋˜๋ฉด, ๋“ฑ๋ก๋œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋‚˜ Promise์˜ ํ›„์† ์ฒ˜๋ฆฌ ๋กœ์ง( .then ๋˜๋Š” await ๋‹ค์Œ ์ฝ”๋“œ)์ด ์ด๋ฒคํŠธ ํ(Event Queue)์— ์ถ”๊ฐ€๋˜๊ณ , ์ด๋ฒคํŠธ ๋ฃจํ”„์— ์˜ํ•ด ์ ์ ˆํ•œ ์‹œ์ ์— ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
tip

I/O ์ž‘์—…์œผ๋กœ ์ธํ•ด ๋Œ€๊ธฐํ•˜๋Š” ์‹œ๊ฐ„ ๋™์•ˆ์—๋„ ๋‹ค๋ฅธ ์š”์ฒญ์ด๋‚˜ ์ž‘์—…์„ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด, ๋†’์€ ์ฒ˜๋ฆฌ๋Ÿ‰(Throughput)๊ณผ ๋น ๋ฅธ ์‘๋‹ต์„ฑ(Responsiveness)์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

3. Promise๋ž€?โ€‹

๊ฐœ๋…: ๋น„๋™๊ธฐ ์ž‘์—…์˜ ์ตœ์ข… ์™„๋ฃŒ(fulfillment) ๋˜๋Š” ์‹คํŒจ(rejection) ์ƒํƒœ์™€ ๊ทธ ๊ฒฐ๊ณผ ๊ฐ’์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. ์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ ์ค‘์ฒฉ์œผ๋กœ ๋ฐœ์ƒํ•˜๋Š” "์ฝœ๋ฐฑ ์ง€์˜ฅ(Callback Hell)" ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ , ๋น„๋™๊ธฐ ์ฝ”๋“œ๋ฅผ ๋”์šฑ ์ฒด๊ณ„์ ์ด๊ณ  ๊ฐ€๋…์„ฑ ์žˆ๊ฒŒ ์ž‘์„ฑํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค.

์ƒํƒœ (States): Promise๋Š” ๋‹ค์Œ ์„ธ ๊ฐ€์ง€ ์ƒํƒœ ์ค‘ ํ•˜๋‚˜๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

  • pending: ์ดˆ๊ธฐ ์ƒํƒœ. ๋น„๋™๊ธฐ ์ž‘์—…์ด ์•„์ง ์™„๋ฃŒ๋˜๊ฑฐ๋‚˜ ์‹คํŒจํ•˜์ง€ ์•Š์€ ์ƒํƒœ.
  • fulfilled (or resolved): ๋น„๋™๊ธฐ ์ž‘์—…์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋œ ์ƒํƒœ. ๊ฒฐ๊ณผ ๊ฐ’์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.
  • rejected: ๋น„๋™๊ธฐ ์ž‘์—…์ด ์‹คํŒจํ•œ ์ƒํƒœ. ์‹คํŒจ ์ด์œ (์˜ค๋ฅ˜ ๊ฐ์ฒด)๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

์ฃผ์š” ๋ฉ”์†Œ๋“œ:

  • .then(onFulfilled, onRejected): Promise๊ฐ€ fulfilled ๋˜๋Š” rejected ์ƒํƒœ๊ฐ€ ๋˜์—ˆ์„ ๋•Œ ํ˜ธ์ถœ๋  ํ•จ์ˆ˜๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด Promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์ฒด์ด๋‹(chaining)์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • .catch(onRejected): Promise๊ฐ€ rejected ์ƒํƒœ๊ฐ€ ๋˜์—ˆ์„ ๋•Œ๋งŒ ํ˜ธ์ถœ๋  ํ•จ์ˆ˜๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. then(null, onRejected)์™€ ์œ ์‚ฌํ•˜๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.
  • .finally(onFinally): Promise์˜ ์„ฑ๊ณต/์‹คํŒจ ์—ฌ๋ถ€์™€ ๊ด€๊ณ„์—†์ด ์ž‘์—… ์™„๋ฃŒ ํ›„ ํ•ญ์ƒ ์‹คํ–‰๋  ํ•จ์ˆ˜๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

4. async/await๋ž€?โ€‹

๊ฐœ๋…: Promise๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•˜๋Š” **๋ฌธ๋ฒ•์  ์„คํƒ•(Syntactic Sugar)**์ž…๋‹ˆ๋‹ค. ๋น„๋™๊ธฐ ์ฝ”๋“œ๋ฅผ ๋งˆ์น˜ ๋™๊ธฐ ์ฝ”๋“œ์ฒ˜๋Ÿผ ๋”์šฑ ์ง๊ด€์ ์ด๊ณ  ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค. async/await๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Promise ์ฒด์ด๋‹๋ณด๋‹ค ๊ฐ€๋…์„ฑ์ด ๋†’์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

async ํ•จ์ˆ˜ (async function)โ€‹

  • ํ•จ์ˆ˜ ์„ ์–ธ ์‹œ async ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ํ•ด๋‹น ํ•จ์ˆ˜๋Š” ํ•ญ์ƒ Promise๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • ํ•จ์ˆ˜ ๋‚ด์—์„œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด, ๊ทธ ๊ฐ’์œผ๋กœ resolve๋˜๋Š” Promise๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.
  • ํ•จ์ˆ˜ ๋‚ด์—์„œ ์˜ˆ์™ธ(Exception)๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, ๊ทธ ์˜ˆ์™ธ๋กœ reject๋˜๋Š” Promise๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

await ์—ฐ์‚ฐ์ž (await operator)โ€‹

  • async ํ•จ์ˆ˜ ๋‚ด์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • await ํ‚ค์›Œ๋“œ ๋‹ค์Œ์—๋Š” ์ฃผ๋กœ Promise๊ฐ€ ์œ„์น˜ํ•ฉ๋‹ˆ๋‹ค. (Promise๊ฐ€ ์•„๋‹Œ ๊ฐ’์ด ์˜ค๋ฉด, ๊ทธ ๊ฐ’ ์ž์ฒด๋กœ resolve๋œ Promise์ฒ˜๋Ÿผ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.)
  • await๋Š” ๋’ค๋”ฐ๋ฅด๋Š” Promise๊ฐ€ fulfilled ๋˜๊ฑฐ๋‚˜ rejected ๋  ๋•Œ๊นŒ์ง€ ํ˜„์žฌ async ํ•จ์ˆ˜์˜ ์‹คํ–‰์„ ์ผ์‹œ ์ค‘์ง€์‹œํ‚ต๋‹ˆ๋‹ค. Promise๊ฐ€ fulfilled๋˜๋ฉด ๊ทธ ๊ฒฐ๊ณผ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , rejected๋˜๋ฉด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
  • ํ•ต์‹ฌ ์›๋ฆฌ: async ํ•จ์ˆ˜์˜ ์‹คํ–‰์€ await ์ง€์ ์—์„œ ์ž ์‹œ ๋ฉˆ์ถ”์ง€๋งŒ, Node.js์˜ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ์ ˆ๋Œ€ ๋ฉˆ์ถ”์ง€ ์•Š์Šต๋‹ˆ๋‹ค (๋…ผ๋ธ”๋กœํ‚น). ์ œ์–ด๊ถŒ์€ ์ฆ‰์‹œ ์ด๋ฒคํŠธ ๋ฃจํ”„์—๊ฒŒ ๋ฐ˜ํ™˜๋˜์–ด ๋‹ค๋ฅธ ์ž‘์—…(๋‹ค๋ฅธ ์š”์ฒญ ์ฒ˜๋ฆฌ, ํƒ€์ด๋จธ ์‹คํ–‰ ๋“ฑ)์„ ๊ณ„์† ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น Promise๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด, ์ด๋ฒคํŠธ ๋ฃจํ”„๋Š” ์ค‘๋‹จ๋˜์—ˆ๋˜ async ํ•จ์ˆ˜์˜ ์‹คํ–‰์„ ์ด์–ด๊ฐ‘๋‹ˆ๋‹ค.

5. ์‹ค์ „ ์˜ˆ์‹œ: ์ฝ”๋“œ ๋น„๊ตโ€‹

์•„๋ž˜๋Š” async/await๋ฅผ ์‚ฌ์šฉํ•œ ๋น„๋™๊ธฐ ์ž‘์—… ์ฒ˜๋ฆฌ์˜ ์ผ๋ฐ˜์ ์ธ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.

// ์ด ํ•จ์ˆ˜๋Š” Promise๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.
// ์˜ˆ: const { readFile } = require('fs').promises;
async function readFileAsync(filePath) {
// ์‹ค์ œ๋กœ๋Š” fs.promises.readFile ๋“ฑ์„ ์‚ฌ์šฉ
return new Promise((resolve, reject) => {
setTimeout(() => { // ํŒŒ์ผ ์ฝ๊ธฐ๋ฅผ ํ‰๋‚ด ๋‚ด๋Š” ์ง€์—ฐ
if (filePath === 'valid-file.txt') {
resolve(`Content of ${filePath}`);
} else {
reject(new Error('File not found or error reading file'));
}
}, 1000);
});
}

async function anotherAsyncTask(inputData) {
return new Promise(resolve => {
setTimeout(() => {
resolve(`Processed: ${inputData}`);
}, 500);
});
}

async function processFileOperations() {
console.log("๋น„๋™๊ธฐ ์ž‘์—… ํ๋ฆ„ ์‹œ์ž‘");
try {
console.log("ํŒŒ์ผ ์ฝ๊ธฐ ์‹œ๋„ (await)...");
// ์—ฌ๊ธฐ์„œ processFileOperations ํ•จ์ˆ˜์˜ ์‹คํ–‰์€ ์ž ์‹œ ๋ฉˆ์ถ”์ง€๋งŒ,
// ์ด๋ฒคํŠธ ๋ฃจํ”„๋Š” ๋‹ค๋ฅธ ์ž‘์—…์„ ๊ณ„์† ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!
const fileData = await readFileAsync('valid-file.txt');
console.log("ํŒŒ์ผ ์ฝ๊ธฐ ์™„๋ฃŒ:", fileData); // ์œ„ await ์ž‘์—…์ด ๋๋‚˜๋ฉด ์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ ์‹คํ–‰ ์žฌ๊ฐœ

console.log("๋‹ค๋ฅธ ๋น„๋™๊ธฐ ์ž‘์—… ์‹œ์ž‘ (await)...");
const result = await anotherAsyncTask(fileData);
console.log("๋‹ค๋ฅธ ๋น„๋™๊ธฐ ์ž‘์—… ์™„๋ฃŒ:", result);

} catch (error) {
console.error("์˜ค๋ฅ˜ ๋ฐœ์ƒ:", error.message);
}
console.log("๋ชจ๋“  ๋น„๋™๊ธฐ ์ž‘์—… ์ ˆ์ฐจ ์™„๋ฃŒ");
}

processFileOperations();
console.log("processFileOperations ํ•จ์ˆ˜ ํ˜ธ์ถœ ์งํ›„ (์•„์ง ๋‚ด๋ถ€ await ์™„๋ฃŒ ์ „)");

์ƒ์„ธ ์˜ˆ์ œ ์ฝ”๋“œ: ๋ธ”๋กœํ‚น vs. ๋…ผ๋ธ”๋กœํ‚น ์‹œ์—ฐโ€‹

๋‹ค์Œ ์ฝ”๋“œ๋Š” test_io_delayed.js๋ผ๋Š” ํŒŒ์ผ๋กœ ์ €์žฅํ•˜์—ฌ Node.js ํ™˜๊ฒฝ์—์„œ ์ง์ ‘ ์‹คํ–‰ํ•ด๋ณด๋ฉด ๊ทธ ์ฐจ์ด๋ฅผ ๋ช…ํ™•ํžˆ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// test_io_delayed.js

// ํ•„์š”ํ•œ ๋ชจ๋“ˆ ๊ฐ€์ ธ์˜ค๊ธฐ
const fs = require('fs'); // ํŒŒ์ผ ์‹œ์Šคํ…œ ๋™๊ธฐ ํ•จ์ˆ˜์šฉ
const fsPromises = require('fs').promises; // ํŒŒ์ผ ์‹œ์Šคํ…œ ๋น„๋™๊ธฐ ํ•จ์ˆ˜์šฉ (Promise ๊ธฐ๋ฐ˜)
const path = require('path'); // ํŒŒ์ผ ๊ฒฝ๋กœ ์ฒ˜๋ฆฌ์šฉ

// ํ…Œ์ŠคํŠธ์šฉ ํŒŒ์ผ ๊ฒฝ๋กœ ์„ค์ • (์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์ƒ์„ฑ๋จ)
const syncFilePath = path.join(__dirname, 'dummy-sync.txt');
const asyncFilePath = path.join(__dirname, 'dummy-async.txt');
// ํŒŒ์ผ ํฌ๊ธฐ (๋ผ์ธ ์ˆ˜) - ์‹ค์ œ I/O ์‹œ๊ฐ„์„ ์œ ์˜๋ฏธํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ํฌ๊ฒŒ ์œ ์ง€
const FILE_SIZE_LINES = 1000000; // ๋ฐฑ๋งŒ ๋ผ์ธ
// ๋น„๋™๊ธฐ ์ž‘์—…์— ์ถ”๊ฐ€ํ•  ์ธ์œ„์ ์ธ ์ง€์—ฐ ์‹œ๊ฐ„ (ms)
const ASYNC_DELAY_MS = 350; // 350ms (I/O ์™ธ ์ถ”๊ฐ€ ์ง€์—ฐ)

// --- 1. ๋™๊ธฐ ๋ธ”๋กœํ‚น ์˜ˆ์ œ ํ•จ์ˆ˜ ---
function runSyncBlockingExample() {
console.log("\n--- [์‹œ์ž‘] ๋™๊ธฐ (readFileSync) ๋ธ”๋กœํ‚น ํ…Œ์ŠคํŠธ ---");
try {
console.log(`[Sync] ํ…Œ์ŠคํŠธ ํŒŒ์ผ ์ƒ์„ฑ ์‹œ์ž‘ (${syncFilePath})...`);
let fileContent = '';
for (let i = 0; i < FILE_SIZE_LINES; i++) {
fileContent += `๋™๊ธฐ ํ…Œ์ŠคํŠธ ํŒŒ์ผ ๋ผ์ธ ${i + 1}\n`;
}
fs.writeFileSync(syncFilePath, fileContent); // ๋™๊ธฐ ํŒŒ์ผ ์“ฐ๊ธฐ
console.log("[Sync] ํ…Œ์ŠคํŠธ ํŒŒ์ผ ์ƒ์„ฑ ์™„๋ฃŒ.");
} catch (err) {
console.error("[Sync] ํŒŒ์ผ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜:", err);
return; // ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ํ•จ์ˆ˜ ์ข…๋ฃŒ
}

let syncIntervalCount = 0;
console.log("[Sync] ํƒ€์ด๋จธ ์‹œ์ž‘ (100ms ๊ฐ„๊ฒฉ). readFileSync ์‹คํ–‰ ์ „.");
const syncTimer = setInterval(() => {
syncIntervalCount++;
// ์ด ๋กœ๊ทธ๋Š” readFileSync๊ฐ€ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ ๋ธ”๋กœํ‚นํ•˜๋Š” ๋™์•ˆ์—๋Š” ์ถœ๋ ฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
console.log(`[Sync Timer] ${syncIntervalCount * 100}ms ๊ฒฝ๊ณผ (์ด๋ฒคํŠธ ๋ฃจํ”„ ํ™œ์„ฑํ™” ์‹œ ์ถœ๋ ฅ)`);
}, 100);

console.log("[Sync] >>> readFileSync ํ˜ธ์ถœ ์‹œ์ž‘... ์ด์ œ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ์—ฌ๊ธฐ์„œ ๋ฉˆ์ถฅ๋‹ˆ๋‹ค! <<<");
try {
const startTime = Date.now();
const data = fs.readFileSync(syncFilePath, 'utf8'); // ๋™๊ธฐ ํŒŒ์ผ ์ฝ๊ธฐ (๋ธ”๋กœํ‚น!)
const endTime = Date.now();
console.log(`[Sync] readFileSync ์™„๋ฃŒ! ${Buffer.byteLength(data)} ๋ฐ”์ดํŠธ ์ฝ์Œ. ์†Œ์š” ์‹œ๊ฐ„: ${endTime - startTime}ms`);
console.log("[Sync] readFileSync๊ฐ€ ์™„๋ฃŒ๋œ ํ›„์—์•ผ ์ด ๋ฉ”์‹œ์ง€๊ฐ€ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. ์ดํ›„ ํƒ€์ด๋จธ๋Š” ์ฆ‰์‹œ ์ •๋ฆฌ๋ฉ๋‹ˆ๋‹ค.");
} catch (err) {
console.error("[Sync] ํŒŒ์ผ ์ฝ๊ธฐ ์ค‘ ์˜ค๋ฅ˜:", err);
} finally {
clearInterval(syncTimer); // ํƒ€์ด๋จธ ์ •๋ฆฌ
console.log("[Sync] ํƒ€์ด๋จธ ์ข…๋ฃŒ๋จ.");
console.log("--- [์ข…๋ฃŒ] ๋™๊ธฐ (readFileSync) ๋ธ”๋กœํ‚น ํ…Œ์ŠคํŠธ ---");
}
}

// --- ๋น„๋™๊ธฐ ํŒŒ์ผ ์ฝ๊ธฐ์— ์ธ์œ„์ ์ธ ์ง€์—ฐ์„ ์ถ”๊ฐ€ํ•˜๋Š” ํ—ฌํผ ํ•จ์ˆ˜ ---
async function readFileWithDelay(filePath, encoding, delayMs) {
// 1. ์‹ค์ œ ํŒŒ์ผ ์ฝ๊ธฐ (๋น„๋™๊ธฐ)
const data = await fsPromises.readFile(filePath, encoding);

// 2. ์ธ์œ„์ ์ธ ๋น„๋™๊ธฐ ์ง€์—ฐ ์ถ”๊ฐ€ (Promise์™€ setTimeout ์‚ฌ์šฉ)
if (delayMs > 0) {
console.log(`[Async Helper] ${delayMs}ms ์ธ์œ„์  ์ง€์—ฐ ์‹œ์ž‘...`);
await new Promise(resolve => setTimeout(resolve, delayMs));
console.log(`[Async Helper] ${delayMs}ms ์ธ์œ„์  ์ง€์—ฐ ์™„๋ฃŒ.`);
}
// 3. ํŒŒ์ผ ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜
return data;
}

// --- 2. ๋น„๋™๊ธฐ ๋…ผ๋ธ”๋กœํ‚น ์˜ˆ์ œ ํ•จ์ˆ˜ ---
// ๋น„๋™๊ธฐ ์ž‘์—…์„ ์œ„ํ•ด async ํ•จ์ˆ˜๋กœ ์ •์˜
async function runAsyncNonBlockingExample() {
console.log("\n--- [์‹œ์ž‘] ๋น„๋™๊ธฐ (readFile + ์ธ์œ„์  ์ง€์—ฐ) ๋…ผ๋ธ”๋กœํ‚น ํ…Œ์ŠคํŠธ ---");

// 2-1. ํ…Œ์ŠคํŠธ์šฉ ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ '๋น„๋™๊ธฐ์ '์œผ๋กœ ์ƒ์„ฑ
console.log(`[Async] ํ…Œ์ŠคํŠธ ํŒŒ์ผ ์ƒ์„ฑ ์‹œ์ž‘ (${asyncFilePath})...`);
try {
let fileContent = '';
for (let i = 0; i < FILE_SIZE_LINES; i++) {
fileContent += `๋น„๋™๊ธฐ ํ…Œ์ŠคํŠธ ํŒŒ์ผ ๋ผ์ธ ${i + 1}\n`;
}
await fsPromises.writeFile(asyncFilePath, fileContent); // ๋น„๋™๊ธฐ ํŒŒ์ผ ์“ฐ๊ธฐ
console.log("[Async] ํ…Œ์ŠคํŠธ ํŒŒ์ผ ์ƒ์„ฑ ์™„๋ฃŒ.");
} catch (err) {
console.error("[Async] ํŒŒ์ผ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:", err);
return; // ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ํ•จ์ˆ˜ ์ข…๋ฃŒ
}

// 2-2. ์ฃผ๊ธฐ์ ์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ํƒ€์ด๋จธ ์„ค์ • (100ms ๊ฐ„๊ฒฉ)
let asyncIntervalCount = 0;
const asyncTimer = setInterval(() => {
asyncIntervalCount++;
// ์ค‘์š”: ์ด ๋กœ๊ทธ๋Š” ์•„๋ž˜ await readFileWithDelay ์ž‘์—…์ด ์ง„ํ–‰๋˜๋Š” '์ค‘์—๋„' ๊ณ„์† ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.
// ์ด๋Š” ์ด๋ฒคํŠธ ๋ฃจํ”„๊ฐ€ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ  ๋‹ค๋ฅธ ์ž‘์—…(ํƒ€์ด๋จธ ์ฝœ๋ฐฑ)์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
console.log(`[Async Timer] ${asyncIntervalCount * 100}ms ๊ฒฝ๊ณผ (์ด๋ฒคํŠธ ๋ฃจํ”„ ํ™œ์„ฑ ์ƒํƒœ)`);
}, 100);
console.log("[Async] ํƒ€์ด๋จธ ์‹œ์ž‘ (100ms ๊ฐ„๊ฒฉ). readFileWithDelay ํ˜ธ์ถœ ์ „.");

// 2-3. ์ง€์—ฐ์ด ์ถ”๊ฐ€๋œ ๋น„๋™๊ธฐ ํŒŒ์ผ ์ฝ๊ธฐ ์‹คํ–‰ (๋…ผ๋ธ”๋กœํ‚น ์ง€์ )
console.log(`[Async] >>> await readFileWithDelay ํ˜ธ์ถœ ์‹œ์ž‘ (ํŒŒ์ผ ์ฝ๊ธฐ + ${ASYNC_DELAY_MS}ms ์ง€์—ฐ)... ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ๋ฉˆ์ถ”์ง€ ์•Š์Šต๋‹ˆ๋‹ค! <<<`);
try {
const startTime = Date.now(); // ์ „์ฒด ์ž‘์—… ์‹œ์ž‘ ์‹œ๊ฐ„ ๊ธฐ๋ก

// readFileWithDelay ํ•จ์ˆ˜ ํ˜ธ์ถœ (๋‚ด๋ถ€์ ์œผ๋กœ await fsPromises.readFile๊ณผ await new Promise ์‚ฌ์šฉ)
const data = await readFileWithDelay(asyncFilePath, 'utf8', ASYNC_DELAY_MS);

const endTime = Date.now(); // ์ „์ฒด ์ž‘์—… ์ข…๋ฃŒ ์‹œ๊ฐ„ ๊ธฐ๋ก
// ์ด ๋กœ๊ทธ๋Š” ํŒŒ์ผ ์ฝ๊ธฐ์™€ ์ถ”๊ฐ€ ์ง€์—ฐ์ด '๋ชจ๋‘ ์™„๋ฃŒ๋œ ์‹œ์ '์— ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.
console.log(`[Async] >>> await readFileWithDelay ์™„๋ฃŒ! <<< ${Buffer.byteLength(data)} ๋ฐ”์ดํŠธ ์ฝ์Œ.`);
console.log(`[Async] ์ „์ฒด ์ž‘์—…(ํŒŒ์ผ ์ฝ๊ธฐ + ์ธ์œ„์  ์ง€์—ฐ) ์†Œ์š” ์‹œ๊ฐ„: ${endTime - startTime}ms`);
console.log(`[Async] ํŒŒ์ผ ์ฝ๊ธฐ + ์ง€์—ฐ ์ž‘์—…์ด ์ง„ํ–‰๋˜๋Š” ๋™์•ˆ ํƒ€์ด๋จธ๊ฐ€ ${asyncIntervalCount}๋ฒˆ ๋™์ž‘ํ–ˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.`);

} catch (err) {
console.error("[Async] ์ž‘์—… ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:", err);
} finally {
// 2-4. ํƒ€์ด๋จธ ์ •๋ฆฌ
clearInterval(asyncTimer);
console.log("[Async] ํƒ€์ด๋จธ ์ข…๋ฃŒ๋จ.");
console.log("--- [์ข…๋ฃŒ] ๋น„๋™๊ธฐ (readFile + ์ธ์œ„์  ์ง€์—ฐ) ๋…ผ๋ธ”๋กœํ‚น ํ…Œ์ŠคํŠธ ---");
}
}

// --- 3. ์˜ˆ์ œ ์‹คํ–‰ ๋ฐ ํŒŒ์ผ ์ •๋ฆฌ ---
async function runTests() {
// ๋™๊ธฐ ์˜ˆ์ œ ์‹คํ–‰
runSyncBlockingExample();

// ๋น„๋™๊ธฐ ์˜ˆ์ œ ์‹คํ–‰ ์ „ ์ž ์‹œ ๋Œ€๊ธฐ (์ฝ˜์†” ์ถœ๋ ฅ ๊ตฌ๋ถ„์„ ์œ„ํ•ด)
console.log("\n์ž ์‹œ ํ›„ ๋น„๋™๊ธฐ ํ…Œ์ŠคํŠธ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค (1.5์ดˆ ๋Œ€๊ธฐ)...");
await new Promise(resolve => setTimeout(resolve, 1500));

// ๋น„๋™๊ธฐ ์˜ˆ์ œ ์‹คํ–‰
await runAsyncNonBlockingExample();

// ํ…Œ์ŠคํŠธ ์™„๋ฃŒ ํ›„ ์ƒ์„ฑ๋œ ์ž„์‹œ ํŒŒ์ผ ์‚ญ์ œ (์ •๋ฆฌ)
console.log("\n--- ํ…Œ์ŠคํŠธ ํŒŒ์ผ ์ •๋ฆฌ ์‹œ์ž‘ ---");
try {
if (fs.existsSync(syncFilePath)) {
fs.unlinkSync(syncFilePath);
console.log(`[์ •๋ฆฌ] ${syncFilePath} ์‚ญ์ œ ์™„๋ฃŒ.`);
}
// ๋น„๋™๊ธฐ ํŒŒ์ผ์€ fsPromises.unlink๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ์กด์žฌ ํ™•์ธ ํ›„ ๋™๊ธฐ ์‚ญ์ œ๋„ ๊ฐ€๋Šฅ
try {
await fsPromises.stat(asyncFilePath); // ํŒŒ์ผ ์กด์žฌ ํ™•์ธ
await fsPromises.unlink(asyncFilePath);
console.log(`[์ •๋ฆฌ] ${asyncFilePath} ์‚ญ์ œ ์™„๋ฃŒ.`);
} catch (statErr) {
if (statErr.code !== 'ENOENT') { // ENOENT๋Š” 'Error NO ENTry' (ํŒŒ์ผ์ด๋‚˜ ๋””๋ ‰ํ„ฐ๋ฆฌ ์—†์Œ)
console.error(`[์ •๋ฆฌ] ${asyncFilePath} ์ƒํƒœ ํ™•์ธ/์‚ญ์ œ ์ค‘ ์˜ค๋ฅ˜:`, statErr);
} else {
console.log(`[์ •๋ฆฌ] ${asyncFilePath} ํŒŒ์ผ์ด ์ด๋ฏธ ์—†๊ฑฐ๋‚˜ ์ƒ์„ฑ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.`);
}
}
} catch (err) {
console.error("[์ •๋ฆฌ] ํŒŒ์ผ ์‚ญ์ œ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:", err);
}
console.log("--- ๋ชจ๋“  ํ…Œ์ŠคํŠธ ๋ฐ ์ •๋ฆฌ ์™„๋ฃŒ ---");
}

// ์ „์ฒด ํ…Œ์ŠคํŠธ ์‹คํ–‰
runTests();


6. ์‹œ์—ฐ ์ฝ”๋“œ ๊ฒฐ๊ณผ ์š”์•ฝโ€‹

  • ๋™๊ธฐ ๋ฐฉ์‹ (readFileSync): fs.readFileSync๊ฐ€ ์‹คํ–‰๋˜๋Š” ๋™์•ˆ์—๋Š” setInterval๋กœ ์„ค์ •ํ•œ ํƒ€์ด๋จธ์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๊ฐ€ ์ „ํ˜€ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ readFileSync ์ž‘์—…์— ์˜ํ•ด ์™„์ „ํžˆ ๋ธ”๋กœํ‚น๋˜์—ˆ์Œ์„ ๋ช…ํ™•ํžˆ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.
  • ๋น„๋™๊ธฐ ๋ฐฉ์‹ (await fsPromises.readFile + ์ถ”๊ฐ€ ์ง€์—ฐ): await fsPromises.readFile (๊ทธ๋ฆฌ๊ณ  await new Promise๋ฅผ ํ†ตํ•œ ์ธ์œ„์  ์ง€์—ฐ) ๋ถ€๋ถ„์ด ์‹คํ–‰๋˜๋Š” ๋™์•ˆ์—๋„ setInterval ํƒ€์ด๋จธ๋Š” ์ฃผ๊ธฐ์ ์œผ๋กœ ๊ณ„์† ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” await๊ฐ€ ํ˜„์žฌ async ํ•จ์ˆ˜์˜ ์‹คํ–‰ ํ๋ฆ„๋งŒ ์ผ์‹œ ์ค‘๋‹จํ•  ๋ฟ, Node.js์˜ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ ๋ธ”๋กœํ‚นํ•˜์ง€ ์•Š๊ณ  ์ด๋ฒคํŠธ ๋ฃจํ”„๊ฐ€ ๋‹ค๋ฅธ ์ž‘์—…(์—ฌ๊ธฐ์„œ๋Š” ํƒ€์ด๋จธ ์ฝœ๋ฐฑ)์„ ๊ณ„์† ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์ฆ๋ช…ํ•ฉ๋‹ˆ๋‹ค.

7. ๊ฒฐ๋ก โ€‹

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

๋”ฐ๋ผ์„œ Node.js ๊ฐœ๋ฐœ ์‹œ์—๋Š” async/await๋ฅผ ์ ๊ทน์ ์œผ๋กœ ํ™œ์šฉํ•˜์—ฌ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์ด๋Š” ๋™์‹œ์—, ์‹œ์Šคํ…œ ์ „์ฒด์˜ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.