| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- Tomcat
- windows
- jquery
- reCAPTCHA
- node.js
- mybatis
- IntelliJ
- dbeaver
- shorturl
- html
- js
- svn
- TIP
- javascript
- SQL Server
- mysql
- Mac
- my sql
- STS
- devlog
- programmers
- urlshortner
- Eclipse
- SQL
- Linux
- spring
- Java
- Oracle
- maria db
- 정보처리기사
- Today
- Total
고양의 성장일기
[Javascript] 자바스크립트 비동기 3부작 <Callback> 본문
자바스크립트 비동기 3부작 <Callback>
지난 글에서 비동기를 처리하는 방법과 관련해 Promise와 async / await 방식에 대해 살펴보았습니다.
이번 글에서는 그보다 더 근본적인 비동기 처리 패턴인 Callback에 대해 정리해보려 합니다.
Callback이란 무엇인가
콜백이란 어떤 작업이 끝난 뒤 실행할 함수를 다른 함수의 인자로 전달하는 방식을 의미합니다.
자바스크립트에서 함수는 1급 객체로서
- 변수에 할당할 수 있고
- 다른 함수에 인자로 전달할 수 있으며
- 객체로 참조할 수 있습니다.
이 특성에 의해 자바스크립트는 비동기 작업의 완료 시점을 직접 기다리고 관측하는 대신
"작업이 끝났을 때 무엇을 할지"를 함수로 전달하는 구조를 사용하곤 했었습니다.
그 덕분에 비동기 작업의 완료시점을 보장받을 수 없는 자바스크립트에서 안정적인 실행흐름을 제어할 수 있었습니다.
function fetchData(callback) {
setTimeout(() => {
callback('data');
}, 1000);
}
fetchData(result => {
console.log(result);
});
Callback 기반 비동기 처리는 어떻게 동작하는가
콜백 패턴의 핵심 흐름은 아래와 같습니다.
- 비동기 함수 호출
- 함수는 즉시 반환되고 호출 스택은 후속 작업 진행
- 비동기 작업이 완료되면
간단히 도식화하면 아래와 같은 패턴이며, 콜백은 비동기 작업 완료 시점에 실행됩니다.
여기서는 console.log('async'); 이 한 줄이 포함된 익명 함수 부분이 콜백이 되는 셈이죠.
console.log('start');
setTimeout(() => {
console.log('async');
}, 1000);
console.log('end');
에러 핸들링하기
콜백 패턴에서의 예외나 에러는 비동기 작업 결과로 직접 받아내어 명시적으로 전달하는 방식이 일반적입니다.
function fetchData(callback) {
setTimeout(() => {
const error = null;
const data = { value: 10 };
callback(error, data);
}, 1000);
}
fetchData((error, result) => {
if (error) {
console.error(error);
return;
}
console.log(result);
});
지옥 창조하기
콜백 패턴에서의 가장 큰 문제는 비동기 작업이 중첩될수록 코드가 급격히 복잡해진다는 점입니다.
마치 한 층 한 층 내려갈수록 더욱 끔찍한 풍경이 펼쳐지는 지옥을 연상시키죠
login(user, () => {
getProfile(user, profile => {
getPosts(profile.id, posts => {
saveLog(posts, () => {
console.log('지옥에 온 걸 환영한다.');
});
});
});
});

예외처리까지 더하면 코드는 아래와 같은 형태가 됩니다.
step1(result => {
if (error) return handleError();
step2(result, next => {
if (error) return handleError();
step3(next, done => {
if (error) return handleError();
});
});
});
예외를 종류별로 처리해야 한다면 if 하나로 안 끝나겠죠 (연쇄 if-else마의 출현)
이 복잡한 비동기의 흐름을 직관적으로 제어하기 위해 등장한 것이 이전 글에서 다뤘던 Promise와 async / await입니다.
Callback 패턴의 이쁜 점
단점만 있는 것은 아닙니다.
원시적인 형태이니만큼 개념이 매우 단순하고 소규모 비동기 작업에는 여전히 유효합니다.
트리거가 있어야 코드가 실행되는 이벤트 핸들링에도 여전히 활용되고 있죠.
button.addEventListener('click', () => {
console.log('clicked');
});
Callback 패턴의 한계와 진화 과정 되짚어보기
비동기 3부작의 마지막 시리즈이자, Promise와 async / await의 프리퀄인 Callback의 진화과정을
인류 역사와 비교해 보면 마치 이 그림과 같습니다.

- Callback : 단순하지만 중첩과 에러 핸들링 문제, 원시인 다리털처럼 복잡해지기 쉬운 코드
- Promise : 체이닝, 중앙 집중식 에러 처리( catch() )
- async / await : 동기 코드와 유사한 가독성, try-catch 블록을 활용한 에러 처리
그럼에도 Callback 이해해야 하는 이유
실무에서는 Promise패턴이나 async / await 패턴이 주력이지만, 콜백을 이해하면 좋은 점은 분명히 있습니다
- 레거시 코드 유지보수 (이게 진짜...)
- 이벤트 기반 프로그래밍의 이해
- Promise 내부 동작 원리 이해
- 비동기 흐름의 본질 파악
때로 지옥이라 멸시받는 콜백은 사실 자바스크립트 비동기 처리의 알파이자 오메가입니다.
마치며
"자바스크립트는 싱글 스레드일까요? 멀티 스레드일까요?"
지금은 제가 속한 파트의 파트장으로 있는 모 대리님이 파트원일 때 다른 파트원들에게 던졌던 질문입니다.
이어진 짧은 강연이 너무 명료하고 감명 깊어서 언젠가 비동기에 관해 공부하고 포스팅해야겠다고 결심했죠.
사실 비동기와 콜백은 우리 실생활에도 이미 깊게 녹아 있습니다.
- 세탁기를 돌리며 블로그에 포스팅을 하는 것 (비동기 흐름)
- 세탁이 완료되면
- 내가 세탁물을 정리할지
- 동생에게 부탁할지 정하는 것 (콜백 설정)
- 세탁이 잘 안됐을 때 뒤처리 방법 (에러 핸들링)
처음엔 단순했던 자바스크립트 애플리케이션들의 구조가 방대해지고 복잡해짐에 따라
다양한 비동기 관련 패턴이 발맞춰 발전했을 것입니다.
저 또한 이번 포스팅을 통해 단순히 비동기 흐름을 이해하는 것을 넘어 현실세계의 패턴들이 어떻게 프로그램으로 구현되는지,
그리고 스트럭쳐와 시스템이 왜 필요한지 다시 한 번 생각하게 된 좋은 계기였던 것 같습니다.
다음 포스팅은 번외편, 자바스크립트는 싱글 스레드 주제에 어떻게 비동기를 해내는지에 대해서 찾아보겠습니다.
감사합니다!
'🖥️ Front-End > Javascript' 카테고리의 다른 글
| [Javascript] IIFE 알아보기 (0) | 2025.12.27 |
|---|---|
| [Javascript] 싱글 스레드인 자바스크립트가 비동기 작업을 수행하는 방법 (0) | 2025.12.17 |
| [Javascript] 자바스크립트 비동기 3부작 <async / await> (1) | 2025.12.15 |
| [Javascript] 자바스크립트 비동기 3부작 <약속의 Promise> (0) | 2025.12.15 |
| [Javascript] Fisher-Yates 알고리즘으로 랜덤 값 가져오기 (3) | 2025.12.15 |