๐Ÿ‘ฉ‍๐Ÿ’ป/JavaScript

[JavaScript] ์ฝœ์Šคํƒ, ์ฝœ๋ฐฑํ, ์ด๋ฒคํŠธ ๋ฃจํ”„์˜ ๋™์ž‘ ์›๋ฆฌ๋ฅผ ํ†ตํ•œ ๋น„๋™๊ธฐ ํ•จ์ˆ˜์˜ ์‹คํ–‰ ๊ณผ์ •

ํ•œ๋‚˜ 2021. 5. 9. 10:31

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ๋‹น์—ฐํžˆ ์˜์‹ฌ ์—†์ด ์‚ฌ์šฉํ•˜๋˜ ๋น„๋™๊ธฐ ํ•จ์ˆ˜ ์‹คํ–‰ ๊ณผ์ •์„ ๋“ค์—ฌ๋‹ค๋ณผ ๊ธฐํšŒ๊ฐ€ ์ƒ๊ฒผ๋‹ค. ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ ์–ธ์–ด์ธ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ํ•œ ์ค„ ํ•œ ์ค„ ์ˆœ์„œ๋Œ€๋กœ ๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋ฉฐ, ๋‘ ๊ฐ€์ง€ ์ด์ƒ์˜ ํƒœ์Šคํฌ๋ฅผ ๋ณ‘๋ ฌ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†์–ด์•ผ ํ•˜๋Š”๋ฐ, setTimeout์ด๋‚˜ ์›น API, ES6์˜ promise ๊ฐ™์€ ๋น„๋™๊ธฐ ์ฝœ๋ฐฑ์€ ์–ด๋–ป๊ฒŒ ์‹คํ–‰๋˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ผ๊นŒ?

์šฐ์„  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ๋‘ ๊ฐ€์ง€ ์ด์ƒ์˜ statements๊ฐ€ ๋ณ‘๋ ฌ์ ์œผ๋กœ ์‹คํ–‰๋˜์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ์€ ๋งž๋‹ค. ์‹คํ–‰์€ ํ•ญ์ƒ ๋™๊ธฐ์ ์ด๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„ V8์€ ํ”„๋กœ๊ทธ๋žจ ๋‚ด ๋ชจ๋“  ๋ณ€์ˆ˜์˜ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์ด ์ผ์–ด๋‚˜๋Š” Memory Heap๊ณผ ์Šคํƒ ํ”„๋ ˆ์ž„์ด ์Œ“์ด๋Š” ์ฝœ ์Šคํƒ์œผ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๊ณ  ์ด ์ฝœ ์Šคํƒ(ํ˜ธ์ถœ ์Šคํƒ)์ด ํ•˜๋‚˜์ด๋ฏ€๋กœ ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ์ด๋‹ค. ํ•˜์ง€๋งŒ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์‹คํ–‰๋˜๋Š” ๋ธŒ๋ผ์šฐ์ €๋‚˜ Node.js ๊ฐ™์€ ํ™˜๊ฒฝ์—์„œ๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ํ™œ์šฉ๋œ๋‹ค. ์ด๋ฅผ ์ƒํ˜ธ ์—ฐ๊ณ„ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ์ด๋ฒคํŠธ ๋ฃจํ”„๊ฐ€ ๋‹ด๋‹นํ•œ๋‹ค.

console.log('Message 1'); 
setTimeout(function() { 
  console.log('Message 2') 
}, 100); 
console.log('Message 3')

// output
// 'Message 1'
// 'Message 3'
// 'Message 2'

setTimeout์˜ ์ฒซ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์ฝœ๋ฐฑ ํ•จ์ˆ˜๊ฐ€ ์˜ค๊ณ , ๋‘ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋ฐ€๋ฆฌ ์„ธ์ปจ๋“œ ๋‹จ์œ„์˜ ์‹œ๊ฐ„์ด ์˜ค๊ธฐ ๋•Œ๋ฌธ์—, ํŠน์ • ์‹œ๊ฐ„ ์ดํ›„์— ์‹คํ–‰๋˜๋Š” ํŠน์„ฑ ์ƒ 'Message 2'์ด ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์— ์ถœ๋ ฅ๋œ๋‹ค.

์—ฌ๊ธฐ์— event loop์˜ ๋„์›€์ด ์žˆ์—ˆ๋‹ค. ์ด๋ฒคํŠธ ๋ฃจํ”„ ๋•๋ถ„์— ๋ชจ๋“  ๋™๊ธฐ์ ์ธ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰์ด ๋๋‚œ ๋’ค์—์•ผ ๋น„๋™๊ธฐ์  ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฒคํŠธ ๋ฃจํ”„๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ฝœ ์Šคํƒ์„ ๋จผ์ € ์•Œ์•„์•ผ ํ•œ๋‹ค.

์ฝœ ์Šคํƒ(Call Stack)์ด๋ž€?

์ฝœ ์Šคํƒ์€ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๊ฐ€ ํ”„๋กœ๊ทธ๋žจ์„ ์ฝ์„ ๋•Œ ํ•˜๋‚˜์”ฉ ์Œ“์—ฌ๋“ค์–ด๊ฐ€๊ณ , ์‹คํ–‰๋˜๋ฉฐ, ์‹คํ–‰์ด ์™„๋ฃŒ๋˜๋ฉด ๋น„์›Œ์ง€๋Š” ๊ณณ์ด๋‹ค. ํ˜ธ์ถœ๋œ ์ˆœ์„œ์™€ ๋ฐ˜๋Œ€๋กœ ์‹คํ–‰์ด ๋˜๋Š” ๊ตฌ์กฐ๋ผ '์Šคํƒ'์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค. ๊ฐœ๋ฐœ์ž์˜ ์‹ค์ˆ˜๋กœ ์žฌ๊ท€ ํ•จ์ˆ˜ ๋“ฑ์„ ์ผ์„ ๋•Œ ์ฝœ์Šคํƒ์— ์˜ฌ๋ผ๊ฐ„ ํ•จ์ˆ˜๊ฐ€ ๋˜ ์ž๊ธฐ ์ž์‹ ์„ ๋ถˆ๋Ÿฌ ์Šคํƒ์„ ์ฑ„์šฐ๊ณ .. ์ ์  ๋ธŒ๋ผ์šฐ์ €๋ณ„ ์ตœ๋Œ€ ํ˜ธ์ถœ ์Šคํƒ์„ ์ดˆ๊ณผํ•  ๊ฒฝ์šฐ Uncaught RangeError: Maximum call stack size exceeded ๊ฐ™์€ ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ๊ทธ๋•Œ ๋“ฑ์žฅํ•˜๋Š” call stack์ด๋‹ค.

๋งŒ์ผ ์‹คํ–‰ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋น„๋™๊ธฐ์ ์ด๋ผ๊ณ  ํ•  ๋•Œ(setTimeout, promise, click event ๋“ฑ), ์ฝ”๋“œ๋Š” ์ด๋ฒคํŠธ ํ…Œ์ด๋ธ”๋กœ ํ–ฅํ•˜๊ณ , ์ด ํ…Œ์ด๋ธ”์€ ์ง€์ •๋œ ์‹œ๊ฐ„ ์ดํ›„์— ๋น„๋™๊ธฐ ์ฝ”๋“œ๋ฅผ ์ฝœ๋ฐฑ/์ด๋ฒคํŠธ ํ๋กœ ์˜ฎ๊ธฐ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

์ฝœ๋ฐฑ ํ(Callback Queue)๋ž€?

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

์ด๋ฒคํŠธ ๋ฃจํ”„(Event Loop)๋ž€?

๊ทธ๋Ÿฐ ๋‹ค์Œ์— ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ด๋ฒคํŠธ ๋ฃจํ”„๊ฐ€ ๋“ฑ์žฅํ•œ๋‹ค. ์ด๋ฒคํŠธ ๋ฃจํ”„๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ๋…ธ๋“œ์—์„œ ์‚ฌ์šฉ๋œ๋‹ค. ์ด๋ฒคํŠธ ๋ฃจํ”„๋Š” ๊ณ„์† ๋Œ์•„๊ฐ€๋ฉด์„œ ๋ฉ”์ธ ์Šคํƒ์„ ๋ณด๋ฉฐ ๋งŒ์ผ ์‹คํ–‰ํ•  ๊ฒŒ ํ•˜๋‚˜๋ผ๋„ ์žˆ๋Š”์ง€ ๋ณด๊ณ , ์—†๋‹ค๋ฉด ์ฝœ๋ฐฑ ํ๋ฅผ ํ™•์ธํ•œ ๋’ค ์ฝœ๋ฐฑ ํ์— ์‹คํ–‰ํ•  ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๋ฉด ๊ทธ ์•ˆ์—์„œ ๊ทธ๊ฑธ ๊บผ๋‚ด๋“ค์–ด์„œ ๋ฉ”์ธ ์Šคํƒ์— ์‹คํ–‰์„ ์œ„ํ•ด ์˜ฌ๋ ค๋‘๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. ์ด ํ•œ ๋ฒˆ์˜ ๊ณผ์ •์„ tick์ด๋ผ๊ณ  ํ•œ๋‹ค. ์ด๋ฒคํŠธ ๋ฃจํ”„๋Š” ์ด ์ž‘์—…์„ ๊ณ„์† ์ง„ํ–‰ํ•ด looping ํ•œ๋‹ค.

Job Queue

์ฝœ๋ฐฑ ํ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ํ๋กœ๋Š” ์žก ํ๊ฐ€ ์žˆ๋‹ค. new Promise() ๊ธฐ๋Šฅ์„ ์œ„ํ•ด์„œ๋งŒ ์กด์žฌํ•œ๋‹ค. ์ฝ”๋“œ ๋‚ด์— promise๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, ์ฝœ๋ฐฑ ๋ฉ”์†Œ๋“œ์ธ then()๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด, ์ด 'thenable'ํ•œ ๋ฉ”์†Œ๋“œ๋Š” promise๊ฐ€ ๋ฆฌํ„ด๋˜๊ฑฐ๋‚˜ resolve๋˜์—ˆ์„ ๋•Œ job queue์— ์ถ”๊ฐ€๋˜๊ณ , ๊ทธ ํ›„์— ์‹คํ–‰์ด ๋œ๋‹ค.

console.log('Message no. 1: Sync'); 
setTimeout(function() { 
  console.log('Message no. 2: setTimeout'); 
}, 0); 
const promise = new Promise(function(resolve, reject) { 
  resolve(); 
}); 
promise
    .then(function(resolve) { 
  console.log('Message no. 3: 1st Promise'); 
    }).then(function(resolve) { 
  console.log('Message no. 4: 2nd Promise'); 
}); 
console.log('Message no. 5: Sync');
// Message no. 1: Sync 
// Message no. 5: Sync 
// Message no. 3: 1st Promise 
// Message no. 4: 2nd Promise
// Message no. 2: setTimeout 

ํ”ํžˆ setTimeout์ด ๋จผ์ € ์ฝœ๋ฐฑ ํ๋กœ ๋“ค์–ด๊ฐ€๊ณ , ๊ทธ ํ›„ promise์˜ thenableํ•œ ๋ฉ”์†Œ๋“œ๊ฐ€ ๋’ค์ด์„ ๊ฑฐ๋ผ ์ƒ๊ฐํ•˜์ง€๋งŒ, ์žกํ๊ฐ€ ์ฝœ๋ฐฑ์„ ์‹คํ–‰ํ•˜๋Š” ๋ฐ ์žˆ์–ด ๋†’์€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ฐ€์ง€๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฒคํŠธ ๋ฃจํ”„๋Š” ์žกํ์— ์žˆ๋Š” ๋ชจ๋“  ํƒœ์Šคํฌ๋ฅผ ์™„๋ฃŒํ•œ ์ดํ›„์— ์ฝœ๋ฐฑ ํ๋กœ ๋„˜์–ด๊ฐ€๋„๋ก ๋˜์–ด ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ”„๋กœ๋ฏธ์Šค๋ฅผ ์œ„ํ•œ ๋ชจ๋“  thenable ์ฝœ๋ฐฑ์ด ๋จผ์ € ๋ถˆ๋ฆฌ๊ณ , ๊ทธ ํ›„์— setTimeout์ฝœ๋ฐฑ์ด ๋ถˆ๋ฆฐ๋‹ค.

setTimeout์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๊ฐ€ 0์ด๋ผ๊ณ  ํ•ด๋„ ์ผ๋‹จ ๋น„๋™๊ธฐ ํ•จ์ˆ˜์ด๋ฏ€๋กœ ์ฝœ๋ฐฑ ํ์— ๋Œ€๊ธฐํ•˜๋Š” ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•œ๋‹ค. ๊ทธ๋ž˜์„œ Message 1 ์ดํ›„์— ๋ฐ”๋กœ Message 2๊ฐ€ ์ฐํžˆ์ง€ ์•Š๋Š”๋‹ค.

Reference