1. 효율적 비동기 처리, 콜백 함수 (Callback function) 개요
| 개념 | 코드 재사용, 비동기 처리를 위해 특정 작업이 완료된 후 실행되도록 다른 코드의 인수로 넘겨주는 실행 가능한 코드 | |
|---|---|---|
| 사용 목적 | 이벤트 핸들링 | – 특정 이벤트에 대한 처리 함수를 등록 후 해당 이벤트 발생 시 호출 |
| 비동기 수행 | – Network 통신 등 오랜 시간이 소요되는 작업의 Non-Blocking I/O 처리 | |
| 추상화/다형성 | – 함수 내부 로직 수정없이 외부 함수를 통해 원하는 동작을 자유롭게 교체 | |
- 콜백 함수 전달 시 콜백 함수의 포인터 (핸들), 서브루틴 또는 람다함수 형태로 넘겨주며, 효율적인 비동기(Async) 처리와 성능 최적화, 코드 재사용성 극대화, 이벤트 기반(Event-driven) 아키텍처 구현이 가능
2. 콜백 함수의 처리 절차 및 구성요소
(1) 콜백 함수의 처리 절차
| ① | Callback 함수 등록 및 비동기 작업 수행 | – 메인 스레드의 Call Stack에서 비동기 태스크와 콜백 함수 전달/등록 후 수행 |
| ② | 비동기 작업 완료 시 Task Queue 등록 | – 백그라운드 영역에서 비동기 작업 완료 시 해당 콜백 함수를 Task Queue에 등록 |
| ③ | Call Stack 확인 및 dispatch | – 이벤트 루프가 Call Stack이 완전히 비어 있는지 확인한 후 콜백 함수를 꺼내 CPU가 수행할 수 있도록 dispatch |
| ④ | Call Stack Push, Main Thread 완료 | – 이벤트 루프가 콜백을 Call Stack으로 Push하여 메인 스레드에서 연산 완료 |
(2) 콜백 함수의 구성요소
| 구분 | 구성요소 | 역할 |
|---|---|---|
| 데이터 전달 /실행 | 호출자 (Caller) | – 콜백 함수를 매개변수로 전달받아 내부 로직에서 적절한 시점에 실행 – 콜백 함수 제어권 위임받아, 비동기 작업 완료 여부에 따라 콜백 호출 |
| 호출 대상 (Callee) | – 호출자(Caller)에게 인자로 넘겨져 나중에 실행되도록 예약된 함수 – 독립된 로직으로 호출자가 넘겨주는 데이터를 넘겨 받아 후속 연산 | |
| 런타임 제어 | 태스크 큐 (Task Queue) | – 백그라운드 작업 후, 실행 대기 콜백 함수들이 FIFO 구조로 적재 – 콜백 함수들이 소실되지 않도록 순서대로 안전하게 보관 |
| 이벤트 루프 (Event Loop) | – 호출 스택(Call Stack)과 태스크 큐(Task Queue)의 상태를 상시 감시 – 태스크 큐에서 대기 중인 콜백 함수를 호출 스택으로 이동(Dispatch) | |
| 실행 보장 | 실행 컨텍스트 (Execution Context) | – 콜백 함수 선언 당시 주변의 변수와 스코프(Scope) 기억하는 메모리 – 나중에 실행되는 콜백 함수의 선언 당시의 상태와 변수 데이터에 접근 |
| 에러 핸들러 (Error Handler) | – 비동기 작업 성공 여부 판별, 예외 처리 위한 방어적 매개변수/분기 – 콜백 내부로 에러 상태를 명확히 전달, 안정적 예외 복구 보장 |
- 프로그램에서 콜백 함수 사용 시 호출자와 콜백이 데이터를 교환, 태스크 큐와 이벤트 루프(런타임)가 실행 타이밍을 조율하며, 클로저와 에러 핸들러(안정성)가 환경과 예외를 방어하는 유기적인 조합으로 수행
3. 콜백 함수 사용 시 고려사항 및 예제
(1) 콜백 함수 사용 시 고려사항
| 구분 | 고려사항 | 목표 |
|---|---|---|
| 오류 관리 | 예외 처리의 분리 | – 예외 발생 시 프로세스 수행 보장 위해 콜백 내부에서 에러 처리 – 에러 인자 존재 시 런타임 예외 로깅 및 리턴하는 방어적 코드 작성 |
| 콜백 지옥과 가독성 관리 | – 코드의 뎁스(Depth)가 3단계 이상 깊어지지 않도록 모니터링 수행 – Promise, async/await 문법으로 리팩토링, 동기식 코드로 구조 개선 | |
| S/W 제어 | 메모리 누수 방지 | – 이벤트 리스너 등 반복 등록 콜백 사용 후 적절한 해제 여부 확인 – removeEventListener 호출, 가비지 컬렉터(GC)로 메모리 정상 수거 |
| 제어권/신뢰성 확보 | – 실행 횟수, 실행 가능성, 실행 속도를 고려하여 프로세스 제어권 확보 – 의도치 않은 다수 호출 방지(once 플래그 래퍼) 등 신뢰성 모듈 탑재 |
(2) 콜백 함수 예제
# 1. 나중에 실행될 콜백 함수를 정의
def send_sms(user):
print(f"[콜백 실행] {user}님에게 '회원가입 완료' 문자를 발송했습니다.")
# 2. 메인 작업을 하는 함수이며, 인자로 콜백 함수 입력
def register_user(user_name, callback):
print(f"[메인 작업] {user_name}님의 정보를 DB에 저장하는 중...")
print("[메인 작업] 회원가입 처리가 완료되었습니다.")
# 3. 메인 작업 종료 시 넘겨받은 콜백 함수를 실행(Call Back)
callback(user_name)
# 4. 함수 호출 시 콜백 함수명을 인자로 전달
register_user("나신입", send_sms) - 회원가입 함수(register_user())에 콜백 함수인 send_sms를 인자로 위임 등록하고, 회원 가입 처리가 완료된 시점에 전달받은 콜백을 최종 호출해 실행
[결과]
| [메인 작업] 나신입님의 정보를 DB에 저장하는 중… [메인 작업] 회원가입 처리가 완료되었습니다. [콜백 실행] 나신입님에게 ‘회원가입 완료’ 문자를 발송했습니다. |
- 현대의 대규모 백엔드 아키텍처는 단발성 비동기 처리보다 지속적인 대량의 데이터 스트림 처리가 많아지고 있으므로, 옵저버 패턴(Observer Pattern) 등 반응형 프로그래밍과 서버리스, 이벤트 기반 아키텍처를 통해 프레임워크로 내재화 필요
[참고]
- Wikipedia, Callback (computer programming)
- MDN Web Doc, Callback function
- Massachusetts Institute of Technology(MIT), Callbacks and Graphical User Interfaces, Spring 2022