toggle menu

[AngularJS] AngularJS의 $q 와 ES6 Promise 의 차이

2014. 12. 24. 15:11 AngularJS

Promies는 비동기적인 요청과 응답의 복잡성을 다루는데 가장 좋은 해결책이다. 지난 포스팅 바보들을 위한 Promise 강의 - 도대체 Promise는 어떻게 쓰는거야? 에서 Promise가 무엇이고 어떻게 사용하는지에 대해서 설명했다.


AngualrJS 역시 이러한 비동기 작업/요청에 대한 처리를 위해 $q, $http 등의 서비스를 통해서 Promise 를 지원하고 있다. Kris Kowal's Q 에서 영향을 받아 $q 라는 이름으로 서비스명을 지었는데, 실제 Kris Kowal's Q 와는 두 가지 정도 차이가 있다.


하나는 $q의 경우 $rootScope.Scope 와 통합되어 있기 때문에 resolution 혹은 rejection 여부가 보다 빠르게 전파되며 이로인해 불필요한 브라우저 리페인팅을 막을 수 있고,

또 하나는 원래 Q에 비해 비동기 적업에 공통적으로 필요한 가장 중요한 기능들만을 포함해서 사이즈를 줄였다.


그런데 이 $q는 최근 ES6에서 규정된 Native Promise 와도 아주 약간의 차이가 있는데, 이번 포스팅에서는 이 부분에 대해서 살짝 다뤄보려고 한다.


앞서 바보들을 위한 Promise 강의를 충분히 파악했다면 아래의 내용은 아주 쉽게 이해될 수 있을 것이다.




ES6 Promise 와 $q의 차이


ES6 Promise의 가장 기본 형태로 우리는 아래와 같은 소스를 분석한 바 있다.



var _promise = function ( param ) {
 
	return new Promise( function ( resolve, reject ) {
 
		/*비동기를 표현하기 위해 setTimeout 함수를 사용*/
		window.setTimeout( function () {
 
			/*파라메터가 참이면,*/
			if ( param ) {
 
				/*해결됨*/
				resolve("해결 완료");
			}
 
			/*파라메터가 거짓이면,*/
			else {
 
				/*실패*/
				reject( Error("실패!!") );
			}
		}, Math.random() * 2000 + 1000);
	});
};
 
 
_promise( true ).then( 
	 
	/*성공시*/
	function (text) { console.log(text); }, 
 
	/*실패시*/
	function (text) { console.log(text); }
);


이를 AngularJS에서 지원하고 있는 $q 로 변경하면 아래와 같이 나타낼 수 있다.


var _promise = function ( param ) {
 
	return $q( function ( resolve, reject ) {
 
		/*비동기를 표현하기 위해 setTimeout 함수를 사용*/
		window.setTimeout( function () {
 
			/*파라메터가 참이면,*/
			if ( param ) {
 
				/*해결됨*/
				resolve("해결 완료");
			}
 
			/*파라메터가 거짓이면,*/
			else {
 
				/*실패*/
				reject( Error("실패!!") );
			}
		}, Math.random() * 2000 + 1000);
	});
};
 
 
_promise( true ).then( 
	 
	/*성공시*/
	function (text) { console.log(text); }, 
 
	/*실패시*/
	function (text) { console.log(text); }
);


어떤 차이가 있는지 눈썰미가 있는 사람은 쉽게 파악했을 것이다. 여기에서 ES6의 Promise와 $q의 사용에 있어서 차이가 있다면, 단지 new Promise 로 선언했던 것을 $q로 변경한 정도이다.




resolved 된 Promise 객체 리턴


그런데 항상 이렇게 변경되는 것만은 아니다.


이러한 케이스 외에도 resolvedPromise().then(....) 과 같은 형태로 chaining을 해줄 목적에서 resolve 된 promise 객체를 리턴하는 경우에는 약간 사용 형태가 달라진다.


function resolvedPromise () {

	return Promise.resolve({
		userID	 : user.email,
		flightID : "UA_343223",
		date	 : "01/14/2014 8:00 AM"
	});	

}


이를 $q로 표현할 경우 아래와 같이 나타낼 수 있다. deferred 객체를 생성하고 resolve된 promise를 반환하는 것이다.


function resolvedPromise() {

	var deferred = $q.defer();

	deferred.resolve({
		userID	 : user.email,
		flightID : "UA_343223",
		date	 : "01/14/2014 8:00 AM"
	});

	return deferred.promise;
}


이해를 돕기 위해 defer() 메서드를 사용하지 않고 굳이 $q 자체만 사용할 경우 아래와 같이 변경할 수 있다.



function resolvedPromise() {

	return $q( function (resolve, reject) {
		resolve({
			userID	 : user.email,
			flightID : "UA_343223",
			date	 : "01/14/2014 8:00 AM"
		});
	});
}


위와 같이 $q 로 콜백을 선언하고 resolve 메서드를 실행하면 promise 객체가 반환되기 때문에 동일한 결과를 얻는다.




Promise.all 메서드


또 여러개의 비동기 응답이 모두 처리되었을 때 우리는 Promise.all 메서드를 사용했었는데, 이 역시도 동일하게 $q.all 메서드를 사용하면 동일하게 처리가 가능하다.



Promise.all([promise1, promise2]).then( function (values) {
	console.log("모두 완료됨", values);
});



위의 로직을 AngularJS의 $q 를 사용해서 표현하면, 아래와 같이 나타낼 수 있다.


$q.all([promise1, promise2]).then( function (values) {
	console.log("모두 완료됨", values);
});




결론


AngularJS에서 비동기 작업을 위해 도입한 $q는 ES6의 것과는 약간의 차이가 있지만, 위에서 언급한 차이점에 대해서 인지한다면 큰 문제없이 AngularJS에서도 Promise 를 활용할 수 있을 것이다.


Promise를 활용해서 코딩하는 것은 다양한 패턴과 방법이 있다. 많이 사용해보고 기존 코드를 반복적으로 개선하는 작업을 통해 보다 나은 비동기 코드를 작성하는 것이 중요하다.



AngularJS 관련 포스팅 더보기