Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

Murpin Tech'blog

웹 브라우저에서의 이벤트 루프와 비동기 이해(1) 본문

FrontEnd/개인공부

웹 브라우저에서의 이벤트 루프와 비동기 이해(1)

Murpin 2022. 7. 10. 17:31

잘못된 정보나 오타의 경우 댓글로 남겨주시면 감사하겠습니다.

javascript가 동작하는 환경은 두 가지이다.

  1. 브라우저 환경
  2. nodejs환경

이 중에 브라우저 환경에 대한 이벤트 루프구조와 비동기에 대해 공부해보자

이벤트 루프 구조


위는 크롬 브라우저 엔진에 대한 이벤트 루프 구조이고 브라우저에서의 비동기 처리는 3개의 테스크 큐가 담당하고 있다.

web api를 호출하면 브라우저는 결과를 적절하게 큐에 등록한다.

    console.log("script start");

    setTimeout(function(){
        console.log("setTimeout")
    })

    Promise.resolve()
        .then(function(){
            console.log("Promise1");
        })
        .then(function(){
            console.log("Promise2");
        });

    requestAnimationFrame(function(){
        console.log("animation");
    })


    console.log("script end");

어라라?! JS는 인터프리터 언어로 위에서 부터 하나씩 순서대로 읽어나가는 언어인데
어째서 실행순서가 이렇게 다를 수가 있지? 라고 생각할 것이다.

이에 대한 결과를 알기 위해서는 JS의 이벤트 루프를 알아야한다.

하나씩 처음에 보여준 JS 이벤트 루프 사진을 분석하면서 천천히 알아보자

1. 콜스택

먼저 콜스택에 대해 이해하고 있어야 한다.
콜스택을 이해한다면 에러핸들링에 유용하게 이용할 수 있을 것이다.
콜스택에선 코드를 위에서 아래로 순차적으로 실행한다.
이는 함수의 실행이 끝날때까지 다른 어떤 작업도 중간에 끼여들어 해당 작업을 방해할 수 없다.
하지만 위에서 봤던 결과 예시에서는 여러 코드가 중간에 끼여드는 모습을 봤을 것이다.
과연 그렇다면 저 사진이 잘 못된건이가? 아니다. 좀 더 간단하게 코드를 수정해서 설명하겠다.

   console.log("script start");

    const timer = setTimeout(function(){
        console.log("setTimeout")
    }, 0);

    console.log(timer);

    const timer1 = setTimeout(function (){
        console.log("setTimeout");
    }, 0);

    console.log(timer1);
    console.log("script end");

setTimeout은 web API의 대표적인 함수로, setTimeout은 두 번째 인자로 전달한 시간 이후에 첫번째로 전달한 콜백 함수를 macroTaskQuque에 등록한다.
콜스택에서는 setTimeout이 정상적으로 호출되었는지가 중요할 뿐, 두 번째 인자로 전달한 시간 이후에 콜백이 등록되었는지 안되었는지를 확인할 필요는 없다.
이벤트루프에 대해서 web API를 호출하는 콜스택에선 해당 setTimeout이 정상적으로 등록되었는지몇 번째로 등록된 타이머인지 id 값만 반환 받고 다음 코드를 수행하게 된다.

웹 브라우저에서 비동기로 동작하는 web api를 호출하는 모든 행위가 위의 설명 같은 형태로 동작한다는 것을 이해하면 좋을 것 같다.

2. 에러핸들링

비동기 작업에서의 에러핸들링은 매우 중요하다. 보통 서버에 받아오는 정보에 대해 비동기 처리를 하기에 에러핸들링은 중요한 작업이다. 에러핸들링에 대한 예시를 한번 적어보겠다.

   console.log("script start");

    try{
        const timer = setTimeout(function(){
        console.log("setTimeout")
    }, 0);
        console.log(timer);
    }catch(error){

    }

    try{
        const timer1 = setTimeout(function (){
        console.log("setTimeout");
    }, 0);
        console.log(timer1);
    }catch(error){

    }


    console.log("script end");

앞의 코드 처럼 에러 핸들링을 할 경우 web api로 호출하는 부분에서 발생한 에러를 핸들링하게 된다.
setTimeout이 0초후에 호출하도록 web api를 요청하는데 브라우저가 해당 타이머를 등록하는 과정에서 발생하는 에러에 대해서 핸들링한다.

만약 setTimeout이 아닌 네트워크 통신을 하는 AJAX라고 한다면 콜스택은 브라우저에게 네트워크 통신을 해달라고 요청하게 되고 요청을 받은 브라우저가 해당 요청을 받고 나서 실제 네트워크 요청을 하게 된다.
이 순간 콜스택에서 브라우저에게 네트워크 요청을 등록하는 과정에 대해서 핸들링을 하게 되는데,
왜냐하면 브라우저에게 네트워크 요청을 했는데 브라우저에서 이 요청을 거부 할 수 있도 있어서이다.

그래서 비동기를 요청할 때 등록하는 콜백은 첫 번째 인자를 에러를 받는 형태로 암묵적인 룰을 갖고 있기도 한다.

정리하면 비동기 코드를 try catch로 감싸는 행위는 실제 작업을 처리하는 담당자에게 등록하는 과정에서 발생하는 에러를 핸들링하며 실제 작업을 수행하는 것에 대해서는 콜백의 에러 인자를 받아 처리하도록 한다.
물론 Promise나 async/await에서도 try catch 사용이 가능하다. 가끔 이 부분에서 Promise를 사용하거나 async/awati을 사용하면 비동기적인 코드가 동기적으로 바뀐다고 생각하는 사람들이 있는데 이 부분에 대해서는 다음에 다루겠다.

정리를 해보면,

  1. 콜스택은 비동기 작업을 web api를 통해 함수를 함께 전달하는 콜백 함수를 함께 전달한다.
  2. 브라우저는 작업이 끝난 후 콜백을 특정 큐에 저장하는데 작업요청 타입에 따로 3개의 큐로 나누어 등록된다.
  3. 이벤트 루프는 각 큐를 돌면서 등록된 콜백을 콜스택에서 처리하도록 한다.

다음에는 위 사진에 있는 이벤트 큐 3가지에 대해 정리해보겠다.