꼬꼬마 블로그

꼬꼬마의 기술 블로그

제가 JS를 처음 접하고 비동기에 대한 개념을 몰라 많이 삽질했던 기억이 있습니다.

const  a = getUser();
console.log(a);

해당 코드는 getUser라는 메소드를 실행 시킨 후 console.log를 찍습니다. 물론 회원정보가 콘솔에 찍힙니다.
하지만 getUser라는 메소드가 만약 비동기 메소드라면 과연 회원정보가 정상적으로 찍힐까요?

비동기


동기와 비동기의 차이점을 간단한 예로 설명해보겠습니다.

저희 학교는 기숙사학교입니다. 오늘 할일이 빨래를 돌리고 양치를 해야합니다. 학교에서 만약 빨래를 돌리는데 40분이 걸린다고 하면 동기적 처리는 빨래 시작버튼을 누르고 40분을 기다린 후 빨래가 다 되면 빨래를 널고 양치를 하는 것 입니다. 하지만 비동기 처리는 빨래 시작버튼을 누르고 양치를 한 후 빨래가 다 되면 빨래를 너는 방식입니다.

시간이 오래 걸리는 일을 기다리지 않고 바로 다음 코드가 실행되는 것이 비동기적 처리입니다.

Callback

setTimeout(() => {
    console.log('finish');
}, 1000);

비동기를 처리하는 방식중 하나인 callback 방식입니다.

이 비동기처리가 끝났을때 실행할 콜백함수를 함수의 인자로 넘겨주고 비동기 함수에서는 처리가 완료되면 인자로 넘겨받은 콜백함수를 실행합니다.

위의 코드의 setTimeout은 두가지 인자를 받습니다. 함수가 처리되고 실행할 콜백함수와 기다리는 밀리세컨드를 받습니다.

하지만 이런 방식은 콜백지옥 이라는 단점이 있습니다.

콜백지옥 이미지

이렇게 비동기함수가 연달아 실행될 경우 콜백함수를 계속 넘겨주어야 합니다

Promise

Promise는 이런 콜백지옥을 해결하기 위해 나온 개념입니다.

Promise는 세가지 상태를 가집니다.

  • Pending(대기) : 비동기 처리가 아직 완료되지 않은 상태
  • Fulfilled(이행) : 비동기 처리가 완료되어 결과 값을 반환해준 상태
  • Rejected(실패) : 비동기 처리가 실패한 상태

간단한 코드를 보면

const getUser = () => {
    return new Promise((resolve, reject) => {
        const user = {
            name: 'jinwoo',
            age: 18,
        };

        resolve(user);
    });
}

getUser()
    .then((user) => {
        console.log(user);
    })

아까와 비슷한 코드입니다. new Promise를 통해 Promise를 생성할 수 있습니다. 해당 프로미스는 resolve, reject를 매개변수로 받는 함수를 인자로 받습니다. 해당 함수에서 비동기 로직을 처리 후 return이 아닌 resolve를 통해 데이터를 반환합니다.

반환된 데이터는 .then으로 받습니다.

const getUser = () => {
    return new Promise((resolve, reject) => {
        reject(new Error('에러 발생'));
    });
}

getUser()
    .then((user) => {
        console.log(user);
    })
    .catch((err) => {
        console.log(err);
    })

위와 같이 비동기 처리 도중 reject를 통해 에러를 던질 수 있으며 해당 에러는 .catch를 통해 핸들링 가능합니다.

Async/Await

Promise를 동기적인 흐름으로 보기위해 사용되는 것이 Async/Await입니다.

await 키워드를 사용하기 위해서는 해당 함수를 async함수로 만들어 주어야 합니다.

const getUser = async () => {
    const user = {
        name: 'jinwoo',
        age: 18,
    };

    return user;
}

const main = async () => {
    const user = await getUser();

    console.log(user);
}

main();

위의 코드에서는 main이라는 함수를 실행합니다. main함수를 보면 getUser라는 메서드를 await키워드를 붙여 호출합니다.
이 경우 getUser함수가 비동기함수라면 해당 비동기함수가 모두 처리된 후 console.log가 실행됩니다.

Async/AwaitPromise 기반이기에 then, catch를 통해 값을 반환받을 수 있습니다.

const getUser = async () => {
    const user = {
        name: 'jinwoo',
        age: 18,
    };

    throw new Error('에러 발생');
}

const main = async () => {
    try {
        const user = await getUser();

        console.log(user);
    } catch (err) {
        console.log(err);
    }
}

main();

await을 사용한다면 에러를 핸들링하지 못하기 때문에 try, catch문을 사용해 에러를 핸들링 해주어야합니다.