๐ 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์ ์ฝ๋ฐฑ ์คํ, ๋คํธ์ํฌ ์๋ต ์ฒ๋ฆฌ ๋ฑ์ด ๋ชจ๋ ์ง์ฐ๋ฉ๋๋ค.
๋์์ ์ฌ๋ฌ ์์ฒญ์ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํด์ผ ํ๋ ์๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ํ๊ฒฝ์์ ๋๊ธฐ ๋ธ๋กํน ๋ฐฉ์์ ์ฌ๊ฐํ ์ฑ๋ฅ ์ ํ๋ฅผ ์ ๋ฐํ ์ ์์ต๋๋ค. ๋จ์ผ ์์ฒญ ์ฒ๋ฆฌ ์ค ๋ค๋ฅธ ๋ชจ๋ ์์ฒญ์ ์๋ต์ด ์ง์ฐ๋๊ธฐ ๋๋ฌธ์ ๋๋ค.
์์ ์ฝ๋:
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)์ ์ถ๊ฐ๋๊ณ , ์ด๋ฒคํธ ๋ฃจํ์ ์ํด ์ ์ ํ ์์ ์ ๋ฉ์ธ ์ค๋ ๋์์ ์คํ๋ฉ๋๋ค.
I/O ์์ ์ผ๋ก ์ธํด ๋๊ธฐํ๋ ์๊ฐ ๋์์๋ ๋ค๋ฅธ ์์ฒญ์ด๋ ์์ ์ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ด, ๋์ ์ฒ๋ฆฌ๋(Throughput)๊ณผ ๋น ๋ฅธ ์๋ต์ฑ(Responsiveness)์ ์ ๊ณตํฉ๋๋ค.
3. Promise๋?โ
๊ฐ๋ : ๋น๋๊ธฐ ์์ ์ ์ต์ข ์๋ฃ(fulfillment) ๋๋ ์คํจ(rejection) ์ํ์ ๊ทธ ๊ฒฐ๊ณผ ๊ฐ์ ๋ํ๋ด๋ ๊ฐ์ฒด์ ๋๋ค. ์ฝ๋ฐฑ ํจ์์ ์ค์ฒฉ์ผ๋ก ๋ฐ์ํ๋ "์ฝ๋ฐฑ ์ง์ฅ(Callback Hell)" ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ , ๋น๋๊ธฐ ์ฝ๋๋ฅผ ๋์ฑ ์ฒด๊ณ์ ์ด๊ณ ๊ฐ๋ ์ฑ ์๊ฒ ์์ฑํ๋ ๋ฐ ๋์์ ์ค๋๋ค.
์ํ (States): Promise๋ ๋ค์ ์ธ ๊ฐ์ง ์ํ ์ค ํ๋๋ฅผ ๊ฐ์ง๋๋ค.
pending: ์ด๊ธฐ ์ํ. ๋น๋๊ธฐ ์์ ์ด ์์ง ์๋ฃ๋๊ฑฐ๋ ์คํจํ์ง ์์ ์ํ.fulfilled(orresolved): ๋น๋๊ธฐ ์์ ์ด ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋ ์ํ. ๊ฒฐ๊ณผ ๊ฐ์ ๊ฐ์ง๋๋ค.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 ์๋ฃ ์ )");