JavaScript(JS) - 비동기

조성주·2023년 3월 23일
1

JavaScript

목록 보기
17/21
post-thumbnail

웹 개발을 하면서 비동기는 꼭 알아야한다고 생각한다.

이런 생각이 든 이유는 지금까지 개발을 하면서 비동기 처리를 많이 했었는데 예를 들면, 게시판이 아마 대표적인 예시이지 않을까 싶다. 비동기처리는 JQuery에 Ajax로만 사용했었는데 강의를 들으면서 여러가지 비동기 처리 방법이 있다는 것을 알게 되었다.

동기와 비동기가 무엇인지 알아보자 !

❓ 동기와 비동기

  • 동기(Synchronous : 동시에 일어나는)

    • 동기란 특정 코드가 끝나야 그 다음 코드를 실행하는 것을 의미한다.
    • 서버에 요청을 보냈을 때 응답이 돌아와야 다음 동작을 수행할 수 있다.
    • 동기는 동작이 끝나야 그 다음 동작을 실행한다.
  • 비동기(Asnchronous : 동시에 일어나지 않는)

    • 비동기란 특정 코드가 끝날때 까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 것을 의미한다.
    • 요청을 보냈을 때 응답 상태에 상관 없이 다음 동작을 수행할 수 있다.
    • 비동기는 상관 없이 동작을 수행한다.
    • 웹 페이지를 리로드하지 않고 데이터를 불러오는 방식이다.

console.log(1);
console.log(2);
console.log(3);

이렇게 되는 경우 1이 출력이 되어야 다음 2가 출력이 되고 다음 3이 출력이 된다. 이렇게 한 동작이 끝나야 실행이 된다. 이것을 동기 라고 한다.

반면에, 비동기는

console.log(1);
setTimeout(() => {
	console.log(2);
}, 2000);
console.log(3);

먼저 1이 출력이 되고 그 다음 3이 출력되고 2초가 지난 후 2가 출력이 된다. 이렇게 코드 순서에 상관 없이 동작하는 것을 비동기 라고 한다.

❓ 왜 비동기처리를 사용해야 할까?

웹을 개발한다고 하면 비동기 처리는 꼭 사용해야한다고 생각한다. 그 이유는 예를 들어 카테고리 별 상품을 보여주는 페이지를 본다고 했을 때, 내가 현재 보고 있는 카테고리 상품에서 다른 카테고리의 상품을 보기 위해 카테고리를 클릭했을 때 상품 목록만 바뀌어 화면에 보여주게 하면 된다. 하지만 비동기처리를 하지 않는다면 웹페이지를 다시 리로드를 해야 한다. 이렇게 리로드를 계속 하게 되면 자원낭비와 시간낭비를 초래한다.

❓ 동기, 비동기의 장 / 단점

  • 동기
    • 장점 : 설계가 간단하고 직관적이다.
    • 단점 : 코드가 끝나야 그 다음 코드를 실행하기 때문에 코드가 끝날때까지 대기해야한다.
  • 비동기
    • 장점 : 코드의 결과를 기다리지 않고 다른 코드 실행이 가능하다.
    • 단점 : 설계가 동기보다 복잡하다.

❓ 비동기 처리방법

1. 콜백함수(Callback Function)

1) 콜백함수란?

  • 어떤 함수를 호출할 때, 인자값으로 넘기는 나중에 실행될 함수이다.
  • 콜백 함수가 실행되는 시점은 콜백함수를 인자값으로 넘겨받은 함수 구현에 따라 결정한다.

2) 콜백함수의 용도

  • javascript의 비동기 작업을 처리하기 위한 방법이다.
  • 주로 어떤 작업이 끝난 이후, 그 결과 값을 가지고 뒤에 이어서 처리되어야 하는 로직을 담는다.
    ex) 성공 또는 실패 핸들러

3)콜백함수 사용 방법

const func = (callbackFunc) => {
	const value = 1 + 1;
	callbackFunc(value);
}


const callbackFunc = (parameter) => {
	console.log(`1 + 1은 ${parameter}입니다.`);
}

func(callbackFunc);
  1. callbackFunc에 파라미터 값으로 callbackFunc을 넘겨 func 함수를 실행한다.
  2. value에 1 + 1한 값을 저장하고 받은 callbackFunc 함수에 파라미터값으로 value를 넘겨 실행한다.
  3. console.log로 1 + 1은 2입니다. 를 출력한다.

이렇게 콜백함수를 사용할 수 있다. 하지만 콜백함수를 사용하면서 주의해야하는 점이 있다. 바로 콜백지옥이다.
콜백지옥은 콜백함수가 반복되는 것을 의미한다.

step1(function (err, value1) {
    if (err) {
        console.log(err);
        return;
    }
    step2(function (err, value2) {
        if (err) {
            console.log(err);
            return;
        }
        step3(function (err, value3) {
            if (err) {
                console.log(err);
                return;
            }
            step4(function (err, value4) {
              // ...
            });
        });
    });
});

이렇게 콜백함수에 콜백함수에 콜백함수를 더하면 콜백지옥이 된다. 따라서, 이런 콜백지옥을 개선하기 위해 만들어진 객체가 있다. 바로 promise이다.

2. promise

  • promise는 비동기 실행 결과를 반환하는 객체이다.
  • js 비동기 작업의 최종적인 실패의 제어흐름을 알아보기 쉽게 표현해주는 객체이다.
  • 비동기 로직 구현 시, 콜백함수를 활용하는 방식보다 더 편리하다.
  • promise는 콜백지옥을 해결하기 위해 생겼다.
  • ECMA Script 2015 (=ES6)에 도입이 되었다.

promise에는 3개의 상태가 있다.

  • pending = 대기 상태
  • fullfill = 완료 - 성공
  • reject = 완료 - 실패

promise 생성 및 사용 구조

let myPromise = new Promise(function(resolve, reject) {
  // 성공 시 resolve()를 호출하여 결과값을 전달
  // 실패 시 reject()를 호출하여 에러를 전달
});

myPromise.then(function(result) {
	//결과값 활용하여 성공처리
}).catch(function(err){
	// 에러 객체 활용하여 실패 처리
}).finally(function()){
    // 공통 마무리 작업
})

// 화살표 함수로 작성
let myPromise = new Promise((resolve, reject) => {
  // 코드 작성
});

myPromise.then((result) => {
	//결과값 활용하여 성공처리
}).catch((err) => {
	// 에러 객체 활용하여 실패 처리
}).finally(() => {
    // 공통 마무리 작업
})

promise 사용 예제

let value = 20;

let promise = new Promise((resolve, reject) => {
  value > 10 ? resolve(value) : reject(value);
});

promise
  .then((result) => {
    console.log(result);
    console.log(`${result}는 10보다 큽니다.`);
  })
  .catch((err) => {
    console.log(`${result}는 10보다 작거나 같습니다.`);
  })
  .finally(() => console.log("종료"));

promise 활용

  • fetch는 promise 기반으로 구성되어 있다.
  • fetch는 promise 객체를 반환하기 때문에 promise처럼 사용할 수 있다.
fetch("https://yts.lt/api/v2/list_movies.json")
.then((response) => console.log(response))
.catch((error) => console.error(error))

이렇게 promise로 콜백함수보다 더 간편하게 사용할 수 있다.

3. async / await

  • js의 비동기 로직 처리 패턴 중 가장 인간이 알아보기 쉬운 형태이다.
  • async / await는 가독성과 개발편의성이 증가하였다.
  • 일반적으로 코드를 위에서 아래로 읽으며 실행 순서를 파악하는 점을 감안하여 만든 기능이다.
  • ECMA Script 2017에 도입하였다.

callback → promise → async / await

async

  • 비동기 로직을 포함하는 함수 앞에 붙인다.
  • 함수가 Promise 객체를 리턴하게 만든다.
  • 함수 내부에서 await 키워드 사용이 가능하게 된다.

await

  • async 키워드를 붙인 함수 안에서만 사용 가능하다.
  • Promise 객체를 리턴하는 함수 호출 코드 앞쪽에만 붙일 수 있다.
  • Promise의 then() 부분에서 전달받던 성공 시 결과값을 곧바로 얻을 수 있다.

성공 시 resolve()로 작성해야했는데 await를 붙이면 바로 결과를 가져온다.
즉, promise로 복잡했던 구조를 더 간편하게 작성할 수 있도록 개선되었다.

async / await 사용방법

Promise 코드로 비동기 처리를 했을 때는 아래 코드 처럼 작성을 했지만

const func = () => {
	return new Promise((resolve, reject) => {
    	resolove("hello")
      // or reject(new Error("error"))
    });
};

func.then((result) => console.log(result));

이렇게 함수앞에 async를 붙이게 되면 promise 객체를 리턴하게 되기 때문에 코드를 더 간편하게 읽기 쉽게 작성할 수 있다.

const func = async () => {
  	return "hello";
}

func().then((result) => console.log(result));

예외처리 방법

promise 객체를 사용하기 때문에 .then / .catch를 사용하여 예외처리를 할 수 있다.

const func = async() => { 
  throw 'error';
  //throw new Error("error");
  //await Promise.reject(new Error("error"));
  //return Promise.reject(new Error("error"));
}

func()
  .then((n) => console.log(n))
  .catch((n) => console.log(n));

이렇게 처리할 수 있지만 async / await는 try/catch 문을 사용하여 예외처리를 할 수 있다.

const test = async () => {
	try{
      let response = await fetch("url");
    }catch(error){
    	console.log(error)
    }  
}

promise를 통한 비동기와 async / await를 통한 비동기의 코드 차이

  • promise를 사용한 비동기 코드
function fetchAuthorName(postId) {
  return fetch(`https://jsonplaceholder.typicode.com/posts/${postId}`)
    .then((response) => response.json())
    .then((post) => post.userId)
    .then((userId) => {
      return fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
        .then((response) => response.json())
        .then((user) => user.name);
    });
}

fetchAuthorName(1).then((name) => console.log("name:", name));
  • async / await를 사용한 비동기 코드
const func = async (postId) => {
  try {
    const postResponse = await fetch(
    	`https://jsonplaceholder.typicode.com/posts/${postId}`
  	);
  	const post = await postResponse.json();
  	const userId = post.userId;
    const userResponse = await fetch(
      `https://jsonplaceholder.typicode.com/users/${userId}`
    );
    const user = await userResponse.json();
    return user.name;
  } catch (err) {
    console.log("Fail to fetch user:", err);
    return "Unknown";
  }
}

func(1)
  .then((name) => console.log("name:", name));

이렇게 되면 성공 했을 때는 name: name이 출력이 되고 실패를 했을 경우 Fail to fetch username : Unknown이 출력이 된다.

참고사이트 : https://www.daleseo.com/js-async-async-await/

profile
프론트엔드 개발자가 되기 위한 기록

0개의 댓글