본문 바로가기
  • 구름빵의 개발 블로그
Front-End/JavaScript,TypeScript

[JavaScript] 비동기 처리 및 Promise 객체

by 은빛구름빵 2025. 9. 10.

배경

프론트엔드 개발할 때 서버와 통신하는 과정에서 굉장히 중요한 이론이 비동기 처리 관련 이론이다. 일반적으로 코드는 동기 처리가 이루어지지만 상황에 따라 비동기 처리가 필요한 경우가 있는데, 주로 서버와 통신을 통해 데이터를 가져오는 방법이다.

 

동기 처리 vs. 비동기 처리

동기 처리는 일반적인 코드 수행 순서와 동일하게 먼저 요청이 들어온 순서로 진행하며 해당 작업이 완료되기 전까지 다음 작업을 진행하지 않고 기다리게 된다.

반면에 비동기 처리는 수행 요청을 동일한 순서로 진행되지만 해당 작업이 완료되지 않더라도 다음 작업을 진행한다. 실행 요청이 들어가는 순서는 동일하지만 다음 작업을 실행하기 전에 코드가 대기를 하느냐 하지 않느냐의 차이를 갖고 있다.

// 동기 처리
console.log( "A" );
console.log( "B" );
console.log( "C" );
// A B C

// 비동기 처리
console.log( "A" );
setTimeout( () => {
	console.log( "B" );
}, 2000 );
console.log( "C" );
// A C B

setTimeout() 함수 또한 비동기 처리 함수이다. 첫 예제의 경우 특별한 것 없이 A B C 순서로 출력된다.

하지만 두 번째 예제의 경우 A가 실행된 이후 B가 실행되기까지 2초를 기다려야 출력이 되는데 setTimeout() 비동기 함수이므로 A가 출력되고 setTimeout() 함수 실행 요청을 한 뒤 바로 C를 출력한다.

 

Promise 객체

비동기 처리를 위해 사용되는 객체로 Promise 객체는 아래 3가지 상태 중 하나를 갖는다.

1. Pending: 대기 상태 - 아직 실행되기 이전 상태

2. Fulfilled: 성공 상태 - 비동기 처리가 완료된 상태

3. Rejected: 거절 상태 - 비동기 처리가 실패한 상태

const myPromise = new Promise( (resolve, reject) => {
	const flag = true;
    if( flag ) resolve( "OK" );
    else reject( "FAIL" );
} )

myPromise
	.then( (result) => console.log( result ) )
    .catch( (error) => console.log( error ) );

Promise 객체에서 위처럼 resolve, reject라는 내장 함수가 있고, 이를 사용할 수 있게 된다.

resolve를 사용하면 해당 비동기 처리를 성공 처리하며 resolve()의 매개변수를 결과로 반환한다.

reject를 사용하면 해당 비동기 처리를 실패 처리하며 reject()의 매개변수를 결과로 반환한다.

Promise 객체의 then() 메서드를 통해 성공하면 처리할 실행문을 정의할 수 있고, catch를 통해 실패하면 처리할 실행문을 정의할 수 있다.

 

await와 async

async는 해당 함수 내부에서 await를 사용한 비동기 함수를 처리할 예정이라고 미리 알려주는 키워드이다.

async 키워드가 있는 함수는 내부에 비동기 함수가 있다면 비동기 처리로 작동하고 그렇지 않다면 동기 처리로 작동한다.

await 함수는 비동기 처리 함수의 값을 기다리도록 하는 키워드이다.

위에서 말했듯이 비동기 처리 함수들은 요청을 하고 결과를 기다리지 않고 다음 작업으로 넘어가게 되는데 await 키워드를 사용하면 다음 작업으로 넘어가지 않고 기다리도록 만들 수 있다.

function fetchData() {
	return new Promise( (resolve, reject) => {
    	setTimeout( () => {
        	const isSuccess = true;
            if( isSuccess ) resolve( "OK" );
            else reject( "FAIL" );
        }, 2000 );
    });
}
const getData = async () => {
	console.log( "Load Data..." );
    try{
    	const result = await fetchData();
        console.log( result );
    }
    catch( error ){
    	console.log( "ERROR: ", error );
    }
}

위와 같은 예제를 만들었을 때, fetchData()는 비동기 함수이다.

getData()라는 함수는 내부에 fetchData라는 비동기 함수가 존재하며 이를 작동시키기 때문에 async 키워드를 달고 있는 getData() 함수는 비동기 처리로 작동하게 된다.

getData() 함수에서 일반적인 비동기 처리 흐름으로 간다면 Load Data... 가 출력하고 fetchData()가 실행된 다음 결과가 오기 전에 result가 출력되어야 한다.

하지만 await 키워드를 사용했기 때문에 fetchData()는 비동기 함수임에도 해당 함수의 결과를 받아 result에 담기 전에 다음 커멘드인 console.log( result )를 수행하지 않는다.

이처럼 await 키워드를 통해 비동기 함수를 사용하더라도 해당 함수의 결과를 기다려 안정성을 보장할 수 있으며 이를 사용하기 위한 상위 함수에서 async 키워드를 사용해 해당 함수가 비동기 처리를 수행할 수 있도록 해준다.

따라서, 비동기 처리 함수를 사용하기 위해선 await와 async 사용이 거의 필수적이라고 할 수 있다.

참고로, await 키워드 뒤에는 반드시 Promise 객체를 반환하는 비동기 처리 함수가 와야 한다.