Vana Blog

Node.js Design Pattern - callback 패턴

April 07, 2019

Node.js의 비동기 패턴

callback(callback)

callback은 작업 결과를 전달하기 위해 호출되는 함수. 비동기 작업을 처리할 때 반드시 필요하다. 이들은 항상 동기적으로 실행되는 return 명령의 사용을 대신한다. callback을 구현하는 이상적인 구조는 클로저(closures)이다. 클로저를 사용하면 실제로 함수가 작성된 환경을 참조할 수 있다. callback이 언제 어디서 호출되는 지 관계없이 비동기 작업이 요청된 컨텍스트를 항상 유지할 수 있기 때문이다.

연속 전달 방식(The Continuation-Passing Style)

결과를 호출자에게 직접 반환하는 대신 다른 함수(callback)로 결과를 전달.

<동기식 연속 전달 방식>

function add(a, b) {
  return a + b
}

위와 같은 스타일은 직접 스타일(Direct Style). 이것을 CPS로 바꾸면

function add(a, b, callback) {
  return callback(+ b)
}

<비동기 연속 전달 방식>

동기와 비동기 방식의 실행 차이. 아래의 코드를 실행시켜보면 콘솔에 결과가 다르게 찍히는 것을 확인할 수 있다.

// sync
function add(a, b, callback) {
  return callback(+ b);
}
console.log('------------------------------------sync before');
add(1, 2, result => console.log('Result: ', + result));
console.log('------------------------------------after');
 
// async
function additionAsync(a, b, callback) {
  setTimeout(() => callback(+ b), 100);
}
 
console.log('------------------------------------async before');
additionAsync(1, 2, result => console.log('Result: ', + result));
console.log('------------------------------------after');

setTimeout()은 비동기로 실행되기 때문에 callback실행을 기다리지 않고 즉시 additionAsync()로 제어를 넘긴다. Node.js의 속성은 비동기 요청이 전달된 후 즉시 제어를 이벤트 루프에 돌려 큐에 있는 새로운 이벤트가 처리될 수 있도록 한다.

<비 연속 전달(Non-continuation-passing) 방식의 callback>

// array.map API
arr.map(callback(currentValue[, index[, array]])[, thisArg])
 
const result = [1,5,7].map(element => element -1);
console.log(result); // [0,4,6]

Array 객체의 map()함수의 callback은 배열 내의 요소를 반복하는데 사용될 뿐 연산 결과를 전달하지 않는다.

동기? 비동기?

<지연실행(Deferred execution)> Node.js의 process.nextTick()은 이벤트 루프의 다음 사이클까지 함수의 실행을 지연시킨다. process.nextTick()을 사용하여 실행을 연기함으로써 callback이 비동기적 호출을 보장할 수 있다.

Node.js callback 규칙

  1. callback은 맨 마지막 인자로 전달한다. - 함수 호출의 가독성이 더 좋다.
  2. 오류는 맨 앞에 - CPS로 생성된 함수의 오류는 항상 callback의 첫 번째 인수로 전달된다.

    fs.readFile('foo.txt', 'utf8', (err, data) => {
      if(err)
        handleError(err);
      else
        processData(data);
    })
  3. 오류 전파 - CPS에서 오류를 전달할 때 return문 사용.
  4. 캐치되지 않는 예외

Vana Yun

Written by Vana Yun