[JS] 동기와 비동기의 차이점

jiny·2025년 1월 27일

기술 면접

목록 보기
42/78

🗣️ 자바스크립트에서 동기와 비동기의 차이에 대해 설명해주세요.

  • 의도: 비동기 프로그래밍에 대해 이해하고 있는지 확인하는 질문

  • 팁: 추가로 둘을 비교한 예시를 들어도 좋다.

  • 나의 답안

    동기한 작업이 끝난 후에 다음 작업이 실행되는 방식입니다.
    즉, 작업이 순차적으로 진행되기 때문에 코드 흐름을 예측하기는 쉽지만,
    하나의 작업이 오래 걸리면 전체 프로그램이 멈춰 있는 것처럼 느려지는 단점이 있습니다.
    예를 들어, 대용량 데이터를 처리하거나 네트워크 요청이 오래 걸릴 때 다른 코드가 블로킹(blocking)되는 현상이 발생합니다.

    반면, 비동기는 작업이 완료되길 기다리지 않고 다음 코드를 먼저 실행하는 방식입니다.
    비동기 작업은 백그라운드인 Web API 영역에서 수행되고, 완료되면 콜백 함수Promise, async/await을 통해 결과를 나중에 전달받습니다.
    이 방식 덕분에 자바스크립트는 싱글 스레드임에도 동시에 여러 일을 하는 것처럼 동작할 수 있습니다.

  • 주어진 답안 (모범 답안)

    자바스크립트에서 동기작업을 순서대로 실행하며, 한 작업이 끝나야 다음 작업을 실행하는 것을 뜻합니다.

    반면에 비동기 처리는 한 작업이 완료되기까지 기다리지 않고 바로 다음 작업을 실행할 수 있어 더 효율적으로 리소스를 활용할 수 있습니다.
    비동기 프로그래밍은 주로 네트워크 요청, 파일 IO와 같이 시간이 얼마나 걸릴지 알 수 없는 작업을 처리할 때 필요합니다.


📝 개념 정리

🌟 동기(Synchronous)

동기 방식은 코드가 작성된 순서대로 하나씩 실행된다.
이전 작업이 완료되지 않으면 다음 작업은 절대 시작되지 않는다.

  1. 동작 원리
    • 자바스크립트는 단일 스레드(single-threaded) 기반으로 동작한다.
      즉, 한 번에 하나의 작업만 실행할 수 있다.
    • 이 작업 흐름은 호출 스택(Call Stack)을 통해 관리된다.
  1. 장점
    • 실행 흐름이 직관적이며 이해하기 쉽다.
    • 코드가 순차적으로 실행되므로 디버깅이 간단하다.
  1. 단점
    • 이전 작업이 오래 걸릴 경우, 다음 작업이 지연된다.
    • 특정 작업(예: 파일 읽기, 네트워크 요청 등)이 오래 걸리면 전체 프로그램이 멈추는 문제가 발생한다.
  1. 예시
    console.log("1"); // 첫 번째 출력
    console.log("2"); // 두 번째 출력
    console.log("3"); // 세 번째 출력
    출력 결과
    1
    2
    3

🌟 비동기(Asynchronous)

비동기 방식은 작업을 시작한 뒤, 작업이 완료되기를 기다리지 않고 다음 작업을 바로 실행한다.
특정 작업이 완료되면 콜백(callback) 또는 프로미스(promise) 등을 통해 결과를 처리한다.

  1. 동작 원리
    • 비동기 작업은 자바스크립트의 이벤트 루프(Event Loop)태스크 큐(Task Queue)를 통해 관리된다.
    1. 호출 스택(Call Stack): 실행 중인 함수들을 관리한다.
    2. 태스크 큐(Task Queue): 비동기 작업이 완료되면 콜백 함수가 여기에 추가된다.
    3. 이벤트 루프(Event Loop): 호출 스택이 비어 있으면 태스크 큐에서 작업을 가져와 실행한다.
  1. 비동기 작업의 종류
    • 타이머: setTimeout, setInterval
    • DOM 이벤트 리스너: addEventListener
    • AJAX 요청: fetch, XMLHttpRequest
    • 파일 입출력: File API
    • Promise와 async/await
  1. 장점
    • 느린 작업(예: 데이터베이스 요청, 파일 읽기 등)이 전체 프로그램을 멈추지 않게 해준다.
    • CPU 효율성을 높일 수 있다.
  1. 단점
    • 코드의 흐름을 이해하기 어려워질 수 있다. (특히 콜백 중첩 문제(콜백 지옥))
    • 적절한 에러 핸들링과 동기화가 필요하다.
  1. 예시
    console.log("1"); // 동기 실행
    setTimeout(() => console.log("2"), 1000); // 비동기 실행, 1초 후 실행
    console.log("3"); // 동기 실행
    출력 결과
    1
    3
    2
    • console.log("1")console.log("3")은 호출 스택에서 바로 실행된다.
    • setTimeout의 콜백은 1초 뒤 태스크 큐에 들어가며, 호출 스택이 비어 있는 상태에서 실행된다.

🌟 콜백 지옥(Callback Hell)

비동기 작업이 중첩되면서 발생하는 코드의 복잡성을 말한다.

  1. 예시

    setTimeout(() => {
      console.log("1");
      setTimeout(() => {
        console.log("2");
        setTimeout(() => {
          console.log("3");
        }, 1000);
      }, 1000);
    }, 1000);
  2. 문제점

    • 코드의 가독성이 낮아지고 유지보수가 어려워진다.
    • 에러 처리가 복잡해질 수 있다.

🌟 Promise

동기 작업을 보다 체계적으로 관리하기 위해 ES6(ES2015)에서 도입된 객체이다.

  1. 특징
    • 3가지 상태를 가진다.
      1) Pending(대기): 초기 상태, 비동기 작업이 진행 중
      2) Fulfilled(성공): 작업이 성공적으로 완료됨
      3) Rejected(실패): 작업이 실패하거나 에러가 발생함
    • .then().catch()로 작업 결과를 처리한다.
  1. 예시

    const promise = new Promise((resolve, reject) => {
      setTimeout(() => resolve("Success!"), 1000);
    });
    
    promise
      .then((result) => {
        console.log(result); // "Success!"
      })
      .catch((error) => {
        console.error(error);
      });

🌟 async/await

ES2017에서 도입된 문법으로, 비동기 작업을 동기적인 방식으로 작성할 수 있게 한다.
Promise 기반이며, 비동기 작업의 가독성을 높인다.

  1. 특징
    • async 키워드를 함수 앞에 사용하면 해당 함수는 항상 Promise를 반환한다.
    • await 키워드는 Promise가 처리될 때까지 기다린다.
  1. 예시

    const fetchData = async () => {
      try {
        const response = await fetch("https://api.example.com/data");
        const data = await response.json();
        console.log(data);
      } catch (error) {
        console.error("Error:", error);
      }
    };
    
    fetchData();

🌟 동기 vs. 비동기

  1. 동기
    function task1() {
      console.log("Task 1");
    }
    function task2() {
      console.log("Task 2");
    }
    task1();
    task2();
    출력 결과
    Task 1
    Task 2
  1. 비동기
    function task1() {
      console.log("Task 1");
    }
    function task2() {
      setTimeout(() => console.log("Task 2"), 1000);
    }
    task1();
    task2();
    출력 결과
    Task 1
    Task 2
    • "Task 2"는 1초 후에 출력된다.

🌟 이벤트 루프와 비동기 실행 흐름

  • 주요 흐름
    1. 호출 스택(Call Stack): 실행 중인 동기 코드가 쌓이고 제거되는 공간
    2. 태스크 큐(Task Queue): 비동기 작업의 콜백이 완료 후 대기하는 공간
    3. 이벤트 루프(Event Loop): 호출 스택이 비어 있는지 확인하고 태스크 큐에서 작업을 꺼냄
  • 그림으로 이해하기
    1. 동기 코드 → 호출 스택 실행
    2. 비동기 작업 → Web APIs로 이동 후 대기
    3. 작업 완료 → 태스크 큐로 이동
    4. 호출 스택이 비면 이벤트 루프가 태스크 큐에서 작업 실행

🌟 결론

  • 동기: 작성된 순서대로 실행, 블로킹(blocking)
  • 비동기: 작성 순서와 다르게 실행, 논블로킹(non-blocking)
  • 자바스크립트는 비동기 작업을 효과적으로 처리하기 위해 이벤트 루프와 태스크 큐를 활용한다.
  • 비동기 코드는 Promiseasync/await을 사용해 가독성을 높일 수 있다.

0개의 댓글