JavaScript Async


자바스크립트에서 비동기 프로그래밍은 웹 애플리케이션에서 중요한 역할을 합니다. 비동기 프로그래밍을 통해 여러 작업을 동시에 수행하면서도 응답성과 성능을 유지할 수 있습니다. 자바스크립트는 비동기 프로그래밍을 지원하기 위해 여러 가지 메커니즘과 패턴을 제공합니다. 여기서는 콜백, 프라미스, async/await와 같은 주요 비동기 프로그래밍 개념과 기법에 대해 상세히 설명하겠습니다.

1. 콜백 (Callback)

콜백 함수는 다른 함수에 인수로 전달되어 특정 작업이 완료된 후 호출되는 함수입니다. 비동기 작업에서 콜백은 결과를 처리하는 데 자주 사용됩니다.

1.1 기본 콜백 예제

function fetchData(callback) {
  setTimeout(() => {
    const data = { id: 1, name: 'John Doe' };
    callback(data);
  }, 1000);
}

function handleData(data) {
  console.log('Data received:', data);
}

fetchData(handleData);
  • fetchData 함수는 1초 후에 콜백 함수를 호출하여 데이터를 전달합니다.
  • handleData 함수는 데이터를 받아서 콘솔에 출력합니다.

1.2 콜백 지옥 (Callback Hell)

복잡한 비동기 작업을 중첩된 콜백으로 처리하면 코드가 복잡해지고 가독성이 떨어집니다. 이를 콜백 지옥이라고 합니다.

function fetchData(url, callback) {
  setTimeout(() => {
    const data = { id: 1, name: 'John Doe' };
    callback(null, data);
  }, 1000);
}

fetchData('api/data1', (err, data1) => {
  if (err) {
    console.error(err);
    return;
  }
  fetchData('api/data2', (err, data2) => {
    if (err) {
      console.error(err);
      return;
    }
    fetchData('api/data3', (err, data3) => {
      if (err) {
        console.error(err);
        return;
      }
      console.log('All data received:', data1, data2, data3);
    });
  });
});

2. 프라미스 (Promise)

프라미스는 비동기 작업의 완료 또는 실패를 처리하기 위한 객체입니다. 프라미스는 콜백 지옥 문제를 해결하고 코드의 가독성을 높입니다.

2.1 프라미스 생성

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true; // 성공 여부를 나타내는 변수
    if (success) {
      resolve('Data received');
    } else {
      reject('Error occurred');
    }
  }, 1000);
});

myPromise
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error(error);
  });
  • myPromise는 1초 후에 성공하면 resolve를 호출하고, 실패하면 reject를 호출합니다.
  • then 메서드는 프라미스가 성공할 때 호출되며, catch 메서드는 프라미스가 실패할 때 호출됩니다.

2.2 여러 프라미스 처리

여러 비동기 작업을 프라미스를 사용하여 순차적으로 처리하거나 병렬로 처리할 수 있습니다.

2.2.1 순차 처리
function fetchData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = { url, data: 'Sample Data' };
      resolve(data);
    }, 1000);
  });
}

fetchData('api/data1')
  .then(data1 => {
    console.log(data1);
    return fetchData('api/data2');
  })
  .then(data2 => {
    console.log(data2);
    return fetchData('api/data3');
  })
  .then(data3 => {
    console.log(data3);
  })
  .catch(error => {
    console.error(error);
  });
2.2.2 병렬 처리
Promise.all([fetchData('api/data1'), fetchData('api/data2'), fetchData('api/data3')])
  .then(results => {
    console.log('All data received:', results);
  })
  .catch(error => {
    console.error(error);
  });
  • Promise.all 메서드는 전달된 모든 프라미스가 완료되면 결과를 배열로 반환합니다.
  • 하나라도 실패하면 catch 블록이 실행됩니다.

3. Async/Await

Async/Await는 프라미스를 사용한 비동기 코드를 더 간단하고 동기 코드처럼 작성할 수 있게 해줍니다. async 키워드를 사용하여 함수를 비동기로 만들고, await 키워드를 사용하여 프라미스가 해결될 때까지 기다릴 수 있습니다.

3.1 기본 Async/Await 예제

async function fetchData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = { url, data: 'Sample Data' };
      resolve(data);
    }, 1000);
  });
}

async function handleData() {
  try {
    const data1 = await fetchData('api/data1');
    console.log(data1);
    const data2 = await fetchData('api/data2');
    console.log(data2);
    const data3 = await fetchData('api/data3');
    console.log(data3);
  } catch (error) {
    console.error(error);
  }
}

handleData();
  • handleData 함수는 비동기 함수로 정의되었으며, await 키워드를 사용하여 프라미스가 완료될 때까지 기다립니다.
  • try...catch 블록을 사용하여 에러를 처리합니다.

3.2 병렬 처리

Async/Await를 사용하여 병렬로 프라미스를 처리할 수도 있습니다.

async function handleData() {
  try {
    const [data1, data2, data3] = await Promise.all([
      fetchData('api/data1'),
      fetchData('api/data2'),
      fetchData('api/data3')
    ]);
    console.log('All data received:', data1, data2, data3);
  } catch (error) {
    console.error(error);
  }
}

handleData();
  • Promise.all을 사용하여 병렬로 프라미스를 처리하고 결과를 배열로 반환받습니다.

4. 비동기 프로그래밍의 실제 사례

비동기 프로그래밍은 주로 네트워크 요청, 파일 I/O, 타이머 등을 처리할 때 사용됩니다.

4.1 네트워크 요청

async function fetchUserData(userId) {
  try {
    const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error fetching user data:', error);
  }
}

fetchUserData(1);
  • fetch API를 사용하여 사용자 데이터를 비동기적으로 가져옵니다.
  • await를 사용하여 네트워크 요청과 JSON 변환이 완료될 때까지 기다립니다.

4.2 파일 I/O (Node.js)

const fs = require('fs').promises;

async function readFileContent(filePath) {
  try {
    const data = await fs.readFile(filePath, 'utf8');
    console.log(data);
  } catch (error) {
    console.error('Error reading file:', error);
  }
}

readFileContent('example.txt');
  • Node.js의 fs 모듈을 사용하여 파일을 비동기적으로 읽습니다.
  • await를 사용하여 파일 읽기 작업이 완료될 때까지 기다립니다.

결론

자바스크립트의 비동기 프로그래밍은 웹 애플리케이션의 성능과 응답성을 향상시키는 데 필수적입니다. 콜백, 프라미스, async/await와 같은 다양한 방법을 사용하여 비동기 작업을 효율적으로 처리할 수 있습니다. 특히, async/await는 코드의 가독성을 높이고 에러 처리를 단순화하여 비동기 프로그래밍을 더 쉽게 만듭니다. 이러한 비동기 프로그래밍 기법을 잘 이해하고 활용하면 더 나은 자바스크립트 애플리케이션을 개발할 수 있습니다.


Leave a Reply

Your email address will not be published. Required fields are marked *