자바스크립트는 변수에 정적인 데이터 뿐만 아니라, 함수를 할당할 수 있다. (함수를 변수의 일종으로 생각하는 방식이다) 이러한 특징으로 인해, 메소드의 파라미터로 다른 메소드를 대입할 수 있거나, 반환값으로 메소드 자체를 지정할 수 있다.
이러한 함수(메소드)들을 콜백 메소드(Callback Method) (또는 콜백 함수)라고 부른다
function add(a, b, callback){
var result = a + b;
callback(result);
}
function callback(result){
console.log('결과 : %d', result);
}
비동기 프로그래밍(Non-Blocking Programming)
함수를 파라미터로 전달하는 (콜백 메소드가 사용되는) 경우는 대부분 비동기 프로그래밍 방식으로 코드를 만들 때이다.
비동기 프로그래밍을 이해하기 전에 동기 프로그래밍을 이해하면 조금 더 이해가 쉬울 것이라 생각한다. 동기 프로그래밍은 코드를 위부터 아래 방향으로 읽으면서, 해당 줄의 모든 실행이 끝나야 다음 줄의 실행을 하는 방식이다. 코드만 봐도 런타임에서의 실행 순서를 짐작할 수 있는 방식이다. 코딩은 상당히 편하지만, I/O 의 실행이 많은 경우에는 적합하지 않다. 프로세스 실행 속도에 비해 현저히 느린 I/O 의 연산을 마냥 기다리고 있는 동기 프로그래밍 방식은 I/O 처리가 많을수록 느려질 수 밖에 없다.
자 이제, 우리의 비동기 프로그래밍을 보도록 하자. 비동기 프로그래밍은 위에서 아래로 코드를 읽어 실행하는 것은 동일하나, 하나의 명령에 대한 I/O 실행을 마냥 기다리고 있지 않는다. 이 친구는 I/O 실행이 시작되면, 바로 프로세스 상의 다음 명령을 실행해 버린다.
이는 상당히 편하다고 볼 수 있으나 다음과 같이 안일하게 코딩을 한다면 오류를 뿜뿜한다.
//C Language
// 파일 열기
FILE *fp = fopen("test.txt", "r");
// 문자열 읽기
fgets(buffer, sizeof(buffer), fp);
printf("%s\n", buffer);
// 파일 닫기
fclose(fp);
파일 시스템을 이해하고 있다면, 아무 문제 없어보이는 코드지만, 해당 환경이 비동기 프로그래밍이라면 이야기가 다르다.
비동기 프로그래밍 환경에서는 fopen(…); 명령(4행)이 시작되었을 때, I/O 시스템이 ‘test.txt’ 파일을 읽는 것을 기다리지 않고 바로 다음 줄의 명령(5행)을 실행해버린다. 아직 다 읽혀지지 않은 파일에 대한 연산을 시작하려고 하면, 당연히 모종의 이유로 오류가 날 수 밖에 없다.
그렇다면, 비동기 프로그래밍에 연산의 절대적인 순서를 배정하기 위해선 어떻게 해야하는가? 그건 다음 줄을 읽어보자
그래서, 콜백 메소드가 필요합니다.
// 파일 시스템 로드
var fs = require('fs');
// 파일 읽기 시도
console.log("test.txt 파일을 읽어옵니다.");
fs.readFile('test.txt', 'utf8', displayData(err, data));
console.log("test.txt 파일을 전부 읽었습니다.");
// 콜백 메소드
var displayData = function(err, data){
console.log(data);
}
파일 읽기의 명령은 보다시피 3개의 인자로 이루어져 있는데, 대상 파일(1), 인코딩 형식(2), 콜백 메소드(3) 로 이루어져 있다.
비동기 프로그래밍 환경에서는 프로세스가 I/O 작업을 대기하지 않기 때문에, 7행 실행 이후에 바로 8행이 시작된다. 그리고, 7행의 I/O 작업이 끝남과 동시에 콜백 메소드로 지정된 displayData() 가 실행되는 구조를 이루고 있다.
그래서 위의 코드는 아래와 같은 실행 결과를 가져온다.
# test.txt 파일을 읽어옵니다. // 6행 consol.log
# test.txt 파일을 전부 읽었습니다. // 8행 console.log
# test.txt 파일의 내용.....으어어어... // 12행 console.log