TypeScript로 비동기 프로그래밍

원도훈·2024년 12월 10일
1

안녕하세요, TypeScript에서 비동기 프로그래밍을 다루는 방법에 대해 알아보겠습니다. TypeScript는 JavaScript의 비동기 프로그래밍 모델을 기반으로 하면서, 정적 타입 시스템을 통해 비동기 코드의 안전성과 가독성을 더욱 향상시킵니다. 이번 글에서는 비동기 프로그래밍의 기본 개념부터 TypeScript에서의 활용 사례까지 살펴보겠습니다.


비동기 프로그래밍이란?

비동기 프로그래밍은 시간이 오래 걸리는 작업(예: 파일 읽기, 네트워크 요청)을 실행하는 동안 프로그램의 나머지 부분이 중단되지 않고 계속 실행될 수 있도록 설계된 프로그래밍 방식입니다. JavaScript에서는 이벤트 루프와 콜백 함수, Promise, async/await 같은 비동기 처리 메커니즘을 사용합니다.

TypeScript에서는 이러한 비동기 메커니즘에 타입 안전성을 추가하여 보다 신뢰할 수 있는 코드를 작성할 수 있습니다.


TypeScript에서의 Promise

Promise는 비동기 작업의 완료 또는 실패를 나타내는 객체입니다. TypeScript에서는 Promise에 반환 타입을 명시하여 예상치 못한 타입 관련 오류를 방지할 수 있습니다.

기본 사용법

function fetchData(): Promise<string> {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('데이터 로드 완료');
    }, 1000);
  });
}

fetchData().then((data) => {
  console.log(data); // "데이터 로드 완료"
});

위 코드에서 fetchData 함수는 Promise<string>을 반환하므로, then 메서드에서 datastring 타입임을 확신할 수 있습니다.

에러 처리

Promise를 사용할 때는 에러를 처리하는 것이 중요합니다.

function fetchDataWithError(): Promise<string> {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Error('데이터 로드 실패'));
    }, 1000);
  });
}

fetchDataWithError()
  .then((data) => {
    console.log(data);
  })
  .catch((error) => {
    console.error(error.message); // "데이터 로드 실패"
  });

Async/Await

async/awaitPromise를 더 간단하고 가독성 높은 방식으로 처리할 수 있도록 도와줍니다. async 함수는 항상 Promise를 반환하며, await 키워드는 Promise가 처리될 때까지 함수를 일시 중단합니다.

기본 사용법

async function fetchDataAsync(): Promise<string> {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('비동기 데이터 로드 완료');
    }, 1000);
  });
}

async function main() {
  const data = await fetchDataAsync();
  console.log(data); // "비동기 데이터 로드 완료"
}

main();

에러 처리

async/await에서 에러를 처리하려면 try/catch 블록을 사용합니다.

async function mainWithError() {
  try {
    const data = await fetchDataWithError();
    console.log(data);
  } catch (error) {
    if (error instanceof Error) {
      console.error(error.message); // "데이터 로드 실패"
    }
  }
}

mainWithError();

비동기 함수와 타입 정의

TypeScript에서는 비동기 함수의 반환 타입을 명확히 정의해야 합니다. Promise<T> 형태로 반환 타입을 지정합니다.

예제: 사용자 데이터 가져오기

interface User {
  id: number;
  name: string;
}

async function getUser(id: number): Promise<User> {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ id, name: '홍길동' });
    }, 1000);
  });
}

async function main() {
  const user = await getUser(1);
  console.log(user.name); // "홍길동"
}

main();

비동기 제네릭 함수

비동기 함수에서도 제네릭을 활용하면 다양한 타입을 처리할 수 있습니다.

async function fetchDataGeneric<T>(data: T): Promise<T> {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(data);
    }, 1000);
  });
}

async function mainGeneric() {
  const stringData = await fetchDataGeneric<string>('Hello');
  console.log(stringData); // "Hello"

  const numberData = await fetchDataGeneric<number>(42);
  console.log(numberData); // 42
}

mainGeneric();

비동기 배열 처리: Promise.allPromise.race

비동기 작업을 배열로 처리할 때 Promise.allPromise.race를 사용할 수 있습니다.

Promise.all

Promise.all은 모든 Promise가 완료될 때까지 기다린 후 결과를 배열로 반환합니다.

async function fetchMultipleData() {
  const results = await Promise.all([
    fetchDataGeneric('데이터 1'),
    fetchDataGeneric('데이터 2'),
    fetchDataGeneric('데이터 3')
  ]);
  console.log(results); // ["데이터 1", "데이터 2", "데이터 3"]
}

fetchMultipleData();

Promise.race

Promise.race는 가장 먼저 완료된 Promise의 결과를 반환합니다.

async function fetchFirstData() {
  const result = await Promise.race([
    fetchDataGeneric('데이터 A'),
    new Promise((resolve) => setTimeout(() => resolve('타임아웃 데이터'), 500))
  ]);
  console.log(result); // "타임아웃 데이터"
}

fetchFirstData();

비동기 프로그래밍의 주의점

1. 에러 누락 방지

비동기 작업에서 에러를 누락하지 않도록 항상 catch 블록이나 try/catch를 사용해야 합니다.

2. await의 순차 실행

await는 순차적으로 실행되므로, 병렬 처리가 필요한 경우 Promise.all을 사용하는 것이 더 효율적입니다.

// 비효율적인 방식
async function sequential() {
  await fetchDataGeneric('Step 1');
  await fetchDataGeneric('Step 2');
}

// 병렬 처리
async function parallel() {
  await Promise.all([
    fetchDataGeneric('Step 1'),
    fetchDataGeneric('Step 2')
  ]);
}

결론

TypeScript의 비동기 프로그래밍은 Promiseasync/await을 활용하여 비동기 작업을 안전하고 효율적으로 처리할 수 있도록 도와줍니다.

  • Promiseasync/await을 활용하여 코드 가독성을 높이고 비동기 작업을 체계적으로 처리하세요.
  • 타입 시스템을 활용하여 비동기 함수의 결과와 에러를 안전하게 처리하세요.
  • Promise.allPromise.race를 사용해 병렬 작업을 최적화하세요.

TypeScript로 더욱 강력하고 유지보수하기 쉬운 비동기 코드를 작성해 보세요! 🚀


참고 자료

profile
개발

0개의 댓글