본문 바로가기

비동기

1. 비동기 처리

1.1 Promise와 async/await 타입 지정

TypeScript에서 비동기 작업을 처리할 때는 Promise 타입을 사용합니다. 특히 API 호출이나 파일 처리 같은 작업에서 자주 사용됩니다.

// 기본적인 Promise 타입 사용
interface User {
    id: number;
    name: string;
    email: string;
}

// Promise를 반환하는 함수 정의
async function fetchUser(id: number): Promise<User> {
    const response = await fetch(`/api/users/${id}`);
    return response.json();
}

// 실제 사용 예제
async function displayUserInfo(id: number): Promise<void> {
    try {
        const user = await fetchUser(id);
        console.log(`${user.name}의 이메일: ${user.email}`);
    } catch (error) {
        console.error('사용자 정보를 가져오는데 실패했습니다.');
    }
}

💡 async 함수는 자동으로 Promise를 반환합니다. 명시적인 타입 지정으로 코드의 의도를 더 명확하게 만들 수 있습니다.

1.2 Promise.all / Promise.allSettled 타입

여러 비동기 작업을 동시에 처리할 때 Promise.all이나 Promise.allSettled를 사용합니다. TypeScript는 각 Promise의 반환 타입을 튜플로 추론합니다.

Promise.all

모든 Promise가 성공해야 결과를 반환합니다. 하나라도 실패하면 전체가 실패합니다.

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

interface Order {
    id: number;
    productName: string;
    price: number;
}

async function fetchUser(id: number): Promise<User> {
    const response = await fetch(`/api/users/${id}`);
    return response.json();
}

async function fetchOrders(userId: number): Promise<Order[]> {
    const response = await fetch(`/api/users/${userId}/orders`);
    return response.json();
}

// Promise.all 사용 - 튜플 타입으로 추론됨
async function fetchUserWithOrders(userId: number): Promise<[User, Order[]]> {
    const [user, orders] = await Promise.all([
        fetchUser(userId),
        fetchOrders(userId)
    ]);
    return [user, orders];
}

// 사용 예제
async function displayUserDashboard(userId: number): Promise<void> {
    const [user, orders] = await fetchUserWithOrders(userId);
    console.log(`${user.name}님의 주문 내역: ${orders.length}건`);
}

Promise.allSettled

모든 Promise의 결과를 반환합니다. 성공/실패 여부와 관계없이 각 결과를 확인할 수 있습니다.

interface Product {
    id: number;
    name: string;
    price: number;
}

async function fetchProduct(id: number): Promise<Product> {
    const response = await fetch(`/api/products/${id}`);
    if (!response.ok) {
        throw new Error(`상품 ${id}를 찾을 수 없습니다.`);
    }
    return response.json();
}

// Promise.allSettled 사용
async function fetchMultipleProducts(ids: number[]): Promise<Product[]> {
    const results = await Promise.allSettled(
        ids.map(id => fetchProduct(id))
    );

    // 성공한 결과만 필터링
    const products: Product[] = [];

    results.forEach((result, index) => {
        if (result.status === 'fulfilled') {
            products.push(result.value);
        } else {
            console.error(`상품 ID ${ids[index]} 조회 실패:`, result.reason);
        }
    });

    return products;
}

💡 Promise.all은 빠른 실패(fail-fast) 전략이 필요할 때, Promise.allSettled는 일부 실패해도 나머지 결과가 필요할 때 사용합니다.

2. 연습문제

  1. 비동기 함수 타입 지정:

    // TODO: 아래 함수의 반환 타입을 지정하고,
    // 적절한 인터페이스를 만드세요
    async function fetchUserOrders(userId) {
        const response = await fetch(`/api/users/${userId}/orders`);
        return response.json();
    }
    
  2. Promise.all 타입 지정:

    // TODO: 아래 함수의 타입을 완성하세요
    // 게시글과 댓글을 동시에 가져오는 함수입니다
    async function fetchPostWithComments(postId) {
        const [post, comments] = await Promise.all([
            fetch(`/api/posts/${postId}`).then(res => res.json()),
            fetch(`/api/posts/${postId}/comments`).then(res => res.json())
        ]);
        return { post, comments };
    }
    

3. 연습문제 해답

  1. 비동기 함수 해답:

    interface Order {
        id: number;
        productId: number;
        quantity: number;
        totalPrice: number;
    }
    
    async function fetchUserOrders(userId: number): Promise<Order[]> {
        const response = await fetch(`/api/users/${userId}/orders`);
        return response.json();
    }
    
  2. Promise.all 해답:

    interface Post {
        id: number;
        title: string;
        content: string;
        authorId: number;
    }
    
    interface Comment {
        id: number;
        postId: number;
        text: string;
        authorName: string;
    }
    
    interface PostWithComments {
        post: Post;
        comments: Comment[];
    }
    
    async function fetchPostWithComments(postId: number): Promise<PostWithComments> {
        const [post, comments] = await Promise.all([
            fetch(`/api/posts/${postId}`).then(res => res.json()) as Promise<Post>,
            fetch(`/api/posts/${postId}/comments`).then(res => res.json()) as Promise<Comment[]>
        ]);
        return { post, comments };
    }