비동기 프로그래밍의 혁신: JavaScript Async/Await 완벽 가이드
JavaScript 비동기 프로그래밍의 혁신: Async/Await 완벽 가이드
JavaScript는 본래 단일 스레드 언어로 설계되었지만, 웹 애플리케이션의 복잡성이 증가하면서 비동기 프로그래밍이 필수 요소로 떠올랐습니다. 전통적으로 콜백 함수(callback function)
와 프로미스(Promise)
를 사용해 비동기 작업을 처리했지만, 이는 코드의 가독성을 떨어뜨리고 유지보수를 어렵게 만들었습니다. 이를 해결하기 위해 ECMAScript 2017(ES8)에서는 async/await
키워드가 도입되었습니다. 이번 가이드에서는 async/await
의 기본 개념부터 고급 사용법까지 다뤄보겠습니다.
기본 개념
async
와 await
는 프로미스를 보다 직관적이고 간결하게 처리할 수 있게 해주는 키워드입니다. 비동기 함수를 정의할 때 async
키워드를 사용하며, 해당 함수 내에서 프로미스를 처리할 때 await
키워드를 사용합니다.
async 함수
async
키워드는 함수가 항상 프로미스를 반환하도록 만듭니다. 예를 들어, 다음은 간단한 async
함수 입니다:
javascriptasync function fetchData() { return 'Hello, Async!'; }
위 함수는 호출시 자동으로 프로미스를 반환합니다. fetchData
함수는 값을 직접 반환하지만, 이는 내적으로 Promise.resolve('Hello, Async!')
로 변환됩니다. 따라서 우리는 다음과 같이 이 값을 처리할 수 있습니다:
javascriptfetchData().then(data => console.log(data)); // Hello, Async!
await 키워드
await
키워드는 async
함수 내에서만 사용할 수 있습니다. 이는 특정 프로미스가 해결될 때까지 함수 실행을 일시 중지하고, 해결된 값을 반환합니다. 다음은 await
키워드를 사용하는 예제입니다:
javascriptasync function fetchData() { const response = await fetch('https://api.example.com/data'); const data = await response.json(); return data; }
여기서 fetch
와 response.json()
은 모두 프로미스를 반환합니다. await
키워드는 이들 프로미스가 해결될 때까지 기다린 후 그 결과를 반환합니다.
에러 처리
async/await
를 이용한 에러 처리는 매우 간단하지만, 중요한 부분입니다. 일반적으로 try
/catch
블록을 사용합니다:
javascriptasync function fetchData() { try { const response = await fetch('https://api.example.com/data'); if (!response.ok) { throw new Error('Network response was not ok'); } const data = await response.json(); return data; } catch (error) { console.error('Fetch error:', error); throw error; // 필요 시 에러를 다시 던질 수 있습니다. } }
복수의 비동기 작업 처리
때때로 복수의 비동기 작업을 병렬로 처리해야 할 때가 있습니다. 이 경우 Promise.all
과 await
를 함께 사용할 수 있습니다:
javascriptasync function fetchMultipleData() { const [data1, data2] = await Promise.all([ fetch('https://api.example.com/data1').then(response => response.json()), fetch('https://api.example.com/data2').then(response => response.json()), ]); return [data1, data2]; }
Promise.all
은 배열 내 모든 프로미스가 해결될 때까지 기다린 후 결과를 배열로 반환합니다. 이 방법을 사용하면 네트워크 요청을 병렬로 처리하여 성능을 최적화할 수 있습니다.
async/await의 실용적인 사례
async/await
는 다양한 실제 상황에서 유용하게 사용될 수 있습니다. 그릇된 시나리오 몇 가지를 살펴보겠습니다.
데이터베이스 쿼리
백엔드(Node.js)에서 데이터베이스 쿼리를 처리할 때 async/await
는 코드를 단순하고 깔끔하게 만들어줍니다:
javascriptconst fetchUserData = async (userId) => { try { const user = await db.query('SELECT * FROM users WHERE id = ?', [userId]); return user; } catch (error) { console.error('Database query error:', error); throw error; } };
파일 읽기/쓰기
Node.js에서 파일 시스템 작업을 처리할 때도 async/await
는 큰 도움이 됩니다:
javascriptconst fs = require('fs').promises; const readFile = async (filePath) => { try { const data = await fs.readFile(filePath, 'utf-8'); return data; } catch (error) { console.error('File read error:', error); throw error; } };
결론
async/await
는 JavaScript 비동기 프로그래밍의 강력한 도구입니다. 이를 사용하면 복잡한 비동기 코드를 단순하고 직관적으로 작성할 수 있으며, 에러 처리도 훨씬 간편하게 할 수 있습니다. 익숙해지기까지 시간이 조금 걸릴 수 있지만, 한 번 숙달되면 코드 가독성과 유지보수성에서 확실한 이점을 얻을 수 있습니다.
이 가이드를 통해 async/await
의 기본 개념과 고급 사용법을 익히며, 비동기 프로그래밍을 보다 효율적으로 다룰 수 있기를 바랍니다.