💻 프로그래밍

Callback

콜백

다른 함수에 인자로 전달되어 나중에 호출되는 함수. 비동기 프로그래밍의 기초.

📖 상세 설명

콜백(Callback)은 다른 함수에 인자로 전달되어 특정 시점에 "다시 호출(call back)"되는 함수입니다. 프로그래밍에서 함수를 일급 객체(first-class citizen)로 취급하는 언어들에서 핵심적인 패턴으로, JavaScript, Python, C++ 등 대부분의 현대 프로그래밍 언어에서 광범위하게 사용됩니다.

콜백의 가장 중요한 용도는 비동기 프로그래밍입니다. 파일 읽기, 네트워크 요청, 타이머 같은 시간이 걸리는 작업을 수행할 때, 프로그램이 결과를 기다리며 멈추는 대신 콜백 함수를 등록해두고 다른 작업을 계속 수행할 수 있습니다. 작업이 완료되면 콜백 함수가 자동으로 호출되어 결과를 처리합니다.

콜백은 동기 방식으로도 사용됩니다. 배열의 map, filter, forEach 같은 고차 함수(Higher-Order Function)에서 각 요소를 처리하는 함수로 콜백을 전달합니다. 이벤트 핸들러도 대표적인 동기 콜백의 예로, 사용자 클릭이나 키보드 입력 시 실행될 함수를 미리 등록해둡니다.

그러나 비동기 콜백을 중첩하여 사용하면 "콜백 지옥(Callback Hell)"이라 불리는 가독성 문제가 발생합니다. 이를 해결하기 위해 Promise, async/await 같은 패턴이 등장했지만, 콜백은 여전히 이벤트 리스너, 라이브러리 API, Node.js 코어 모듈 등에서 핵심 개념으로 사용되고 있습니다.

💻 코드 예제

// 1. 기본 콜백 패턴 - 동기 콜백
function greet(name, callback) {
    console.log(`안녕하세요, ${name}님!`);
    callback();  // 전달받은 함수를 호출
}

greet('홍길동', function() {
    console.log('환영합니다!');
});
// 출력: 안녕하세요, 홍길동님!
// 출력: 환영합니다!


// 2. 비동기 콜백 - setTimeout, 파일 읽기
console.log('1. 시작');

setTimeout(function() {
    console.log('2. 2초 후 실행됨');
}, 2000);

console.log('3. setTimeout 등록 후 바로 실행');
// 출력 순서: 1 → 3 → 2 (2초 후)


// 3. 에러 처리 패턴 (Node.js 스타일)
function fetchData(userId, callback) {
    // 비동기 작업 시뮬레이션
    setTimeout(() => {
        if (userId <= 0) {
            callback(new Error('유효하지 않은 사용자 ID'), null);
            return;
        }
        const user = { id: userId, name: '김철수' };
        callback(null, user);  // 첫 번째 인자가 에러, 두 번째가 결과
    }, 1000);
}

fetchData(1, (error, user) => {
    if (error) {
        console.error('오류:', error.message);
        return;
    }
    console.log('사용자:', user.name);
});


// 4. 배열 고차 함수에서의 콜백
const numbers = [1, 2, 3, 4, 5];

// map: 각 요소를 변환
const doubled = numbers.map(n => n * 2);
console.log(doubled);  // [2, 4, 6, 8, 10]

// filter: 조건에 맞는 요소만 선택
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens);  // [2, 4]

// reduce: 누적 계산
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum);  // 15


// 5. 이벤트 리스너 콜백 (브라우저 환경)
document.getElementById('myButton').addEventListener('click', function(event) {
    console.log('버튼이 클릭되었습니다!');
    console.log('클릭 위치:', event.clientX, event.clientY);
});

🗣️ 실무에서 이렇게 말하세요

💬 회의에서
"이 API는 비동기라서 콜백으로 결과를 받아야 합니다. 근데 콜백이 3단계 이상 중첩되면 가독성이 떨어지니까 Promise로 래핑하거나 async/await으로 리팩토링하는 게 좋을 것 같아요."
💬 면접에서
"콜백은 함수를 인자로 전달해서 나중에 호출하는 패턴입니다. 비동기 처리에서 많이 쓰이는데, 중첩이 깊어지면 콜백 지옥이 발생할 수 있습니다. 이를 해결하기 위해 Promise가 도입되었고, ES2017에서 async/await이 추가되어 동기 코드처럼 작성할 수 있게 되었습니다."
💬 코드 리뷰에서
"여기 콜백에서 에러 처리가 빠져있네요. Node.js 컨벤션대로 첫 번째 파라미터로 error를 받고, null 체크 후에 로직을 실행해주세요. 아니면 try-catch로 감싸서 예외를 잡아야 합니다."

⚠️ 흔한 실수 & 주의사항

콜백 지옥(Callback Hell) 피하기

콜백을 3단계 이상 중첩하면 코드가 피라미드 모양으로 들여쓰기가 깊어집니다. Promise, async/await을 사용하거나 콜백 함수를 별도로 분리하여 가독성을 높이세요.

에러 처리 누락

비동기 콜백에서 에러 파라미터를 무시하면 오류가 조용히 무시됩니다. Node.js 스타일의 error-first 콜백 패턴을 따르고, 항상 에러를 먼저 체크하세요.

this 바인딩 문제

일반 함수로 콜백을 전달하면 this가 예상과 다르게 바인딩될 수 있습니다. 화살표 함수를 사용하거나 .bind(this)로 명시적으로 바인딩하세요.

콜백의 적절한 사용

이벤트 리스너, 단순한 비동기 작업에는 콜백이 적합합니다. 복잡한 비동기 흐름에는 Promise나 async/await을 사용하고, 적재적소에 맞는 패턴을 선택하세요.

🔗 관련 용어

📚 더 배우기