C++ 콜백 함수(Callback Function) 이해하기

  • 최초 작성일: 2025년 6월 25일 (수)

목차

  1. 콜백 함수란?
  2. 콜백 함수가 필요한 이유
  3. 기본적인 콜백 함수 예제
  4. 클래스 멤버 함수 콜백 예제
  5. std::function을 이용한 콜백
  6. 콜백 사용 시 주의할 점
  7. 결론

콜백 함수란?

콜백 함수는 특정 이벤트가 발생하거나 조건이 충족되었을 때 자동으로 호출되는 함수이다. 일반적으로 함수의 포인터나 객체를 통해 전달되어 프로그램 실행 중 특정 시점에 실행된다. 주로 이벤트 기반 프로그래밍이나 비동기 처리 방식에서 많이 활용된다.

콜백 함수가 필요한 이유

콜백 함수가 사용되는 대표적인 이유는 다음과 같다:

  • 비동기 처리: 프로그램이 작업을 수행하는 동안 다른 이벤트나 작업이 완료되었을 때 특정한 동작을 실행할 수 있다.
  • 유연한 설계: 호출되는 함수의 동작을 사용자가 원하는 대로 정의할 수 있다.
  • 모듈 간 낮은 결합도 유지: 함수를 직접 호출하지 않고, 콜백을 통해 간접적으로 호출함으로써 코드의 모듈 간 의존성을 낮춘다.

기본적인 콜백 함수 예제

가장 단순한 형태의 콜백 함수 예제를 살펴보자. 아래의 예제는 두 정수를 더한 결과를 콜백 함수를 통해 전달하고 출력한다.

#include <iostream>

// 콜백 함수 정의
void CallbackFunction(int result) {
    std::cout << "콜백이 호출됨, 결과값: " << result << std::endl;
}

// 콜백을 실행하는 함수 정의
void PerformTask(int a, int b, void(*callback)(int)) {
    int sum = a + b;
    callback(sum);  // 콜백 함수 호출
}

int main() {
    PerformTask(5, 10, CallbackFunction);  // 콜백 함수 전달
    return 0;
}

출력:

콜백이 호출됨, 결과값: 15

클래스 멤버 함수 콜백 예제

클래스의 멤버 함수를 콜백으로 사용할 경우를 예제로 들어 보자. 이 경우는 일반 함수 포인터 대신 std::function을 활용한다.

#include <iostream>
#include <functional>

class Calculator {
public:
    void Add(int a, int b, std::function<void(int)> callback) {
        int sum = a + b;
        callback(sum);  // 결과값을 콜백 함수로 전달
    }
};

class ResultHandler {
public:
    void DisplayResult(int result) {
        std::cout << "결과: " << result << std::endl;
    }
};

int main() {
    Calculator calc;
    ResultHandler handler;

    // 클래스 멤버 함수를 람다 표현식으로 감싸서 콜백 전달
    calc.Add(7, 3, [&handler](int result) {
        handler.DisplayResult(result);
    });

    return 0;
}

출력:

결과: 10

std::function을 이용한 콜백

C++11 이상에서 제공하는 std::function은 다양한 호출 가능 객체(함수 포인터, 멤버 함수 포인터, 람다 표현식 등)를 통합적으로 관리할 수 있도록 돕는다. 다음은 파일 읽기 작업을 시뮬레이션한 직관적이고 이해하기 쉬운 예제이다.

#include <iostream>
#include <functional>
#include <string>

void ReadFile(const std::string& filename, std::function<void(const std::string&)> callback) {
    // 파일 읽기 작업을 시뮬레이션
    std::string fileContent = "파일 '" + filename + "' 의 내용을 성공적으로 읽었습니다.";

    // 읽기 작업 완료 후 콜백 호출
    callback(fileContent);
}

int main() {
    ReadFile("example.txt", [](const std::string& content) {
        std::cout << "콜백 호출됨: " << content << std::endl;
    });

    return 0;
}

출력:

콜백 호출됨: 파일 'example.txt' 의 내용을 성공적으로 읽었습니다.

콜백 사용 시 주의할 점

  • 콜백 함수가 호출되는 시점의 상태를 명확하게 관리해야 한다. 예를 들어, 객체가 이미 파괴되었거나 자원이 유효하지 않을 때 호출되는 것을 방지해야 한다.
  • 콜백 구조를 지나치게 복잡하게 설계하면 유지보수하기 어렵고 디버깅이 힘들어진다. 가능한 간결하고 명확한 설계를 유지하는 것이 좋다.

결론

콜백 함수는 함수 포인터, std::function, 람다 표현식을 통해 다양한 방식으로 구현할 수 있으며, 이를 효과적으로 사용하면 더욱 유연하고 유지보수하기 쉬운 프로그램 설계를 할 수 있다. 특히, 비동기 처리 및 이벤트 기반 프로그램에서 그 효용성이 더욱 두드러진다.