쾌락코딩

비동기 프로그래밍 작성(feat.싱글스레드)

|

새로운 기술을 배우는데 급했고, React, Django, Express.js 같은 라이브러리, 프레임워크를 공부하는데 재미가 있어서 내가 좋아하는 node.js의 기본 원리를 대충 넘어간게 아닌가 싶다. node.js를 떠올리면 가장 먼저 떠오르는 비동기 프로그래밍, 싱글 스레드에 대해서 우선 적당히 알아보자!

비동기 프로그래밍

말은 많이 들어봤다. 무엇을 뜻하는지도 알고 있다. 시간이 많이 걸리는 file I/O나 network I/O 처리 요청을 운영체제에 전달하고 CPU는 다른 연산을 계속 진행하는 것. 따라서 CPU 사용 효율성이 늘어나고, 요청했던 I/O처리의 값이 돌아오면 콜백으로 값을 받아 처리하는 것이다. 자바스크립트나 Node.js로 코딩을 조금이라도 해본 사람들이라면 이런 작업들이 코드상에서 callback 함수로써 등장하게 된다는 것을 알고 있을 것이다. 많은 I/O 처리 라이브러리들의 사용법을 보면(특히 자주 쓰는 데이터베이스 라이브러리를 생각해보자), 보통 아래와 같은 식이기 때문이다.

// 코드1
// 비동기 함수 호출
// db에서 option에 해당하는 유저를 찾는 함수라고 생각하자
asyncFunc(option,(err,result) => {
  //blablabla
});

많이 봐서 익숙하다! node.js는 비동기로 처리한다고 배웠으니 위의 함수가 실행되는 동안 그 가만히 기다리지 않고 다른 연산을 수행하다가 결과가 도착하면 err나 result를 다루면 되겠구나?! 아니. 과연 이걸 훌륭하다고 말할 수 있을까? 이 정도 이해 상태라면 그저 비동기 == 콜백이 된다. 그냥 라이브러리를 가져다 쓰는데에 무리는 없겠지만 우리가 비동기 함수를 직접 만들 때 다 들통난다.

아래의 콘솔 출력 순서를 맞춰보자.

// 코드2
const func = (msg, cb) => {
  console.log(msg);
  //시간이 오래 걸리는 작업
  //예를 들어 네트워크 응답
  cb(null, 'callback function call');
};

//호출
func('hello world', (err, result) => {
  console.log(result);
});

//다른 작업
console.log('실행');

func 함수를 호출하는 방법은 코드1과 비슷하다. 함수의 첫번째 인자로 option(기타 등등)을 주고, 콜백함수의 인자로 err나 결과를 기다린다. 실행 순서는 어떻게 될까? 이 함수는 우리가 흔히 라이브러리를 쓸때 보았던 형식과 비슷하게 콜백을 사용하므로 비동기적으로 동작할까?

답은 ‘hello world’ -> ‘callback function call’ -> ‘실행’ 순서이다. 나 같은 초보는 처음 봤을 때, ‘hello world’ -> ‘실행’ -> ‘callback function call’ 라고 생각할 수 도 있다. 늘 저렇게 콜백을 받는 함수는 비동기식으로 진행되어왔고 그걸 그냥 사용만 해보았으니까!

그러나 코드2는 겉보기에만 우리가 익숙한 비동기 코드형태일 뿐 사실은 동일한 스레드 위에서 동기적으로 동작하는 함수이다. 생각해보면 당연한 일이다. 코드는 위에서 아래로 진행되고, 심지어 어디선가 node.js는 싱글스레드라는 말을 들은적이 있으니 당연한것 아닌가? 그럼 어떻게 비동기 적으로 시간이 오래 걸리는 I/O작업을 처리할 수 있을까?

우선은 한가지 오해부터 잡고 가자. node.js가 싱글 스레드이지만, 모두 같은 스레드에서 동작하는 것은 아니다. 내가 제대로 넘겨준 콜백을 처리하는 스레드는 다른 스레드라는 정도로만 알고 있자(이게 아니라면 알려주십쇼ㅠ). 그래서 다른 연산을 하는 중에도 비동기로 넘긴 코드가 실행이되는 것이다.

아무튼 이제 func 함수 내부에서 비동기적으로 콜백처리를 해보자. 키워드는 바로 process.nextTick 함수다. process.nextTick 함수는 첫 번째 인자로 함수를 넘겨주고, 이후 인자들에 파라미터를 넘겨주게 된다. 코드2에 적용시켜서 실행 순서를 ‘hello world’ -> ‘실행’ -> ‘callback function call’ 이렇게 바꿔보자.

// 코드3
const func = (msg, cb) => {
  console.log(msg);
  //시간이 오래 걸리는 작업
  //예를 들어 네트워크 응답
  process.nextTick(cb,null,'callback function call');
};
 
 //호출
func('hello world', (err, result) => {
  console.log(result);
});

//다른 작업
console.log('실행');

이제 드디어 비동기적으로 동작하는 코드가 완성되었고 실행순서는 ‘hello world’ -> ‘실행’ -> ‘callback function call’ 가 된다!

결론

  • process.nextTick 함수를 이용하면 비동기적 코드를 작성할 수 있다.

더 좋은 자료

Comments