프론트엔드 개발을 하다 보면 자주 접하는 단어, "비동기".
어느 날 문득 생각했다.
"비동기가 대체 정확히 뭘까? 왜 이렇게 다양하게 처리 방식이 존재할까?"
이번 포스트에서는 비동기의 기본 개념부터, 콜백 → then → async/await → try/catch 그리고 React Query에서의 현대적 비동기 처리 방식까지 순차적으로 정리해보려 한다.
📌 "응답 시간이 예측 불가능한 작업"이 비동기의 본질
예: 서버 API 호출, 파일 읽기, setTimeout 등
getData((result) => {
console.log("데이터:", result);
});
결과를 받으면 콜백 함수에 전달해서 실행
문제: 중첩이 생기면 가독성이 떨어짐 → "콜백 지옥(callback hell)" 발생
// 콜백 지옥 예시 코드
getUser(userId, (user) => {
getPosts(user.id, (posts) => {
getComments(posts[0].id, (comments) => {
getLikes(comments[0].id, (likes) => {
console.log("최종 결과:", {
user,
posts,
comments,
likes
});
});
});
});
});
fetchUser() // 사용자 정보 가져오기 → user
.then((user) => fetchPosts(user.id)) // 그 user.id로 게시물 가져오기 → posts
.then((posts) => fetchComments(posts[0].id)) // 첫 번째 게시물의 id로 댓글 가져오기 → comments
.then((comments) => console.log(comments)); // comments를 콘솔에 출력
async function loadData() {
const user = await fetchUser();
const posts = await fetchPosts(user.id);
const comments = await fetchComments(posts[0].id);
console.log(comments);
}
try {
const res = await fetchData();
console.log(res);
} catch (e) {
console.error("문제 발생", e);
}
React Query는 내부적으로 이미 try/catch 구조로 요청을 감싸고 있다.
const { data, isError, error } = useQuery(['data'], fetchData, {
onSuccess: () => toast.success("성공!"),
onError: () => toast.error("실패!"),
});
✅ 즉, React Query의 onError, isError는 try/catch를 선언적으로 대체한 구조!
| 상황 | 처리 방식 |
|---|---|
| 여러 API를 순차 실행 | async/await + try/catch |
| UI 상태 기반 자동 fetch | React Query 권장 |
| 재사용 가능한 API 함수 | try/catch 없이 순수 반환 |
| 사용자 메시지, 리디렉션 등 UX 연계 | React Query의 onError, onSuccess 활용 |
프론트엔드 개발자라면 비동기 처리는 떼려야 뗄 수 없는 친구다. 우리는 이 친구를
콜백으로 처음 만나고, then으로 정리해보고, async/await로 길들이고, 이제는 React Query 같은 툴에게 안심하고 맡길 수 있게 되었다.
“비동기란, 제어에서 관찰로의 전환이다.”
비동기는 흐름을 제어하는 기술이 아니라, 흐름을 다루는 태도다.