728x90


javascript는 callback으로 처리를 하는 경우가 많다.

정확히 말하면 nodejs가 callback형으로 많이 처리를하지만 brower js에도 역수입이 되서 요즘에는 brower js도 callback으로 많이 처리한다.

물론 둘이는 조금은 다르지만 쓰는 방식이 비슷하므로 똑같이 적용해도 큰 무리는 없다.


어쨋든 callback을 이용한 비동기 루틴은 nodejs에서 최고의 효율을 내지만 한편으로는 callback 지옥이라는 새로운 영역으로 발을 들이게 된다.


npm install --save axios


일단 axios를 추가해주자. ajax를 사용하게 해주는 라이브러리이다.

아래의 코드를 보자.


var axios = require('axios');

//main.js
function f1(callback) {
axios.get('https://www.naver.com').then((result) => {
console.log('function1 main : ' + result.status);
callback();
});
}

function f2(callback) {
axios.get('https://www.daum.net').then((result) => {
console.log('function2 main : ' + result.status);
callback();
});
}

function f3(callback) {
axios.get('https://nate.com').then((result) => {
console.log('function3 main : ' + result.status);
callback();
});
}

var globalNum = 10;

f1(() => {
console.log('function1 callback');
f2(() => {
console.log('function2 callback');
f3(() => {
console.log('function3 callback');
});
});
});

console.log('main : ' + globalNum);

전통적인 js코드를 보자.

3개의 함수가 모두 콜백을 호출하도록 되어 있다.

각각의 함수는 axios를 호출헤서 network 자원을 사용하므로 이는 async하게 작동하게 된다.

이의 실행 흐름이 예측이 되는가?


먼저 main스크립트가 실행된다.

실행되면서 function1의 콜백이 적제된다. 그후 function2의 콜백, function3의 콜백이 적재된다.

main이 끝나면 먼저 끝난 순서대로 콜백이 실행하게 된다.

그리하여 결과는 아래와 같다.


뭐 당연하다.

main이 먼저 실행된다. 그다음은 axios의 콜백으로 작동하므로 순서대로 작동하게 된다.

콜백을 체이닝했으므로 당연히 순서대로 작동하게 된다.

하지만 이렇게 짜게 되면... 영원히 콜백으로 들여쓰기를 하게 된다.

그러면 코드에서 마치 비행기 마냥 V표로 펼쳐지게된다.


이러한 코드를 그냥 쓰겠다면 상관 없지만...

보기 매우 짜증난다. 그냥 보기만 해도 혈압오르는 코드다.

아.. 물론 안그런사람들도 있다. 필자는 그렇다.

그럼 이런경우 어떻게 처리하게 될까?

이 때는 총 3가지의 방법이 있다.

하나는 키워드사용, async 패키지, promise 패키지 사용이다.

그 중에 async는 이미 필자가 올렸다.

근데 요즘에 권장되는 패키지는 아니다. 요즘에는 promise를 자주 사용한다.

그러니 promise를 사용하도록 해보자.


var axios = require('axios');

function func1(parent_func) {
return new Promise((resolve, reject) => {
axios.get('http://www.naver.com').then((result) => {
console.log('function1 routine**********');
console.log(parent_func);
resolve('function1에서 왔습니다.');
});
});
}

function func2(parent_func) {
return new Promise((resolve, reject) => {
axios.get('http://www.daum.net/').then((result) => {
console.log('function2 routine**********');
console.log(parent_func);
resolve('function2에서 왔습니다.');
});
});
}

var start_str = 'main에서 왔습니다.';

console.log('main routine**********');
func1(start_str).then((parent_func) => {
console.log('function1 callback**********');
console.log(parent_func);
return parent_func
}).then(func2);

console.log("main 종료");

로직이 복잡해 보이지만 전혀 복잡하지 않다.

하나하나 뜯어보도록 하자.


function func1(parent_func) {
return new Promise((resolve, reject) => {
axios.get('http://www.naver.com').then((result) => {
console.log('function1 routine**********');
console.log(parent_func);
resolve('function1에서 왔습니다.');
});
});
}

이 함수를 보자.

반환으로 Promise객체를 반환하는데 Promise객체는 익명함수를 인자로 받는다.

이 익명함수의 파라메터는 resolve와 reject를 받는다.

reslove는 성공했을때, reject는 실패했을 때 사용하는 함수이다.

이번에는 실패했을 경우를 하지 않도록 하겠다.

모든 경우는 성공했다고 가정하겠다.


axios.get('http://www.naver.com').then((result) => {
console.log('function1 routine**********');
console.log(parent_func);
resolve('function1에서 왔습니다.');
});

내부에 axios의 콜백을 보면 파라메터로 받은 문자열을 출력하고 마지막에 콜백을 다시 던진다.

실행해보도록 하자.


실행했을 때의 결과가 예상이 됬는가?

가장 먼저 main루틴이 실행된다.


func1(start_str).then((parent_func) => {
console.log('function1 callback**********');
console.log(parent_func);
return parent_func
}).then(func2);

func1이 먼저 실행된다.

그래서 function1 routine이 실행된다. 그리고 function1루틴에서 콜백(resolve)을 다시 던진다.

이제 resolve루틴이 실행된다. 그리고 이 루틴에서 func2의 파라메터로 던질값을 return에 넣어준다.

이 return값은 다음 함수의 파라메터로 들어가게 된다.

연결해서 실행할 함수는 .then으로 실행하면된다.


function func2(parent_func) {
return new Promise((resolve, reject) => {
axios.get('http://www.daum.net/').then((result) => {
console.log('function2 routine**********');
console.log(parent_func);
resolve('function2에서 왔습니다.');
});
});
}

이 코드를 보면 func1과 사실 거의 동일한 코드이다.

이 코드도 마지막에 resolve로 콜백을 던진다.

그러나 우리는 추가적으로 붙힌게 없으므로 여기서 추가적인 callback이 호출되지는 않는다.


var axios = require('axios');

function func1(parent_func) {
return new Promise((resolve, reject) => {
axios.get('http://www.naver.com').then((result) => {
console.log('function1 routine**********');
console.log(parent_func);
resolve('function1에서 왔습니다.');
});
});
}

function func2(parent_func) {
return new Promise((resolve, reject) => {
axios.get('http://www.daum.net/').then((result) => {
console.log('function2 routine**********');
console.log(parent_func);
resolve('function2에서 왔습니다.');
});
});
}

function func3(parent_func) {
return new Promise((resolve, reject) => {
axios.get('http://www.daum.net/').then((result) => {
console.log('function3 routine**********');
console.log(parent_func);
resolve('function3에서 왔습니다.');
});
});
}


var start_str = 'main에서 왔습니다.';

console.log('main routine**********');
func1(start_str).then((parent_func) => {
console.log('function1 callback**********');
console.log(parent_func);
return parent_func
}).then(func2)
.then(func3);

console.log("main 종료");

콜백을 추가적으로 또 붙힐경우 .then을 추가해주면된다.


제대로 작동한다.


그럼 실패했을 경우는 어떻게 해야하는가??

그것 다음에 알려드리도록 하겠다.



+ Recent posts