2017-05-15
필자가 AngularJS 1.x로 프로젝트를 시작한지 한달이 조금 넘었다.
front에서 back에서나 되던 기능들이 어느정도 처리 되는걸 보면서 기뻐하던 것도 잠시..
"간단히 뷰만 보여주는 걸로 만들꺼면 괜찮은데, 조금 복잡한 기능을 만들려면 비추야..."라는 친구의 말이 절실하게 느껴지기 시작하고 있다.

그래서, 최근 겪었던 난관들 중에 필자가 가장 짜증났던 오류를 하나 소개 하려고 한다.
일단 아래 소스를 보자.
var modalInstance = $modal.open({
	templateUrl: 'classAdd.html' 
	,size: 'lg'
	,scope: $scope
	,controller:'classCtrl'
}).opened.then(function() {
	$('#n_class_name').val( angular.element('#class_name').html('클래스 이름을 적으세요') );
});
간단해 보이는, modal창을 띄우는 예제이다.
문제는 modal창에 보여줄 내용을 templateUrl의 주소를 참조하여 렌더링 하여 보여준다는 점이다.

예를 들어 필자가 classAdd.html의 #class_name오브젝트에 특정 값을 넣는다고 할때,
angular.element('#class_name').html() 라는 구문으로 #class_name를 인식하려면,
<input type="text" id="n_class_name">
위와같은 html오브젝트가 렌더링이 된 후에나 가능하다.

참조할 오브젝트는 아직 그려지지도 않았는데, 해당 오브젝트에 값을 집어 넣으려면 undefined만 뜨게 되는 것이다.

그런데 AngularJS는 저걸 알아서 그려주지도 않고,
비동기라서 promise를 사용한다고 해도 그려지는 시점이 다르기도 하고.. 아주 제어가 힘들때가 많다.

템플릿을 적용시킨후에 해당 구문을 사용하기 위해, 아래와 같이 $scope.$apply()를 사용하려고 하면,
.......
}).opened.then(function() {
	$scope.$apply();
	$('#n_class_name').val( angular.element('#class_name').html('클래스 이름을 적으세요') );
});
"$digest already in progress"라는 식으로 이미 $digest가 돌아가고 있다는 오류가 나오기 쉽상이고..
$digest가 겹치는 부분을 체크 하기 위해서는 아래와 같은 방식을 사용하면 되는데,
if ($scope.$$phase == '$apply' || $scope.$$phase == '$digest' ) {
	//do something...
} else {
	$scope.$apply(function() {
		//do something...
	});
}
상황에 따라선, 위와 같은 방식으로 잡히지 않는 구조도 많았다.

그래서 잠시뒤에 그려질 object를 가져오기 위해서는 아래와 같이 $timeout을 사용 하는 방식도 꽤 도움이 되기도 한다.
.......
}).opened.then(function() {
	$timeout(function() {
		$('#n_class_name').val( angular.element('#class_name').html('클래스 이름을 적으세요') );
	});
});