JavaScript 심화 – 비동기 프로그래밍과 Promises

비동기 프로그래밍은 현대 웹 애플리케이션 개발에서 필수적인 개념입니다. 비동기 프로그래밍을 이해하면 JavaScript에서 더욱 효율적이고 응답성이 뛰어난 코드를 작성할 수 있습니다. 이 포스팅에서는 "JavaScript 비동기", "Promises", "JavaScript 심화"를 중심으로 비동기 프로그래밍의 개념과 Promises의 사용법을 설명하겠습니다. 중급 개발자를 대상으로 상세히 설명하고, 예제를 통해 실습해 보겠습니다.


비동기 프로그래밍이란?


비동기 프로그래밍은 시간이 오래 걸리는 작업을 수행하면서도 애플리케이션이 응답성을 유지하도록 하는 프로그래밍 방식입니다. 비동기 작업은 주로 네트워크 요청, 파일 읽기/쓰기, 타이머 설정 등에서 사용됩니다.


JavaScript는 단일 스레드 언어로, 한 번에 하나의 작업만 실행할 수 있습니다. 비동기 프로그래밍을 통해 작업을 블로킹하지 않고 동시에 여러 작업을 처리할 수 있습니다.


동기 vs 비동기


  • 동기(synchronous): 작업이 순차적으로 실행되며, 이전 작업이 완료될 때까지 다음 작업이 시작되지 않습니다.
  • 비동기(asynchronous): 작업이 병렬로 실행되며, 이전 작업이 완료될 때까지 기다리지 않고 다음 작업을 실행할 수 있습니다.

// 동기 예제
console.log('Start');
console.log('Processing');
console.log('End');

// 비동기 예제
console.log('Start');
setTimeout(() => {
  console.log('Processing');
}, 1000);
console.log('End');

비동기 프로그래밍의 기본 개념


콜백 함수


콜백 함수는 다른 함수에 의해 호출되는 함수입니다. 콜백 함수는 비동기 작업이 완료된 후에 실행될 코드를 정의하는 데 사용됩니다.


function fetchData(callback) {
  setTimeout(() => {
    const data = { name: 'John', age: 30 };
    callback(data);
  }, 1000);
}

fetchData((data) => {
  console.log(data); // { name: 'John', age: 30 }
});

콜백 함수는 간단하고 직관적이지만, 여러 개의 비동기 작업을 처리할 때 콜백 지옥(callback hell) 문제가 발생할 수 있습니다. 이를 해결하기 위해 Promises가 도입되었습니다.


Promises


Promise는 비동기 작업의 완료 또는 실패를 나타내는 객체입니다. Promise는 콜백 지옥 문제를 해결하고, 비동기 코드를 더욱 간결하고 직관적으로 작성할 수 있게 해줍니다.


Promise의 상태


Promise는 다음 세 가지 상태를 가집니다:


  1. 대기(pending): 비동기 작업이 아직 완료되지 않은 상태.
  2. 이행(fulfilled): 비동기 작업이 성공적으로 완료된 상태.
  3. 거부(rejected): 비동기 작업이 실패한 상태.

Promise 생성


new Promise 생성자를 사용하여 Promise를 생성할 수 있습니다. Promise는 resolvereject 함수를 인수로 받는 콜백 함수를 필요로 합니다.


const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve('작업 성공');
    } else {
      reject('작업 실패');
    }
  }, 1000);
});

myPromise
  .then((result) => {
    console.log(result); // 작업 성공
  })
  .catch((error) => {
    console.error(error); // 작업 실패
  });

thencatch


  • then: Promise가 이행되었을 때 실행될 콜백 함수를 등록합니다.
  • catch: Promise가 거부되었을 때 실행될 콜백 함수를 등록합니다.

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

finally


finally: Promise의 결과와 상관없이 항상 실행될 코드를 정의합니다.


myPromise
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.error(error);
  })
  .finally(() => {
    console.log('작업 종료');
  });

Promise 체이닝


Promise는 체이닝을 통해 순차적으로 비동기 작업을 처리할 수 있습니다.


const fetchData = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('데이터 받아옴');
  }, 1000);
});

fetchData
  .then((result) => {
    console.log(result); // 데이터 받아옴
    return '다음 작업 실행';
  })
  .then((nextResult) => {
    console.log(nextResult); // 다음 작업 실행
  })
  .catch((error) => {
    console.error(error);
  });

async/await


async/await는 Promise를 더 쉽게 사용할 수 있도록 도와주는 문법입니다. async/await는 비동기 코드를 마치 동기 코드처럼 작성할 수 있게 해줍니다.


async 함수


async 함수는 항상 Promise를 반환합니다. 함수 내부에서 await 키워드를 사용하여 Promise가 이행될 때까지 기다릴 수 있습니다.


async function fetchData() {
  return '데이터 받아옴';
}

fetchData().then((result) => {
  console.log(result); // 데이터 받아옴
});

await


await 키워드는 async 함수 내에서만 사용할 수 있으며, Promise가 이행될 때까지 대기합니다.


function fetchData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('데이터 받아옴');
    }, 1000);
  });
}

async function processData() {
  const data = await fetchData();
  console.log(data); // 데이터 받아옴
}

processData();

async/await 예제


async/await를 사용하여 여러 비동기 작업을 순차적으로 처리하는 예제입니다.


function fetchData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('데이터 받아옴');
    }, 1000);
  });
}

function fetchMoreData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('추가 데이터 받아옴');
    }, 1000);
  });
}

async function processData() {
  try {
    const data = await fetchData();
    console.log(data); // 데이터 받아옴

    const moreData = await fetchMoreData();
    console.log(moreData); // 추가 데이터 받아옴
  } catch (error) {
    console.error(error);
  } finally {
    console.log('작업 종료');
  }
}

processData();

비동기 프로그래밍의 장점


  • 성능 향상: 비동기 프로그래밍을 통해 시간이 오래 걸리는 작업을 비동기로 처리하여 애플리케이션의 응답성을 향상시킬 수 있습니다.
  • 사용자 경험 개선: 비동기 작업이 완료될 때까지 기다리지 않고 다른 작업을 계속 수행할 수 있어 사용자 경험을 개선할 수 있습니다.
  • 코드 가독성 향상: async/await를 사용하여 비동기 코드를 동기 코드처럼 작성함으로써 가독성을 향상시킬 수 있습니다.

결론


비동기 프로그래밍은 JavaScript 개발에서 중요한 개념입니다. Promises와 async/await를 사용하면 비동기 작업을 효율적으로 관리하고, 코드의 가독성을 높일 수 있습니다. 이번 포스팅에서는 비동기 프로그래밍의 기본 개념과 Promises, 그리고 async/await의 사용법을 예제와 함께 설명하였습니다. 이러한 기술을 잘 활용하여 복잡한 비동기 작업을 효율적으로 처리할 수 있는 능력을 키우세요.


이 포스팅이 JavaScript의 비동기 프로그래밍을 이해하는 데 도움이 되길 바랍니다. 질문이나 추가 정보가 필요하시면 언제든지 댓글로 남겨주세요.

다음 이전