비동기 함수와 callback 함수

Young Han·2021년 10월 4일
0

TIL

목록 보기
11/12

비동기 함수와 Callback 함수 구분을 하기 잘 어려운 부분이 많아서 개념 정리를 하고자 한다.

콜백함수

function findUser(id) {
  const user = {
    id: id,
    name: "User" + id,
    email: id + "@test.com",
  };
  return user;
}

const user = findUser(1);
console.log("user:", user);

결과

user: {id: 1, name: "User1", email: "1@test.com"}

다음과 같은 findUser()라는 함수가 있다고 하자, 일반적으로 우리가 생각하는 일반적인 함수란 파라미터가 있고 출력되는 리턴값이 있다. 하지만 자바스크립트에서는 출력되는 리턴값이 없고 그 대신에 콜백함수를 입력받는 함수들을 많이 볼 수 있다.

예를 들어, 위의 코드와 같은 동일한 결과가 나오게끔 콜백함수를 써서 재작성을 한번 해보겠다.

function findUserAndCallBack(id, cb) {
  const user = {
    id: id,
    name: "User" + id,
    email: id + "@test.com",
  };
  cb(user);
}

findUserAndCallBack(1, function (user) {
  console.log("user:", user);
});

결과

user: {id: 1, name: "User1", email: "1@test.com"}

10번째 줄의 findUserAndCallBack()함수의 호출부를 보면 두번째 인자로 콜백함수를 선언하여 넘겼다. 따라서 7번째줄의 findUserAndCallBack()함수가 실행될 때 cb매개 변수는 콜백 함수를 할당 받으며, cb(user)가 실행이 될때, 이 콜백함수가 실행이 되게 된다.

위 두 코드의 차이점은 findUser()함수는 결과 값을 리턴하고 함수 외부에서 결과값을 이용하여 작업을 수행하는 반면, findUserAndCallBack()함수는 결과값을 이용해야할 작업까지 함수 내부에서 수행해주기 때문에 리턴을 할 필요없이 결과값을 얻을 수 있다.

콜백 함수를 통한 비동기

비동기 함수란? 호출부에서 함수 실행결과를 기다리지 않아도 되는 함수이고, 반대로 동기적 함수란?
호출부에서 함수 실행결과를 기다려야 하는 함수 이다.

비동기 함수의 이러한 Non-blockin이라는 장점 때문에, 자바스크립트 처럼 싱글 쓰레드 환경에서도 흔히 많이 쓰곤 한다. 예를 들어 브라우저에서 어떤 로직이 동기 함수만으로 실행될 경우, 기다리는 시간이 많아져서 사용자 경험에 부정적인 영향을 끼칠 수 있다. 또한 비동기 함수를 사용하면 로직을 순차적으로 처리할 필요가 없기 때문에 동기적 함수보다는 비동기적 함수가 이러한 경우에는 훨씬 유리한 것으로 알고 있다.

하지만 비동기적 함수는 동기적 함수 보다는 좀 덜 직관적이라고 느껴질 수 있다. 동기적 함수처럼 순차적으로 함수 처리가 보장되지 않기 때문에 아래에 위치해 있던 함수가 위에 위치해 있던 함수 보다 먼저 실행이 될 수 있기 때문이다.

자바스크립트에는 setTimeout() 이라는 대표적인 내장 비동기 함수가 있다. setTimeout()은 두개의 매개변수를 가지고 있는데, 첫번째는 실행할 작업내용을 담은 콜백함수 이고, 두전째는 이 콜백함수를 수행하기 전에 기다리는 밀리초 단위의 시간이다. 즉 setTimeout()함수는 두 번째 인자로 들어온 시간 만큼 기다린 후에 첫번째 인자로 들어온 콜백함수를 실행시켜 준다.

실제 프로젝트를 만들다 보면 db나 api를 통해서 유저의 데이터를 얻어오는 경우, 필연적으로 지연시간이 발생하게 된다. 이러한 상황을 실뮬레이션 하기 위해선 setTimeout()을 이용하여 위 섹션에서 작성했던 finUser()함수를 수정하였다. const가 아닌 let을 이용해서 user로컬 변수를 선언하고, setTinmeout()함수를 통해 0.1초 후에 user 변수에 객체를 할당하였다.

function findUser(id) {
  let user;
  setTimeout(function () {
    console.log("waited 0.1 sec.");
    user = {
      id: id,
      name: "User" + id,
      email: id + "@test.com",
    };
  }, 100);
  return user;
}

const user = findUser(1);
console.log("user:", user);

결과

user: undefined
waited 0.1 sec.

위 코드를 실행해보면 예상치 못함 순서로 코드가 실행됨을 알 수 있다. 3번째 줄의 setTimeout()은 비동기 함수의 호출이기 때문에 실행이 완료될 때 까지 기다리지 않고 다음 라인인 11번째 줄로 넘어가 버린다. 따라서 14번째 줄의 findUser(1)는 undefined를 리턴하게 되고 user 변수에는 그대로 undefined가 할당된다. 0.1초 후에 setTimeout()함수의 첫번째 인자로 넘어간 콜백함수가 실행되면서 "waited 0.1 sec"가 실행되고 user 로컬 변수에 원하는 객체가 할당되었지만 이미 때 늦은 상황이 되었다.

이와 같이 setTimeout() 과 같은 비동기 함수를 호출하게 되면, 함수의 실행이 완료도 되기 전에 다음 라인이 실행되어 버리기 때문에 각별히 주의를 해야한다.

위와 같은 코드 실행 순서가 뒤죽박죽이 될 수 있는 난처한 상황에서는 콜백 함수를 이용해서 해결 할 수 있다. 함수로 부터 결과값을 리턴 받기를 포기하고, 결과값을 이용해서 처리할 로직을 콜백 함수에 담아 인자로 전달해 주면 된다.

function findUserAndCallBack(id, cb) {
  setTimeout(function () {
    console.log("waited 0.1 sec.");
    const user = {
      id: id,
      name: "User" + id,
      email: id + "@test.com",
    };
    cb(user);
  }, 100);
}

findUserAndCallBack(1, function (user) {
  console.log("user:", user);
});

결과

waited 0.1 sec.
user: {id: 1, name: "User1", email: "1@test.com"}

이번에는 findUserAndCallBack() 함수의 2번째 인자로 결과값을 이용해서 실행될 로직을 넘겼고, setTimeout() 함수는 0.1초 후에 이 콜백 함수를 호출하였다.이와 같이 비동기 함수를 호출 할때는 결과값을 리턴 할려고 하지 말고, 결과값을 통해 처리할 로직을 콜백 함수로 전달해 주는 스타일로 코드를 작성해 주어야 한다.

0개의 댓글