우당탕탕
JavaScript async await 쓰면서 제가 자주 실수한 패턴들 이야기해볼게요 본문
{"title":"JavaScript async await 쓰면서 제가 자주 실수한 패턴들 이야기해볼게요","content":"
JavaScript async/await를 쓰다가 생각보다 삽질이 많았는데요. 특히 비동기 처리 때문에 의도한 대로 동작하지 않거나, 에러 핸들링을 제대로 못 해서 버그가 생기기도 했어요. 저처럼 고생하실 분들을 위해 제가 겪은 실수하기 쉬운 패턴들과 해결 방법을 정리해봤습니다.
\n
이 글에서 async/await 기본 오해, 병렬 처리와 순차 처리 혼동, 에러 처리 실수, 그리고 프로미스 체인과의 차이까지 실제 코드와 경험 위주로 다뤄볼게요.
\n
async 함수에서 반환값 제대로 이해하기
\n
사실 이 부분이 가장 기본인데도 많이 헷갈려요. async 함수는 항상 Promise를 반환합니다. 그래서 내부에서 그냥 값을 리턴해도 그 값이 곧바로 나오지 않고 Promise.resolve(값)가 됩니다.
\n
async function getNumber() {\n return 42; // 실제로는 Promise<number> 반환\n}\n\ngetNumber().then(num => {\n console.log(num); // 42\n});
\n
이렇게 그냥 값을 리턴하는 게 의외로 헷갈려서, await 없이도 값이 바로 나온다고 착각하는 경우가 종종 있어요.
\n
await은 프로미스 앞에서만 써야 해요
\n
여기서 많이들 헷갈려하시는 게, await은 프로미스가 아닌 값에는 사실상 의미가 없어요. 그런데 어떤 함수가 async라고 해서 내부에서 반환한 값이 프로미스가 아닐 수도 있거든요.
\n
async function foo() {\n return 10;\n}\n\nasync function test() {\n const val = await foo(); // foo()가 Promise를 줘서 await 필요\n console.log(val); // 10\n const val2 = await 20; // 20은 그냥 숫자, await 해도 그냥 숫자 반환\n console.log(val2); // 20\n}\n\ntest();
\n
사실 await 20도 JS 엔진이 내부적으로 Promise.resolve(20) 처리해주긴 하는데, 실무에선 굳이 프로미스 아닌 값에 await 붙이지 말아야 혼란 없습니다.
\n
병렬 실행 vs 순차 실행, 이 차이 제대로 알고 쓰기
\n
비동기 작업 여러 개를 할 때, 기다리는 순서와 병렬 실행 차이 이해하는 게 정말 중요해요. 저는 여기에서 삽질 엄청 했거든요.
\n
예를 들어서 아래처럼 await을 여러 개 연달아 쓴 코드는 순차 실행합니다. 즉 첫 번째가 끝나야 두 번째가 실행돼요.
\n
async function fetchData() {\n const res1 = await fetch('https://api.example.com/a');\n const res2 = await fetch('https://api.example.com/b');\n return [await res1.json(), await res2.json()];\n}
\n
이게 문제되는 경우, 같은 시간에 동시에 요청 가능한데 기다림이 쌓여서 느려요.
\n
그런데 이렇게 하면 병렬 실행 하는 효과를 볼 수 있습니다:
\n
async function fetchData() {\n const promise1 = fetch('https://api.example.com/a');\n const promise2 = fetch('https://api.example.com/b');\n const res1 = await promise1;\n const res2 = await promise2;\n return [await res1.json(), await res2.json()];\n}
\n
이렇게 프로미스 두 개를 미리 만들고 한꺼번에 await를 걸면 네트워크 요청이 동시에 나가서 훨씬 빠르더라고요.
\n
try-catch로 에러 잡는 위치, 너무 넓게 잡지 마세요
\n
에러 처리가 생각보다 까다로워서 여기서도 실수가 많았어요. 특히 try-catch를 너무 넓은 범위로 감싸면 어디서 에러가 났는지 헷갈립니다.
\n
예를 들어 아래처럼 전체를 감싸면 어떤 fetch가 실패했는지 알기 어려워요.
\n
async function fetchAll() {\n try {\n const data1 = await fetchData1();\n const data2 = await fetchData2();\n return [data1, data2];\n } catch(e) {\n console.error('에러 발생:', e);\n }\n}
\n
그래서 저는 각 fetch마다 개별 try-catch를 걸어서 어디서 실패했는지 디버깅하기 편했어요.
\n
각 fetch에 개별 에러 처리 예시
\n
async function fetchAll() {\n let data1, data2;\n try {\n data1 = await fetchData1();\n } catch(e) {\n console.error('fetchData1 실패:', e);\n }\n try {\n data2 = await fetchData2();\n } catch(e) {\n console.error('fetchData2 실패:', e);\n }\n return [data1, data2];\n}
\n
이렇게 하면 어떤 요청이 실패했는지 바로 알 수 있고, 실패해도 나머지 요청은 계속 처리할 수 있어서 내구성 있는 코드가 됩니다.
\n
프로미스 체인과 async/await, 같이 쓸 때 주의점
\n
프로미스 체인(.then())과 async/await를 섞어 쓰다 보니 의도치 않은 동작이 나와서 삽질했어요. 특히 async 함수 내에서 .then()을 제대로 기다리지 않으면 비동기 흐름이 깨져요.
\n
async function foo() {\n fetch('https://api.example.com')\n .then(res => res.json())\n .then(data => {\n console.log(data);\n });\n console.log('fetch 끝');\n}\n\nfoo();
\n
이 코드는 fetch 끝이 먼저 찍히고 그 다음에 데이터가 나와서 실행 순서가 헷갈렸어요. 이럴 땐 await를 써서 프로미스가 완료될 때까지 기다리면 더 직관적입니다.
\n
async function foo() {\n const res = await fetch('https://api.example.com');\n const data = await res.json();\n console.log(data);\n console.log('fetch 끝');\n}\n\nfoo();
\n
사실 then() 대신 await를 쓰는 게 가독성도 좋고 오류 추적도 쉬워서 저는 이제 거의 async/await만 쓴답니다.
\n
여기서 또 헷갈린 추가 팁: forEach에서 async/await 안 되는 이유
\n
많이들 실수하는 게 Array.prototype.forEach 안에서 async 함수를 쓰는 거예요. forEach는 async 함수를 기다리지 않아서 내부 await가 제대로 동작하지 않아요.
\n
async function processItems(items) {\n items.forEach(async item => {\n await doSomething(item); // 이것도 병렬이긴 한데, 대기 못함\n });\n console.log('끝났다');\n}
\n
이 코드는 사실 doSomething이 모두 끝나기 전에 '끝났다'가 찍혀서 정말 곤란했어요.
\n
대신 저는 for...of 문을 사용하거나, Promise.all()을 썼어요.
\n
'언어 > JavaScript' 카테고리의 다른 글
| React useEffect 의존성 배열 때문에 겪은 삽질과 해결 과정 이야기 (0) | 2026.05.30 |
|---|---|
| React useEffect 의존성 배열 때문에 삽질한 경험과 해결 방법 (0) | 2026.05.27 |
| React useEffect 의존성 배열 때문에 고생하다가 알게 된 핵심 포인트 (0) | 2026.05.17 |
| Next.js 13 App Router 마이그레이션 직접 해보니 생각보다 이랬어요 (1) | 2026.05.16 |
| JavaScript async await 쓰면서 제가 실수한 패턴 5가지와 해결법 (0) | 2026.05.13 |
