몸과 마음이 건전한 SW 개발자

[CS 지식 - 프론트엔드] 비동기와 동기 본문

CS 지식/프론트엔드 용어

[CS 지식 - 프론트엔드] 비동기와 동기

스위태니 2024. 9. 26. 15:14

오늘 면접 보는데 가장 기본적인 동기와 비동기에 대해서 설명하지 못했다. 심지어 반대로 설명함;; 비동기를 순차적으로 사용한다고 했는데... 내가 프로젝트 때 사용한 기술과 용어에 대해서 무지하여 이번 면접을 계기로 공부해볼 예정이다.

 주의 ※
이 블로그는 어디까지나 CS관련 지식을 정리하는 것이 목적입니다. 제가 이해한 내용이 잘못 된 것 같다면 댓글로 남겨주세요. 여러분의 관심이 저의 지식 함양에 도움이 됩니다.

 < 목차 > 

  1. 동기란 무엇인가?
  2. 비동기는...?
  3. promise, async, await?
  4. 언제 사용하지?
  5. 한 줄 정리

>> 동기란 무엇인가? 

  • 영어로  Synchronous
    • existing or occurring at the same time.
    • 쉽게 말해서 동시에 존재하거나 동작한다는 말이다.
  • 그러면 동시에 동작한다는 말은 어떤 것을 의미할까?
    • 코드가 위에서부터 아래로 순차적으로 진행됨을 말한다.
    • 짧게 코드로 보면
async function synchronous() {
    // A 실행
    console.log("A");
    // B 실행
    console.log("B");
    // C 실행
    console.log("C");
}
synchronous();

// 프로그램 출력
// A
// B
// C
  • synchronous 함수를 실행하면 당연히 A, B, C가 순서대로 출력된다.
  • 이미 우리가 알고 있는 코드는 위에서 부터 아래로 작동하는 원리가 동기다.
  • 또한 A가 나오기 전에 B가 나오지 않는다.
  • 다시 말해서 A가 실행되고 작업이 완료되면 B가 실행된다는 말이다.

>> 비동기는...? 

  • 비동기는 먼저 setTimeout함수 => 비동기 함수를 바탕으로 이해해보자.
  • 위에서 부터 아래로 실행 되는 것은 똑같다.
  • 하지만 A, B, C는 차례대로 나오지만 A함수가 끝나기 전에 B함수와 C함수가 실행되기 때문에 C-1, B-1, A-1 순서로 출력되게 된다.
  • 코드와 같이 살펴보자.
function asynchronous() {
    function A() {
        console.log("A");
        setTimeout(function() {
            console.log("A-1");
        }, 3000);
    }
    function B() {
        console.log("B");
        setTimeout(function() {
            console.log("B-1");
        }, 2000);
    }
    function C() {
        console.log("C");
        setTimeout(function() {
            console.log("C-1");
        }, 1000);
    }
    A();
    B();
    C();
}
asynchronous();

// 프로그램 출력
// A
// B
// C
// C-1
// B-1
// A-1
  • A-1, B-1, C-1로 나오지 않는 이유는 setTimeout이 비동기 함수이기 때문이다.
  • 그러면 이것을 동기식으로 바꿔서 함수 A가 완료되면 B가 실행되고 B가 완료되면 C가 실행되게 해보자.
  • promise와 async/await를 사용해야 한다.
  • 이건 밑에서 확인해보자.

>> promise, async, await? 

async function synchronous() {
    function A() {
        console.log("A");
        return new Promise(resolve => {
            setTimeout(function() {
                console.log("A-1");
                resolve();
            }, 3000);
        });
    }
    function B() {
        console.log("B");
        return new Promise(resolve => {
            setTimeout(function() {
                console.log("B-1");
                resolve();
            }, 2000);
        });
    }
    function C() {
        console.log("C");
        return new Promise(resolve => {
            setTimeout(function() {
                console.log("C-1");
                resolve();
            }, 1000);
        });
    }
    await A();
    await B();
    await C();
}
synchronous();

// 프로그램 출력
// A
// A-1
// B
// B-1
// C
// C-1
  • 실행 순서:
    • synchronous() 함수가 호출되고, 함수 내부에서 await A();가 실행
    • 함수 A()가 호출되고, "A"를 출력한 후 setTimeout을 통해 3초 후에 "A-1"을 출력하고 resolve()를 호출하는 Promise를 반환
      • await A();가 완료되었으므로 다음으로 await B();가 실행
    • 함수 B()가 호출되고, "B"를 출력한 후 2초 후에 "B-1"을 출력하고 resolve()를 호출하는 Promise를 반환
      • await B();가 완료되었으므로 다음으로 await C();가 실행
    • 함수 C()가 호출되고, "C"를 출력한 후 1초 후에 "C-1"을 출력하고 resolve()를 호출하는 Promise를 반환
    • 모든 함수 호출이 완료되어 synchronous() 함수의 실행이 종료
  • Promise를 사용하는 이유:
    • JavaScript에서 비동기 작업의 완료를 기다리기 위해서는 Promise를 사용해야 한다.
    • Promise를 반환하고 resolve()를 호출함으로써 비동기 작업이 완료되었음을 알린다.
  • 요약
    • await는 Promise를 반환하는 함수에 대해서만 의미가 있다.
    • 각 함수에서 Promise를 반환하도록 수정하면, await를 사용하여 비동기 작업이 완료될 때까지 기다릴 수 있다.
    • 이렇게 하면 A(), B(), C() 함수의 실행이 순차적으로 이루어지며, 각 함수의 비동기 작업이 완료된 후 다음 함수가 실행된다.

>> 시간에서 차이가 난다. 

  • synchronous() 함수 (동기 함수)
    • A가 실행 되고 => A 출력 =>
    • 3초 후 => A -1 출력 => A 함수 완료(종료)
    • B가 실행 되고 => B 출력  =>
    • 2초 후 => B-1 출력 => B 함수 완료(종료)
    • C가 실행 되고 => C 출력 =>
    • 1초 후 => C-1 출력 => C함수 완료(종료)
    • 약 6초가 소요된다.
  • asynchronous() 함수 (비동기 함수)
    • A가 실행되고 => A 출력 =>
    • A가 완료 되기 전에 => B가 실행되고 => B 출력 =>
    • A가 완료 되기 전에 => C가 실행되고 => C 출력 =>
    • 1초 후 => C-1 출력 => C 함수 완료(종료)
    • 1초 후 => B-1 출력 => B 함수 완료(종료)
    • 1초 후 => A-1 출력 => A 함수 완료(종료)
    • 약 3초가 소요된다. 

>> 언제 사용하지? 

  • 언제 동기를 사용할까?
    • 작업 순서가 중요할 때: 이전 작업이 완료된 후에 다음 작업을 수행해야 할 경우.
    • 간단한 연산이나 빠른 처리: 실행 시간이 짧아 프로그램 흐름을 방해하지 않는 작업.
    • 코드의 예측 가능성: 코드가 작성된 순서대로 실행되어 디버깅과 유지보수가 쉬울 때.
  • 언제 비동기를 사용할까?
    • 시간이 오래 걸리는 작업: 파일 입출력, 네트워크 요청 등 대기 시간이 필요한 작업.
    • 동시에 여러 작업을 처리해야 할 때: 사용자 인터페이스 응답성을 유지하면서 백그라운드에서 작업을 처리해야 할 경우.
    • 성능 향상: 병렬 처리를 통해 전체 실행 시간을 단축하고자 할 때.

>> 한 줄 정리 

  • 동기는 순차적 처리가 필요할 때 사용하고, 비동기는 동시에 여러 작업을 효율적으로 처리해야 할 때 사용한다.