군만두의 IT 개발 일지

[DAY7] JS/Node 비동기 본문

개발일지/SW직무역량 부트캠프

[DAY7] JS/Node 비동기

mandus 2023. 7. 7. 14:24

목차

    🚀 진행기간: 2023년 6월 26일 ~ 2023년 7월 13일

    ⭐요약


    JavaScript의 동기/비동기 개념, 비동기 제어 방법(Callback, Promise, Async/Await), Node.js 모듈 시스템, fetch API와 Axios를 이용한 네트워크 요청에 대해 정리한다.

    더보기

     

    동기 / 비동기 동기는 작업을 순차적으로 처리하고, 비동기는 이전 작업이 끝나지 않아도 다음 작업을 바로 실행함.
    JavaScript는 싱글 스레드 기반이지만, 런타임 환경에서 비동기 처리를 지원함.
    타이머 API setTimeout은 일정 시간 후 한 번 실행하고, setInterval은 일정 간격으로 반복 실행함.
    clearTimeout/clearInterval로 각각 종료할 수 있음.
    Callback / Promise Callback은 비동기 제어가 가능하나 중첩이 깊어지면 Callback Hell이 발생함.
    Promise는 .then(), .catch(), .finally()로 비동기 흐름을 제어하며 chaining이 가능함.
    Async / Await 복잡한 Promise 코드를 간결하게 작성할 수 있음.
    async 함수 내에서 await 키워드로 Promise 작업이 끝날 때까지 실행을 일시 정지함.
    Node.js 모듈 require로 내장 모듈 또는 서드 파티 모듈을 불러올 수 있음.
    서드 파티 모듈은 npm install로 먼저 설치해야 함.
    fetch API / Axios fetch는 브라우저 내장 API로 특정 URL로부터 비동기적으로 데이터를 받아옴.
    Axios는 Promise 기반의 HTTP 통신 라이브러리로 fetch보다 더 많은 기능을 제공함.

     

    ⭐비동기(Asynchronous)


    1. 동기와 비동기

    🚀 동기(Synchronous)

    • 동기: 특정 코드의 실행이 완료될 때까지 기다린 후 다음 코드를 수행하는 방식이다.
    장점 모든 작업이 순차적으로 이루어지므로 코드를 단순한 구조로 작성할 수 있고, 실행 흐름을 쉽게 예측할 수 있음
    단점 앞의 작업이 끝나기 전까지 다음 코드가 실행되지 않으므로 이후 작업들이 지연됨

     

    🚀 비동기(Asynchronous)

    • 비동기: 특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드를 수행하는 방식이다.
    장점 앞의 작업이 끝나지 않아도 바로 다음 작업을 실행할 수 있어 효율적임
    단점 코드가 예측한 대로 실행되지 않을 수 있으므로, Callback이나 Promise 등으로 순서를 제어해야 함

     

    JavaScript는 싱글 스레드 기반으로 동작하는 언어로 기본적으로는 동기적으로 작동한다. 그러나 JavaScript가 실행되는 런타임 환경(브라우저, Node.js)에서 비동기 처리를 지원한다.

    2. 타이머 관련 API

    🚀 setTimeout(callback, millisecond): 일정 시간 후에 함수를 한 번 실행한다.

    • 매개변수: 실행할 콜백 함수, 콜백 함수 실행 전 기다려야 할 시간 (밀리초)
    • return 값: 임의의 타이머 ID
    setTimeout(function () {
      console.log('1초 후 실행');
    }, 1000);
    // 123

    🚀 clearTimeout(timerId): setTimeout 타이머를 종료한다.

    • 매개변수: 타이머 ID
    • return 값: 없음
    const timer = setTimeout(function () {
      console.log('10초 후 실행');
    }, 10000);
    clearTimeout(timer);
    // setTimeout이 종료됨.

    🚀 setInterval(callback, millisecond): 일정 시간 간격으로 함수를 반복적으로 실행한다.

    • 매개변수: 실행할 콜백 함수, 반복적으로 함수를 실행시키기 위한 시간 간격 (밀리초)
    • return 값: 임의의 타이머 ID
    setInterval(function () {
      console.log('1초마다 실행');
    }, 1000);
    // 345

    🚀 clearInterval(timerId): setInterval 타이머를 종료한다.

    • 매개변수: 타이머 ID
    • return 값: 없음
    const timer = setInterval(function () {
      console.log('1초마다 실행');
    }, 1000);
    clearInterval(timer);
    // setInterval이 종료됨.

    3. Callback

    • Callback은 비동기로 작동하는 코드의 실행 순서를 제어할 수 있는 방법 중 하나이다.
    // 터미널에 `node index.js`를 입력하여 비동기 코드가 작동하는 순서를 확인해보세요.
    const printString = (string, callback) => {
      setTimeout(function () {
        console.log(string);
        callback();
      }, Math.floor(Math.random() * 100) + 1);
    };
    
    const printAll = () => {
      printString('A', () => {
        printString('B', () => {
          printString('C', () => {
            printString('D', () => {
              printString('E', () => {
                printString('F', () => {
                  printString('G', () => {
                    printString('H', () => {
                      printString('I', () => {
                        printString('J', () => {
                          printString('K', () => {
                            printString('L', () => {
                              printString('M', () => {
                                printString('N', () => {
                                  printString('O', () => {
                                    printString('P', () => {});
                                  });
                                });
                              });
                            });
                          });
                        });
                      });
                    });
                  });
                });
              });
            });
          });
        });
      });
    };
    
    printAll();
    
    console.log(
      `아래와 같이 Callback 함수를 통해 비동기 코드의 순서를 제어할 수 있지만 코드가 길어질 수록 복잡해지고 가독성이 낮아지는 Callback Hell이 발생하는 단점이 있습니다.`
    );

    4. Promise

    • Promise: 비동기 코드를 제어할 수 있는 방법 중 하나로, Callback Hell을 방지하는 역할을 한다.
    • new Promise로 Promise 객체를 생성하며, 비동기 처리를 위한 콜백 함수를 인수로 전달받는다. 콜백 함수는 (resolve, reject)를 인수로 전달받는다.
    • resolve: 작업이 성공했을 때 실행하는 함수로, .then()을 이용해 결과값을 전달한다.
    • reject: 작업이 실패했을 때 실행하는 함수로, .catch()를 이용해 에러를 전달한다.
    let promise = new Promise((resolve, reject) => {
        // 1. 정상적으로 처리되는 경우
        // resolve의 인자에 값을 전달할 수도 있습니다.
        resolve(value);
    
        // 2. 에러가 발생하는 경우
        // reject의 인자에 에러메세지를 전달할 수도 있습니다.
        reject(error);
    });

    🚀 Promise 객체의 내부 프로퍼티: stateresult 내부 프로퍼티를 가진다. 직접 접근할 수 없으며 .then, .catch, .finally 메서드를 사용해야 접근할 수 있다.

    프로퍼티 초기값 resolve 호출 시 reject 호출 시
    state pending (대기) fulfilled (이행) rejected (거부)
    result undefined resolve(value)의 value reject(error)의 error

    🚀 .then(): 작업이 성공했을 때 결과값을 받아 처리한다. 리턴한 값이 Promise이면 그 결과값을 다음 .then()의 인자로 전달한다.

    let promise = new Promise((resolve, reject) => {
        resolve("성공");
    });
    
    promise.then(value => {
        console.log(value);
        // "성공"
    })

    🚀 .catch(): 에러가 발생했을 때 reject 함수가 호출되며, .catch() 메서드로 에러를 처리한다.

    let promise = new Promise(function(resolve, reject) {
        reject(new Error("에러"))
    });
    
    promise.catch(error => {
        console.log(error);
        // Error: 에러
    })

    🚀 .finally(): 성공·실패 여부와 관계없이 무조건 실행된다.

    let promise = new Promise(function(resolve, reject) {
        resolve("성공");
    });
    
    promise
    .then(value => {
        console.log(value);
        // "성공"
    })
    .catch(error => {
        console.log(error);
    })
    .finally(() => {
        console.log("성공이든 실패든 작동!");
        // "성공이든 실패든 작동!"
    })

    🚀 Promise chaining: 비동기 작업을 순차적으로 진행해야 하는 경우 .then()을 연달아 연결하여 사용한다.

    let promise = new Promise(function(resolve, reject) {
        resolve('성공');
    });
    
    promise
      .then((value) => {
        console.log(value);
        return '성공';
      })
      .then((value) => {
        console.log(value);
        return '성공';
      })
      .then((value) => {
        console.log(value);
        return '성공';
      })
      .catch((error) => {
        console.log(error);
        return '실패';
      })
      .finally(() => {
        console.log('성공이든 실패든 작동!');
      });

    🚀 Promise.all(): 여러 개의 비동기 작업을 동시에 처리하고 싶을 때 사용한다. 배열 안의 Promise 중 하나라도 에러가 발생하면 즉시 종료된다.

    // 기존 방식
    const result = [];
    promiseOne()
      .then(value => {
        result.push(value);
        return promiseTwo();
      })
      .then(value => {
        result.push(value);
        return promiseThree();
      })
      .then(value => {
        result.push(value);
        console.log(result);
        // ['1초', '2초', '3초']
      })
    // Promise.all() 사용
    Promise.all([promiseOne(), promiseTwo(), promiseThree()])
      .then((value) => console.log(value))
      // ['1초', '2초', '3초']
      .catch((err) => console.log(err));

    ⚠️ Promise Hell: Promise를 통해 비동기 코드의 순서를 제어할 수 있지만, .then()이 깊이 중첩되면 Callback Hell과 유사하게 코드가 복잡해지고 가독성이 낮아지는 Promise Hell이 발생할 수 있다.

    5. Async / Await

    • Async/Await: 복잡한 Promise 코드를 더 간결하게 작성할 수 있도록 해준다.
    • 함수 앞에 async 키워드를 사용하고, async 함수 내에서만 await 키워드를 사용할 수 있다.
    • await 키워드가 작성된 코드가 동작을 완료하고 나서야 다음 순서의 코드가 동작한다.

    🚀 async 함수의 실행 흐름

    • await를 만나면 async 함수의 실행을 잠시 멈춘다.
    • Promise 작업이 끝나길 기다린다.
    • 작업이 완료되면 async 함수를 다시 실행한다.
    // 함수 선언식
    async function funcDeclarations() {
        await 작성하고자 하는 코드
        ...
    }
    
    // 함수 표현식
    const funcExpression = async function () {
        await 작성하고자 하는 코드
        ...
    }
    
    // 화살표 함수
    const ArrowFunc = async () => {
        await 작성하고자 하는 코드
        ...
    }

    ⭐Node.js


    1. Node.js 모듈 사용법

    🚀 require: JavaScript 코드 상단에 require 구문을 이용하여 다른 파일이나 모듈을 불러온다.

    const fs = require('fs');  // 파일 시스템 모듈을 불러옵니다
    const dns = require('dns'); // DNS 모듈을 불러옵니다
    
    // 이제 fs.readFile 메서드 등을 사용할 수 있습니다!

    🚀 서드 파티(3rd-party) 모듈: 해당 프로그래밍 언어에서 공식적으로 제공하는 빌트인 모듈(built-in module)이 아닌 모든 외부 모듈을 말한다. 사용 전에 반드시 npm install로 설치해야 한다.

    npm install underscore

    설치 후에는 내장 모듈과 동일하게 require 구문으로 불러와 사용할 수 있다.

    const _ = require('underscore');

    ⭐fetch API


    1. fetch를 이용한 네트워크 요청

    • fetch API: 특정 URL로부터 정보를 비동기적으로 받아오는 브라우저 내장 API이다.
    • fetch()는 Promise를 반환하므로 .then(), .catch()로 결과를 처리한다.
    let url = "https://koreanjson.com/posts/1";
    fetch(url)
      .then((response) => response.json())
      .then((json) => console.log(json))
      .catch((error) => console.log(error));

    [코드] 개발자 도구의 콘솔에서 fetch API를 사용하여 데이터를 요청하는 예시

    function getNewsAndWeather() {
      return fetch(newsURL)
      .then((response) => response.json())
      .then((news) => {
        return fetch(weatherURL)
        .then((res) => res.json())
        .then((weather) => {
          return {news: news.data, weather}
        })
      })
    }

    [코드] fetch를 중첩하여 두 개의 URL에서 데이터를 순차적으로 받아오는 예시

    2. Axios

    • Axios: 브라우저와 Node.js 환경 모두에서 사용 가능한 Promise 기반의 HTTP 비동기 통신 라이브러리이다.
    • Axios는 서드 파티 라이브러리이므로 사용 전에 반드시 설치가 필요하다.
    npm install axios

    🚀 Axios 주요 메서드

    메서드 용도 사용법
    GET 정보를 요청할 때 사용함 axios.get("url"[, config])
    POST 서버에 데이터를 보낼 때 사용함 axios.post("url"[, data[, config]])

    ⭐후기


    • 동기와 비동기의 차이를 단순히 개념으로만 알고 있었는데, 타이머 API 예시를 통해 실제로 코드가 어떤 순서로 실행되는지 체감할 수 있었다.
    • fetch와 Axios의 차이를 정리하면서, 자동 JSON 변환과 에러 처리가 편리한 Axios를 많이 선호한다는 것을 알게 되었다.

    ⭐참고자료


    1) MDN Web Docs, "Using Promises", https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Using_promises

    2) MDN Web Docs, "async function", https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/async_function

    3) MDN Web Docs, "Fetch API", https://developer.mozilla.org/ko/docs/Web/API/Fetch_API

    4) Axios 공식 문서, https://axios-http.com/kr/docs/intro

     

    이 글은 코드스테이츠 x KNU가 주최한 직무역량캠프에서 공부한 내용을 작성한 것입니다.

    '개발일지 > SW직무역량 부트캠프' 카테고리의 다른 글

    [DAY9] React State & Props  (0) 2023.07.10
    [DAY8] React SPA  (0) 2023.07.10
    [DAY6] JS/브라우저 DOM  (0) 2023.07.07
    [DAY6] JavaScript 고차 함수  (0) 2023.07.06
    [DAY 5] JavaScript 핵심 개념과 주요 문법  (0) 2023.07.06
    Comments