🌟 CallBack 함수란?
"특정 상황이 되면 이 함수를 호출해줘" 라며 미리 등록해주는 함수
음식점에서 진동벨을 받고 자리에 앉아있는 것과 같다.
음식이 준비되면 → 진동벨이 울린다. (콜백 함수 호출)
손님은 카운터를 계속 바라볼 필요 없이 다른 일을 할 수 있다.
1단계 - 콜백 함수 타입 정의
typedef void (*pfLight_Status)(int nIndex, int nStatus);
// ↑ 함수 포인터 타입 이름
// ↑ 이 형태의 함수를 가리킬 수 있음
2단계 - 콜백 함수를 등록하는 함수
void SetCallBack_Status(void* _ctx, pfLight_Status _pfStatus);
// ↑ 어느 객체에서 호출할지 ↑ 어떤 함수를 호출할지
3단계 - 실제 콜백 함수 (호출할 함수)
void CCameraLightDlg::CallBackAlarm(int nIndex, int nStatus)
{
if (nStatus == 0) AfxMessageBox(_T("연결되었습니다^^"));
else AfxMessageBox(_T("연결 실패...ㅠㅠ"));
}
4단계 - 콜백 등록
m_pEthernet->SetCallBack_Status(this, CallBackAlarm);
// ↑ ↑
// 이 다이얼로그 호출할 함수
```
---
## 전체 흐름 정리
```
버튼 클릭
↓
BtnClickLightConnect() 실행
↓
SetCallBack_Status(this, CallBackAlarm) 등록
↓
ConnectTCP() 실행 (연결 시도 중...)
↓
연결 성공/실패 결과 나옴
↓
내부에서 등록된 CallBackAlarm(0, 결과값) 자동 호출
↓
"연결되었습니다^^" or "연결 실패...ㅠㅠ" 메시지 출력
콜백을 쓰는 이유는?
// 콜백 없는 방식 - 결과가 나올 때까지 무한 대기
while(연결중) {
// 프로그램이 멈춰버림
}
// 콜백을 쓰면
// 콜백 방식 - 연결 시도하고 다른 작업 가능
ConnectTCP(); // 연결 시도만 하고
// → 결과가 나오면 알아서 CallBackAlarm이 불림
// → 그동안 프로그램은 다른 작업 가능
즉, 프로그램이 멈추지 않고 결과를 받을 수 있어서 사용함.
🌟 내가 이해한 CallBack 함수 (대리님이 만들어주신 예제 코드를 보며 정리)
콜백 함수가 무엇인가?
콜백 함수를 한 문장으로 표현하면 이렇다.
"지금 당장 실행하는 게 아니라, 나중에 특정 조건이 되면 불러줘!" 라고 미리 등록해두는 함수
예를 들어, 친구에게 이렇게 말하는 상황이다.
"나 밥 다 되면 불러줘. 그때 내가 직접 가져갈게."
여기서 "내가 직접 가져갈게."에 해당하는 행동이 바로 콜백 함수다.
밥이 다 된 시점(=조건 충족)에 친구(=다른 클래스/함수)가 나 대신 내 행동을 실행시켜주는 거다.
예제 코드 구조 한 눈에 보기
| 클래스 | 역할 |
| CFuncTestDlg | 버튼을 누르고, 결과를 화면에 표시하는 다이얼로그 |
| CCalcTest | 실제 계산 (합계/곱셈/뺄셈)을 수행하는 클래스 |
핵심은 CCalTest가 계산을 끝낸 뒤 결과를 어떻게 처리할지 직접 알 필요가 없다는 점이다. 그냥 콜백 함수를 불러주기만 하면 되니까!
단계별 코드 흐름 해설
step 1 ㅡ 콜백 함수 타입을 미리 정의한다
typedef void (*ComFunc)(void *ctx, int Res);
ComFunc 라는 이름으로 함수 포인터 타입을 만들어 둔 거다.
Void* 를 인자로 받는 이유는 어떤 객체든 받을 수 있게 하기 위해. (나중에 다이얼로그 주소를 여기에 담아 넘겨줌.)
step 2 ㅡ 콜백 함수를 미리 만들어 둔다.
// 선언 (헤더파일)
static void Excute(void* ctx, int Res);
// 구현 (cpp 파일)
void CFuncTestDlg::Excute(void* ctx, int Res)
{
CFuncTestDlg* dlg = (CFuncTestDlg*)ctx;
dlg->SetDlgItemInt(IDC_STATIC_1, Res);
}
Excute 함수가 바로 콜백 함수.
ctx로 받은 void*를 CFuncTestDlg*로 변환해서 다이얼로그의 함수를 호출.
"왜 static으로 선언했을까?"
일반 멤버 함수는 숨겨진 this 포인터가 붙어 있어서 함수 포인터로 넘기기가 까다롭다.
static 함수는 그런 제약이 없어서 주소를 깔끔하게 넘길 수 있다.
step 3 ㅡ 버튼을 누르면 콜백 함수를 등록하면서 요청한다.
void CFuncTestDlg::OnBnClickedButton1()
{
c1.SettingCalc(this, 0, m_nArr, Excute);
// ^^^^ ^ ^^^^^^ ^^^^^^
// ctx 타입 배열 콜백함수 주소
}
버튼1을 누르면 SettingCalc를 호출한다.
4번째 인자로 Excute의 함수 주소를 넘겨주고 있다.
CCalcTest 입장에서는 "계산 끝나면 이 주소에 있는 함수 불러!" 라는 명령을 받은 셈.
step 4 ㅡ CCalcTest 내부에서 계산 후 콜백을 실행한다
void SettingCalc(void* ctx, int nType, int(&narr)[4], ComFunc Func)
{
m_ctx = ctx;
m_Func = Func; // 콜백 함수 주소를 저장!
int nRes = 0;
switch (m_nType) {
case 0: nRes = TotalSum(narr); break;
case 1: nRes = TotalMul(narr); break;
case 2: nRes = TotalSub(narr); break;
}
m_Func(m_ctx, nRes); // 저장해뒀던 콜백 함수 실행!
}
계산이 끝나고 나서 m_Func(m_ctx, nRes)로 콜백을 직접 호출.
이때 Excute 함수가 실행되고, 결과가 화면에 표시된다.
버튼별 실행 결과
배열 값은 {4, 7, 8, 9} 로 고정되어 있다.
| 버튼 | nType | 연산 함수 | 계산식 | 결과 |
| Button1 | 0 | TotalSum | 4 + 7 + 8 + 9 | 28 |
| Button2 | 1 | TotalMul | 4 x 7 x 8 x 9 | 2016 |
| Button3 | 2 | TotalSub | 0 - 4 - 7 - 8 - 9 | -28 |
핵심 포인트 정리
| 질문 | 답변 |
| 콜백 함수란? | 내가 직접 호출한 게 아니라, 다른 쪽에서 조건이 충족되면 대신 불러주는 함수 |
| 왜 쓰나? | CCalcTest가 결과 처리 방법 몰라도 됨. 콜백만 바꾸면 화면 출력, 파일 저장 등 자유롭게 교체 가능 |
| void* ctx가 왜 필요한가? | 콜백 내부에서 다이얼로그 객체의 함수를 호출해야 하기 때문에, 객체 주소를 담아 함께 넘겨주는 용도 |
| static은 왜 붙이나? | 멤버함수는 this 포인터 때문에 함수 포인터로 넘기기 어려움. static이면 깔끔하게 넘기기 가능 |
마치며
콜백 함수는 처음 보면 흐름이 왔다 갔다해서 헷갈리기 쉽다.
하지만 핵심은 하나,
"함수의 주소를 미리 넘겨두고, 나중에 불러달라고 예약하는 것."
이번 예제처럼 계산 클래스(CCalcTest)가 결과 처리 방식(Excute)을 직접 알 필요 없이,
콜백만 교체하면 동작을 바꿀 수 있다는 게 콜백 함수의 가장 큰 장점.

'MFC_C++ > Task' 카테고리의 다른 글
| MFC_C++ 스레드 (Thread) (0) | 2026.04.20 |
|---|---|
| MFC_C++ 멀티바이트 vs 유니코드 (0) | 2026.04.03 |
| MFC_C++ 윈도우 계산기 리펙토링 (0) | 2026.03.19 |
| MFC_C++ 배열 (0) | 2026.03.03 |