JS 비동기 처리방식 - Callback

yurim·2020년 5월 13일
1
post-thumbnail

동기 / 비동기 처리의 비교

동기와 비동기를 나누는 가장 큰 차이점은 어떻게 실행 순서를 가지는 지에 있다.
동기(Syncronous)는 요청을 보낸 후 해당 요청의 응답을 받아야 다음 동작을 실행하는 방식을, 비동기(Asyncronous)는 요청을 보낸 후 응답과 관계없이 다음 동작을 실행할 수 있는 방식을 의미한다.

동기(Syncronous)는 먼저 시작한 작업이 끝날 때 까지 다른 작업을 할수없는 반면에, 비동기(Asyncronous)는 동시에 여러가지 작업을 처리하며 기다리는 과정에서 다른 함수 호출이 가능하다.

비동기 처리의 예

다음과 같은 작업들은 주로 비동기적으로 처리하게 된다.

  • Ajax Web API요청 : 만약 서버쪽에서 데이터를 받아와야 할 때는, 요청을 하고 서버에서 응답을 할 때 까지 대기를 해야 되기 때문에 작업을 비동기적으로 처리한다.
  • 마냥 다른 코드를 실행안하고 기다릴 순 없다.
  • 파일읽기 : 주로 서버 쪽에서 파일을 읽어야 하는 상황에는 비동기적으로 처리한다.
  • 암호화/복호화 : 암호화/복호화를 할 때에도 바로 처리가 되지 않고, 시간이 어느정도 걸리는 경우가 있기 때문에 비동기적으로 처리한다.
  • 작업예약 : 단순히 어떤 작업을 몇초 후에 스케쥴링 해야 하는 상황에는, setTimeout 을 사용하여 비동기적으로 처리한다.
    - setTimeout(a,b) : Web API의 한 종류로, 코드를 바로 실행하지 않고 지정한 시 간만큼 기다렸다가 로직을 실행한다.
    + b ms 후에 코드를 실행한다는 뜻인데, 사실은 (b+4)ms 후에 실행된다.

Single Thread로 작동하는 자바스크립트

자바스크립트는 Single Thread 언어이다. 즉, 자바스크립트는 한번에 하나의 작업밖에 수행하지 못한다.
또한 자바스크립트는 이벤트를 처리하는 Call Stack이 하나뿐인 언어이다.
따라서 여러가지 이벤트를 처리할 때에 동기적으로 처리하게 된다면 하나의 이벤트가 모두 처리될 때까지 다른 어떤 업무도 수행하지 못하는 현상이 일어나게 된다.

따라서 자바스크립트는 즉시 처리하지 못하는 이벤트들을 Web API로 보내 콜스택이 비었을 때에 먼저 처리된 이벤트들을 줄세워 다시 이벤트 큐에 줄을 세워놓게 된다.

Web API는 브라우저에서 제공되는 API이며, AJAX나 Timeout 등의 비동기 작업을 실행한다.

Web API로 들어오는 순서는 중요하지 않고, 어떤 이벤트가 먼저 처리되느냐가 중요하다. (실행 순서가 불명확한 비동기!!)

그런데 만약 비동기 처리 이벤트들의 순서가 중요해지게 된다면 어떻게 될까?
예를 들어서 서버에 로그인 사용자 아이디를 요청하는 비동기 처리 후 사용자의 아이디를 이용해 프로필 사진 정보를 재요청해야하는 상황이라면..?

비동기 처리 방식의 문제점 해결

1. Callback 함수

콜백함수는 나중에 호출할 함수를 의미한다. 더 자세하게는, 함수 타입의 값을 파라미터로 넘겨줘서 파라미터로 받은 함수를 특정 작업이 끝나고 호출해주는 것을 의미한다.

function findUser(id, callback) {
  setTimeout(function() {
    console.log("waited 1 sec");
    const user = {
      id : id,
      name : "User" + id,
      email : id + "@abc.com"
    }
    callback(user); // user 객체를 callback함수에 넘겨준다
  }, 1000)

findUser(1, function(user) {
  console.log("user:" , user)
})
//결과
waited 1 sec
user: {id: 1, name: "User1", email: "1@abc.com"}

findUser 함수 안에 id와 callback함수를 인자로 넘겨주고, callback함수에 대한 설명을 따로 정의해준다.

callback 함수를 식당 자리 예약에 비유할 수 있다.
일반적으로 맛집을 가면 사람이 많아 자리가 없어서 대기자 명단에 이름을 쓴 다음 자리가 날 때까지 주변 가게를 돌아다닌다. 만약 식당에서 자리가 생기면 전화로 자리가 났다고 연락이 온다. 그 전화를 받는 시점이 여기서의 콜백 함수가 호출되는 시점과 같다. 손님 입장에서는 자리가 날 때까지 식당에서 기다리지 않고 근처 가게에서 잠깐 쇼핑을 할 수도 있고 아니면 다른 식당 자리를 알아볼 수도 있다.

자리가 났을 때만 연락이 오기 때문에 미리 가서 기다릴 필요도 없고, 직접 식당 안에 들어가서 자리가 비어 있는지 확인할 필요도 없다. 자리가 준비된 시점, 즉 데이터가 준비된 시점에서만 우리가 원하는 동작(자리에 앉는다, 특정 값을 출력한다 등)을 수행할 수 있다.

Callback hell (콜백지옥)

콜백 기반 비동기 처리는 언뜻 봤을 때 꽤 쓸만해 보이고, 실제로도 그렇다. 한 개 혹은 두 개의 중첩 호출이 있는 경우는 보기에도 나쁘지 않다.

하지만 비동기 동작이 많아진다면 치명적인 단점들이 나타난다.

$.get('url', function(response) {
  parseValue(response, function(id) {
    auth(id, function(result) {
      display(result, function(text) {
        console.log(text);
      });
    });
  });
});
  1. 코드의 가독성이 떨어진다
    • 코드의 중첩으로 인해 '멸망의 피라미드'가 만들어진다.
  2. 콜백함수에 에러처리를 한다면, 모든 콜백에서 각각 에러핸들링을 해주어야한다.
  3. 로직 변경에도 어려움이 있다.

Callback hell을 해결하는 방법

  1. 코딩 패턴으로 해결
function parseValueDone(id) {
  auth(id, authDone)
}
function authDone(result) {
  display(result, displayDone)
}
function displayDone(text) {
  console.log(text);
}
$.get('url', function(response) {
  parseValue(response, parseValueDone);
});
  1. Promise 로 해결

  2. async / await 로 해결


<참고자료>

profile
닭발먹고 힘내서 복습하자👻

0개의 댓글