🎯 Achievement Goals


  • Callback
  • Promise 사용패턴과 언어적인 특징들을 이해할 수 있다.
    • resolve, reject의 의미와 then, catch와의 관계를 이해할 수 있다.
    • Promise.all의 사용법을 이해할 수 있다.
  • async / await 의 키워드를 이해하고 작동 원리를 이해할 수 있다.
  • node.js의 fs 모듈 사용법을 이해한다.




👉 Callback


callback 함수는 오늘 배울 async를 다룰 수 있게 도와주는 함수이기도 하며, 우리가 함수를 전달해주면 그 함수를 실행해주는 함수이다.


그래서 오늘 배운 코드는 에러가 났을 때, 앞에서 에러를 표시해주고 정상적으로 실행이 되었을 때 데이터를 반환하는 코드를 배웠다.

//생략..
// 먼저 파일을 불러왔다.
fs.readFile(filePath, "uft-8", function(err, data) {
    // 만약 에러일 경우에는 에러를 반환한다.
    if (err) {
        return callback(err, null);
    }
    // 정상일 경우에는 데이터를 반환한다.
    else {
        return callback(null, data);
    }
})


일단 먼저 에러일 경우를 반환해주는 조건문을 써주는게 좋다고 한다. 그래서 에러일 경우에는 콜백함수에 전달인자를 (err, null) 이런식으로 주었는데, err를 반환하면 data는 null 처리를 한 것이다.

만약 정상적으로 실행이 된다면 데이터를 반환하는데 전달인자는 (null, data)로 해주었다. 여기서 전달인자를 null 먼저 쓴 이유도 에러를 먼저 쓰기 때문이다.

에러 -> 데이터 순서는 관례적인 것 같다.



  • callback hell
const printString = (string, callback) => {
    setTimeout (() => {
        console.log(string);
        callback();
    }),
    Math.floor(Math.random() * 100) + 1;
}

const printAll = () => {
    printString("A", () => {
        printString("B", () => {
            pintString("C", () => {})
        })
    })
}


위 코드는 콜백지옥이라고도 불리는데 저렇게 코드를 작성할 경우에는 가독성이 떨어질뿐더러, 어디서 에러가 났는지 찾기 힘들 것이다. 그리고 디버깅 또한 어려울 것이다.

그래서 오늘 Promise 를 배웠다.




👉 Promise


Promise는 앞서 배웠던 callback을 핸들링 하기 위한 테크닉이다.


Promise 는 주로 서버에서 받아온 데이터를 화면에 표시할 때 사용된다. Promise 는 자바스크립트에서 제공하는 것이고, 비동기를 간편하게 처리해주는 오브젝트이다.

그리고 또 좋은 점은 기능을 수행하다가 에러가 수행을 못할 경우 error를 보내준다.



Promise는 다음 두가지로 나눠 볼 수 있다.

  • State: pending -> fulfilled or rejected
  • Producer vs Consumer


Promise Producer


참고로 Promise는 클래스이다.

전달인자는 두가지가 있는데, Promise로 부터 기대한 값을 얻은 경우, resolve API를 사용하고, State: fulfilled가 된다.


reject API는 기대한 값으로 부터 값을 얻지 못한 경우 사용한다. 상태는 State: rejected가 된다.


// 생략..
return new Promise((resolve, reject) => {
    fs.readFile(filePath, "utf-8", function(err, data) => {
        // 에러 = reject
        if (err) {
            reject(err);
        }
        // 정상 = resolve
        else {
            resolve(data);
        }
    })
})




Promise Consumer


then은 Promise가 잘 수행이 되어서 최종적으로 resolve 콜백함수를 통해서 전달한 값이다.

catch는 Promise에서 에러가 났을 때 어떻게 처리할지 수행해주는 함수이다.


const printString = (string) => {
    return new Promise((resolve, reject) => {
        // ...
    })
}

const printAll = () => {
    printString("A")
    .then(() => {
        return printString("B");
    })
    .then(() => {
        return printString("C");
    })
}
printAll() // A B C


Promise도 .then() 안에서 return을 해주지 않으면 콜백지옥처럼 프로미스 지옥이 될수도 있으니 조심하자.



Promise.all


Promise.all은 모든 Promise들이 병렬적으로 다 받을 때 까지 기다렸다가 다시 전달하게 해주는 함수이며, 전달인자는 객체인 배열로 받는다.


// 생략..

return Promise.all([
    getDataPromise(user1),
    getDataPromise(user2),
]).then((data) => {
    // JSON으로 받은 data들을 map 함수를 통해 모두 다 객체로 다시 변환한다.
    return data.map((el) => JSON.parse(el));
})




👉 Async & Await


Async & AwaitES7 이후에 나온 것으로, 위에 배웠던 프로미스들을 더 읽기 쉽게 해준다.

즉, 동기식처럼 보이게 해주며 조금 더 간편한 API를 제공해준다. (syntactic sugar)

아래 좋은 예제 코드가 있다.


// URL을 가져와서 응답을 텍스트로 로그하려는 경우
function logFetch(url) {
  return fetch(url)
    .then(response => response.text())
    .then(text => {
      console.log(text);
    }).catch(err => {
      console.error('fetch failed', err);
    });
}


다음 코드도 똑같은 결과를 낸다.


async function logFetch(url) {
  try {
    const response = await fetch(url);
    console.log(await response.text());
  }
  catch (err) {
    console.log('fetch failed', err);
  }
}


awaitAsync가 있는 함수에서만 사용할 수 있으며, Promise.resolve() 를 통해서 전달이 된다.


오늘 한가지 깨달았던 점은 return await getDataPromise(user)… 이 부분에서 await는 바로 return으로 쓸 수 없다는 것이었다.

다행히 페어분께서 변수에 할당해서 하면 될 것이라고 말씀해주셔서 오늘 일정도 빠르게 끝낼 수 있었다.







🙌 느낀점


자바스크립트를 배우는 과정에서 비동기는 정말정말 중요하다고 수 없이 들었다. 그래서 오늘 배웠던 부분을 일찍 끝마쳤지만, 복습은 꾸준히 할 예정이다. 문제들은 쉽게 풀었지만 막상 실제로 사용한다고 하면 많이 버벅거릴 것 같다.

아직도 다른 할 일이 많이 남았지만, 비동기는 틈틈히 공부해보자!




👊 내일의 TIW(today I Will)

fetch API