Front-End/React

[React, JS] 비동기 동기 처리

Voyage_dev 2022. 8. 11. 14:13

동기 (Syncronous)와 비동기 (Asynchronous)는 프로그래밍 언어에서 중요한 개념이다

동기

  • 동기는 요청 후 응답을 받아야 다음 동작을 실행하는 방식을 말한다. 즉, 요청과 결과가 동시에 일어나기 때문에 코드가 직관적이고 이해하기 쉽다.
  • 코드가 짜여진 순서대로 실행된다.

비동기

  • 비동기는 요청을 보낸 후 응답과 관계없이 다음 동작을 실행하는 방식이다. 즉, 결과가 나올 때 까지 기다리지 않고 다른 일을 처리할 수 있기 때문에 코드나 순서는 이해하기 어려워도 자원 사용을 좀 더 효율적으로 할 수 있다.
  • 코드가 짜여진 순서대로 실행되지 않는다.

자바스크립트에서 동기와 비동기

자바스크립트는 단일 스레드 프로그래밍 언어로 단일 호출 스택이 있어 한 번에 하나의 일을 처리할 수 있다. 그러므로 자바스크립트는 동기 방식으로 진행이 된다. 하나의 호출 스택만 있기 때문에 하나의 함수를 처리하는데 매우 오랜 시간이 걸린다면 다음 실행해야할 함수에 지장을 줄 수 있다는 문제가 발생한다. 이 부분이 바로 비동기의 필요성이다. 단일 호출 스택을 가진 자바스크립트에서 비동기를 구현할 수 있게 하는 방법을 알아보자

해결방안

Callback 함수

  • callback 함수는 다른 함수의 인자로써 이용될때나 어떤 이벤트에 의해 호출되어질때 사용하는 함수로, 비동기 처리의 해결방안이 될 수 있지만, callback 함수 안에 callback 함수를 계속 중첩으로 사용하다 보면 코드의 가독성이 매우 나빠지고 로직변경도 어려운 콜백지옥에 빠질 수 있다.

Promise

  • 동기적으로 보이는, 비동기 처리 방식
  • 프로미스 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타낸다.
  • 프로미스를 사용할 때 알아야 하는 가장 기본적인 개념이 바로 프로미스의 상태이다. 여기서 상태란 프로미스의 처리 과정을 의미한다. new Promise()로 프로미스를 생성하고 종료될 때까지 3가지 상태를 갖는다
    • Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
    • Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
    • Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태

Pending (대기)

new Promise();
  • new Promise() 메서드를 호출하면 대기 상태가 된다
new Promise(function(resolve, reject) {
// ...
});
  • new Promise() 메서드를 호출할 때 콜백 함수를 선언할 수 있고, 콜백 함수의 인자는 resolve, reject이다.

Fulfilled (이행)

new Promise(function(resolve, reject) {
  resolve();
});
  • 여기서 콜백 함수의 인자 resolve를 실행하면 이행(Fulfilled) 상태가 된다

그리고 이행 상태가 되면 아래와 같이 then()을 이용하여 처리 결과 값을 받을 수 있다

function getData() {
  return new Promise(function(resolve, reject) {
    var data = 100;
    resolve(data);
  });
}

// resolve()의 결과 값 data를 resolvedData로 받음
getData().then(function(resolvedData) {
  console.log(resolvedData); // 100
});

Rejected (실패)

new Promise()로 프로미스 객체를 생성하면 콜백 함수 인자로 resolve와 reject를 사용할 수 있다고 했다. 여기서 reject를 아래와 같이 호출하면 실패(Rejected) 상태가 된다

new Promise(function(resolve, reject) {
  reject();
});

그리고, 실패 상태가 되면 실패한 이유(실패 처리의 결과 값)를 catch()로 받을 수 있다.

function getData() {
  return new Promise(function(resolve, reject) {
    reject(new Error("Request is failed"));
  });
}

// reject()의 결과 값 Error를 err에 받음
getData().then().catch(function(err) {
  console.log(err); // Error: Request is failed
});

Promise 함수를 이용해 비동기 처리를 해보면

function getData() {
  return new Promise(function(resolve, reject) {
    $.get('url', function(response) {
      if (response) {
        resolve(response);
      }
      reject(new Error("Request is failed"));
    });
  });
}

// 위 $.get() 호출 결과에 따라 'response' 또는 'Error' 출력
getData().then(function(data) {
  console.log(data); // response 값 출력
}).catch(function(err) {
  console.error(err); // Error 출력
});
  • 서버에서 제대로 응답을 받아오면 resolve() 메서드를 호출하고, 응답이 없으면 reject() 메서드를 호출한다. 호출된 메서드에 따라 then()이나 catch()로 분기하여 응답 결과 또는 오류를 출력할 수도 있다.

Async / Await

async/await 은 Promise를 더욱 쉽게 사용할 수 있도록 해 주는 문법이다.

사용하려면 함수의 앞부분에 async 키워드를 추가하고, 해당 함수 내부에서 Promise 앞 부분에 await 키워드를 사용한다.

이렇게 하면 Promise가 끝날 때까지 기다리고, 결과 값을 특정 변수에 담을 수 있다.

function getData(callback) {
  return new Promise((resolve)=> {
    $.get('url', (response)=> {
       resolve(response);
    });
  });
}

async function callData() {
    try {
       let result = await getData();// 프로미스 결과를 기다립니다.
	console.log(result)
    } catch(error) {
       console.log(error)
    }
}

프로미스 처리 흐름 - 출처: MDN

React 비동기 처리

이런 비동기 처리를 React에서 그나마 편하게 하기 위해 제공하는 훅이 있다.

useState와 useEffect이다.

 

useState : 변수의 상태를 추적해준다.

const [currentUser, setCurrentUser] = useState(초기에 지정할 내용)

위 코드는 currentUser라는 변수를 useState로 관리한다는 뜻이고 useState가 제공하는 핵심 기능은 추적의 기능을 사용할 수 있어 currentUser라는 변수의 내용이 변하면 자동으로 jsx가 재 렌더링 되어 변화된 변수가 화면에 반영된다.

React에서 이렇게 변수를 useState가 아닌

const currentUser = "hello"

이런 식으로 저장하게 되면 스크립트 내부에서 currentUser변수에 변화가 있어도 jsx는 변수의 변화를 감지하지 못하기 때문에 항상 초기 상태만 표시하게 될 것이다.

 

useEffect : 변수의 상태를 추적해 뭔가를 하게 해준다.

useEffect(() => {
    const load = async () => {
      try {
        if (user !== undefined) {
          setCurrentUser(user);
        }
      } catch (error) {
        console.log("하하");
      }
    };
    load();
    console.log("Hello");
  }, [user]);

useState와 비슷한 기능의 훅이다. useState는 변수를 추적해 변화가 생긴다면 재 렌더링 하는 기능이라면 useEffect는 변수를 추적해 변화가 생기면 어떠한 기능을 수행할 수 있게 해 준다.

 

 

 

출처 : 아래의 사이트들을 보면서 큰 공부 하였습니다

https://jihyee.tistory.com/9

https://chiefcoder.tistory.com/14

https://higher77.tistory.com/72

https://velog.io/@jiwon/Javascript는-동기일까-비동기일까

https://ljtaek2.tistory.com/142