Published on

이벤트 루프, V8 엔진, 그리고 스택 & 큐

Authors
  • avatar
    Name
    Hyo814
    Twitter

Q1. V8 엔진의 주요 역할은 무엇인가요?

답변: V8 엔진은 자바스크립트 코드를 빠르게 실행하기 위해 설계된 고성능 엔진입니다. 자바스크립트 코드를 먼저 파싱하여 AST(Abstract Syntax Tree)를 생성한 후, 바이트코드와 네이티브 코드로 변환합니다. 특히 JIT(Just-In-Time) 컴파일을 통해 자주 실행되는 코드를 최적화하여 빠르게 처리하며, 메모리 관리를 위해 가비지 컬렉션을 수행합니다.


Q2. 이벤트 루프(Event Loop)의 역할과 구조를 설명하세요.

답변: 이벤트 루프는 자바스크립트의 비동기 작업을 처리하는 핵심 메커니즘입니다.

  • 콜스택(Call Stack): 함수 실행 컨텍스트가 쌓이는 구조로, 동기적으로 코드를 실행합니다.
  • 테스크 큐(Task Queue): 비동기 작업(예: setTimeout, I/O 작업)의 콜백이 대기하는 큐입니다.
  • 마이크로태스크 큐(Microtask Queue): Promisethen이나 MutationObserver 같은 작업이 처리되는 더 높은 우선순위의 큐입니다.

이벤트 루프는 콜스택이 비어 있는 상태에서 태스크 큐의 작업을 순차적으로 실행합니다. 마이크로태스크는 일반 태스크보다 우선 처리됩니다.


Q3. 이벤트 루프에서 마이크로태스크와 테스크의 차이점은 무엇인가요?

답변:

  • 마이크로태스크(Microtask):
    • Promise.then, async/await의 결과 처리, MutationObserver가 포함됩니다.
    • 우선순위가 높아 콜스택이 비어 있을 때 바로 실행됩니다.
  • 테스크(Task):
    • setTimeout, setInterval, DOM 이벤트 처리 등이 포함됩니다.
    • 마이크로태스크가 모두 처리된 후에 실행됩니다.

Q4. 왜 이벤트 루프는 콜스택과 큐를 사용하나요?

답변: 콜스택은 함수 호출과 실행을 관리하기 위해 사용되고, 큐는 비동기 작업을 처리하기 위해 사용됩니다. 자바스크립트는 싱글 스레드로 동작하기 때문에, 이벤트 루프를 통해 비동기 작업의 순서를 조정하고 처리할 수 있습니다. 이를 통해 UI가 멈추지 않고 비동기 처리가 가능합니다.


**2. 기술 노트 **

기술 노트 1: V8 엔진 구조

1. **코드 파싱**: 자바스크립트 코드를 AST로 변환
2. **바이트코드 생성**: 인터프리터(Ignition)가 실행
3. **최적화된 네이티브 코드**: JIT 컴파일러(Turbofan)가 생성
4. **메모리 관리**: 가비지 컬렉션(정리되지 않은 메모리 해제)
5. **콜스택 관리**: 실행 중인 함수와 컨텍스트를 추적

기술 노트 2: 이벤트 루프 단계

1. **콜스택(Call Stack)**:
   - 현재 실행 중인 함수가 쌓이고 처리됨.

2. **테스크 큐(Task Queue)**:
   - 타이머, I/O 작업 완료 후 실행될 콜백이 대기.
   - `setTimeout`, `setInterval` 등이 포함.

3. **마이크로태스크 큐(Microtask Queue)**:
   - 더 높은 우선순위를 가짐.
   - `Promise.then`, `MutationObserver` 등이 포함.

4. **우선순위**:
   - 콜스택 비우기 → 마이크로태스크 처리 → 테스크 처리.

기술 노트 3: 이벤트 루프의 동작 예시

console.log('Start')

setTimeout(() => {
  console.log('Task Queue')
}, 0)

Promise.resolve().then(() => {
  console.log('Microtask Queue')
})

console.log('End')

// 출력 순서:
// Start
// End
// Microtask Queue
// Task Queue

**기술 노트 4

// 이벤트 예제: 이벤트, target, currentTarget, 이벤트 리스너, 이벤트 전파, 이벤트 위임

// 1. 버튼 클릭 이벤트 예제
document.querySelector('#myButton').addEventListener('click', function () {
  console.log('Button clicked!')
})

// 2. 이벤트 타겟 (event.target) 예제
document.querySelector('#parent').addEventListener('click', function (event) {
  console.log('Target:', event.target) // 실제 클릭된 요소
})

// 3. 커렌트 타겟 (event.currentTarget) 예제
document.querySelector('#parent').addEventListener('click', function (event) {
  console.log('Current Target:', event.currentTarget) // 이벤트 핸들러가 연결된 요소
})

// 4. 이벤트 타겟과 커렌트 타겟 비교
document.querySelector('#parent').addEventListener('click', function (event) {
  console.log('Target:', event.target) // 클릭된 실제 요소
  console.log('Current Target:', event.currentTarget) // 이벤트 핸들러가 연결된 요소
})

// 5. 이벤트 위임 예제
document.querySelector('#list').addEventListener('click', function (event) {
  if (event.target.tagName === 'LI') {
    console.log('Clicked item:', event.target.textContent)
  }
})

// 6. 이벤트 전파 단계: 캡처링(Capturing)과 버블링(Bubbling)
// 이벤트 전파 단계
// - 캡처링 단계: 이벤트가 최상위 요소에서부터 이벤트가 발생한 요소로 내려옴
// - 타깃 단계: 이벤트가 발생한 요소에 도달
// - 버블링 단계: 이벤트가 다시 상위 요소로 올라감

document.querySelector('#child').addEventListener(
  'click',
  function (event) {
    console.log('Child clicked - Bubbling')
  },
  false
) // 버블링 단계 (default)

document.querySelector('#parent').addEventListener(
  'click',
  function (event) {
    console.log('Parent clicked - Capturing')
  },
  true
) // 캡처링 단계

// 7. 이벤트 전파 중단: stopPropagation()
document.querySelector('#child').addEventListener('click', function (event) {
  console.log('Stopping propagation')
  event.stopPropagation() // 부모로 전파 중단
})

document.querySelector('#parent').addEventListener('click', function (event) {
  console.log('Parent clicked (will not log if propagation is stopped)')
})

// 8. preventDefault() 사용 예제
document.querySelector('#link').addEventListener('click', function (event) {
  event.preventDefault() // 기본 동작(예: 링크 이동)을 막음
  console.log('Default action prevented')
})

// 이벤트 루프와 관련된 예제

// 기본적인 이벤트 루프 동작 예제
console.log('Start')

setTimeout(() => {
  console.log('Task from setTimeout')
}, 0)

Promise.resolve().then(() => {
  console.log('Microtask from Promise')
})

console.log('End')

// 실행 순서 설명:
// 1. console.log("Start") 실행
// 2. setTimeout의 콜백은 테스크 큐(Task Queue)로 이동
// 3. Promise.then의 콜백은 마이크로태스크 큐(Microtask Queue)로 이동
// 4. console.log("End") 실행
// 5. 마이크로태스크 큐의 작업 실행 ("Microtask from Promise")
// 6. 테스크 큐의 작업 실행 ("Task from setTimeout")

// 마이크로태스크 우선 처리 예제
console.log('Start')

setTimeout(() => {
  console.log('Task from setTimeout')
}, 0)

Promise.resolve()
  .then(() => {
    console.log('Microtask 1 from Promise')
  })
  .then(() => {
    console.log('Microtask 2 from Promise')
  })

console.log('End')

// 실행 순서 설명:
// 1. console.log("Start") 실행
// 2. setTimeout의 콜백은 테스크 큐(Task Queue)로 이동
// 3. 첫 번째 Promise.then의 콜백은 마이크로태스크 큐(Microtask Queue)로 이동
// 4. console.log("End") 실행
// 5. 마이크로태스크 큐 작업 실행 ("Microtask 1 from Promise" -> "Microtask 2 from Promise")
// 6. 테스크 큐 작업 실행 ("Task from setTimeout")