멀티/싱글 스레드 → 콜백 개념 → 동기/비동기 예시 → 일반함수/콜백함수 실습 → forEach → setTimeout → 콜백지옥 순서로 실습을 진행하였다.
Java와 JavaScript를 비교할 때 가장 먼저 잡아야 하는 차이는 “실행 모델”이다.
그래서 JavaScript는 “싱글스레드”라는 기반 위에서, 오래 걸리는 작업 때문에 전체가 멈추는 상황을 줄이려고 비동기(Asynchronous) 패턴을 적극적으로 사용한다.
코드가 A → B → C 순서로 실행된다고 가정하자.
이 문제를 줄이는 전형적인 방법이 A를 비동기적으로 시작해 두고, A가 끝나기 전이라도 B, C를 먼저 진행하는 방식이다.
이때 “A가 끝났을 때 해야 하는 후속 처리”를 등록해두는 대표적인 도구가 콜백 함수다.
JavaScript에서 함수는 “코드 블록”이면서 동시에 “값(데이터)”처럼 다룰 수 있다.
그래서 함수를 변수에 담을 수도 있고, 다른 함수의 인자로 넘길 수도 있는데, 다른 함수에 인자로 전달되는 함수를 콜백(callback) 함수라고 부른다.
forEach, map, filter 등click, scroll 등setTimeout콜백 함수 자체가 “동기/비동기”를 결정하는 게 아니라, 그 콜백을 호출하는 주체가 언제 호출하느냐가 핵심이다.
가장 기본적인 함수 호출은 “정의한 함수를 직접 실행”하는 것이다.
const basicFunction = () => {
console.log("hello world");
}
버튼을 눌렀을 때 콘솔에 "hello world"가 찍히면, “함수 정의 → 호출” 흐름이 정상적으로 잡힌 것이다.
콜백을 쓰는 목적은 “지금이 아니라, 특정 시점에 실행할 로직을 전달”하기 위해서다.
네 예제는 콜백 형태를 가장 대표적인 2가지로 보여준다.
callBackTest(basicFunction);
callBackTest(() => {
console.log("hello java");
});
그리고 callBackTest는 전달받은 함수를 내부에서 실행한다.
const callBackTest = (f1) => {
f1();
}
여기서 핵심은 함수 자체를 넘긴다는 점이다.
즉 basicFunction을 넘기는 것이고, basicFunction()처럼 “호출 결과”를 넘기는 게 아니다.
배열 메서드는 콜백을 굉장히 자연스럽게 사용한다.
forEach는 배열의 각 요소에 대해 콜백을 실행한다.
const numbers = [1, 2, 3, 4, 5];
numbers.forEach((number) => {
console.log(number);
});
이 패턴이 익숙해지면, 반복문을 “의도 중심 코드”로 바꾸는 연습(가독성)이 된다.
setTimeout은 대표적인 “비동기 + 콜백” 함수다.
네 예제는 아래 구조로 되어 있다.
setTimeout(() => console.log("hello java1"), 2000);
console.log("hello python1");
setTimeout(() => console.log("hello java2"), 2000);
console.log("hello python2");
여기서 포인트는 이거다.
setTimeout을 호출하는 순간, “2초 뒤 실행할 콜백”이 등록된다.python1, python2가 먼저 찍힐 수 있다.java1, java2가 찍힌다.즉, 코드 작성 순서와 출력 순서가 달라질 수 있다는 점이 “비동기” 체감 포인트다.
비동기 작업을 단순히 “동시에 실행”하는 건 문제가 아닌데, 실무에서는 종종 이런 요구가 생긴다.
이때 콜백만으로 순서를 강제하려고 하면 콜백 내부에 또 콜백이 들어가고, 중첩이 계속 깊어지면서 코드가 읽기 어려워진다.
이 현상을 흔히 “콜백지옥”이라고 부른다.
그래서 이런 흐름은 읽고 다음 게시글에서 Promise, async/await로 개선하는 방향으로 학습 예정!