[JS] 비동기 처리(Async/Await)

javaScript 기초를 탄탄하게 다져보자

Posted by lim.Chuck on December 23, 2024

[JavaScript]

기초

  1. [JS] Js기초
  2. [JS] Js기초 변수
  3. [JS] Js기초 자료형
  4. [JS] Js기초 형변환
  5. [JS] Js기초 연산자
  6. [JS] Js기초 반복문
  7. [JS] Js기초 switch
  8. [JS] Js기초 function
  9. [JS] Js기초 객체
  10. [JS] Js기초 배열

중급

  1. [JS] Js중급 호이스팅(Hoisting)과 TDZ(Temporal Dead Zone)
  2. [JS] Js중급 생성자함수
  3. [JS] 객체 메소드(Object methods), 계산된 프로퍼티(Computed property)
  4. [JS] 심볼(Symbol)
  5. [JS] WeakMap WeakSet
  6. [JS] 숫자, 수학 method (Number, Math)
  7. [JS] 문자열 메소드(String methods)
  8. [JS] 배열 메소드(Array methods)
  9. [JS] 구조 분해 할당 (Destructuring assignment)
  10. [JS] 매개변수 리스트와 전개 문법(Rest parameters and spread syntax)
  11. [JS] 클로저(Closure)
  12. [JS] setTimeout / setInterval
  13. [JS] call / apply / bind
  14. [JS] 상속, 프로토타입(Prototype)
  15. [JS] 클래스(Class)
  16. [JS] 클로미스(Promise)
  17. [JS] 비동기 처리(Async/Await)
  18. [JS] Generator
  19. [JS] 메모리 릭(Memory Leak)

async/await는 Promise를 더 쉽게 사용할 수 있게 해주는 문법이야! 😊

1. 기본 문법

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Promise 방식
function fetchUser() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ id: 1, name: "김코딩" });
    }, 1000);
  });
}

// async/await 방식
async function getUser() {
  const user = await fetchUser();
  console.log(user); // {id: 1, name: "김코딩"}
}

getUser();

2. 에러 처리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
async function fetchData() {
  try {
    const response = await fetch("https://api.example.com/data");

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const data = await response.json();
    return data;
  } catch (error) {
    console.error("데이터 가져오기 실패:", error);
    throw error; // 에러 다시 던지기
  }
}

// 사용
async function main() {
  try {
    const result = await fetchData();
    console.log(result);
  } catch (error) {
    console.error("메인 에러:", error);
  }
}

3. 병렬 처리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 순차 처리 (더 느림)
async function sequential() {
  const user = await fetchUser(1); // 1초
  const posts = await fetchPosts(1); // 1초
  const comments = await fetchComments(1); // 1초
  // 총 3초 소요

  return { user, posts, comments };
}

// 병렬 처리 (더 빠름)
async function parallel() {
  const userPromise = fetchUser(1);
  const postsPromise = fetchPosts(1);
  const commentsPromise = fetchComments(1);

  // 동시에 실행
  const [user, posts, comments] = await Promise.all([
    userPromise,
    postsPromise,
    commentsPromise,
  ]);
  // 총 1초 소요

  return { user, posts, comments };
}

4. 실전 예제: API 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class API {
  constructor(baseURL) {
    this.baseURL = baseURL;
  }

  async get(endpoint) {
    const response = await fetch(this.baseURL + endpoint);
    if (!response.ok) throw new Error("API 호출 실패");
    return response.json();
  }

  async post(endpoint, data) {
    const response = await fetch(this.baseURL + endpoint, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data),
    });
    if (!response.ok) throw new Error("API 호출 실패");
    return response.json();
  }
}

// 사용 예시
const api = new API("https://api.example.com");

async function getUserData(userId) {
  try {
    // 병렬로 데이터 가져오기
    const [user, posts] = await Promise.all([
      api.get(`/users/${userId}`),
      api.get(`/users/${userId}/posts`),
    ]);

    return { user, posts };
  } catch (error) {
    console.error("데이터 로딩 실패:", error);
    throw error;
  }
}

5. 반복문에서의 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// forEach와 async/await (작동하지 않음)
async function wrong() {
  const ids = [1, 2, 3];

  ids.forEach(async (id) => {
    // 주의: 이렇게 하면 안됨!
    const data = await fetchData(id);
    console.log(data);
  });

  console.log("완료"); // 데이터 로딩 전에 출력됨
}

// 올바른 방법 1: for...of
async function correct1() {
  const ids = [1, 2, 3];

  for (const id of ids) {
    const data = await fetchData(id);
    console.log(data);
  }

  console.log("완료"); // 모든 데이터 로딩 후 출력
}

// 올바른 방법 2: Promise.all
async function correct2() {
  const ids = [1, 2, 3];
  const promises = ids.map((id) => fetchData(id));

  const results = await Promise.all(promises);
  results.forEach((data) => console.log(data));

  console.log("완료");
}

6. 유용한 패턴들

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 타임아웃 추가
async function fetchWithTimeout(timeout) {
  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => reject(new Error("시간 초과!")), timeout);
  });

  try {
    const result = await Promise.race([fetchData(), timeoutPromise]);
    return result;
  } catch (error) {
    console.error("에러:", error);
    throw error;
  }
}

// 재시도 로직
async function fetchWithRetry(maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fetchData();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      console.log(`재시도 ${i + 1}/${maxRetries}`);
      await new Promise((resolve) => setTimeout(resolve, 1000));
    }
  }
}

꿀팁! 🍯

  1. async 함수는 항상 Promise를 반환
1
2
3
4
5
6
7
8
async function example() {
  return 123;
}

// 위 함수는 아래와 동일
function example() {
  return Promise.resolve(123);
}
  1. 화살표 함수에서 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const fetchData = async () => {
  const response = await fetch("https://api.example.com/data");
  return response.json();
};

// 즉시 실행 함수
(async () => {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
})();

이제 async/await는 마스터! 😎 비동기 코드를 동기 코드처럼 깔끔하게 작성할 수 있을 거야!