본문 바로가기

Dev/Dev:: JavaScript

[AngularJS 1.x] AngularJS - 04. $scope



AngularJS의 $scope

AngularJS에서 가장 기초가 되고 중요한 $scope에 대해 알아보자.


$scope 객체

AngularJS의 $scope은 뷰(View)와 컨트롤러(Controller)를 연결하는 객체이다.

$watch API를 제공하며 이는 모델의 변경을 감시한다.

$apply API를 제공하며 controllers, services, Angular event handlers와 같은 외부로 부터 발생된 변경사항을 뷰(View)로 반영




$scope 컨트롤러의 관계

1. AngularJS의 컨트롤러는 하나의 컨트롤러에 하나의 $scope만을 가지게 된다.

2. 컨트롤러 함수 두 개가 있을 경우 컨트롤러 함수당 별도의 $scope 객체가 생성된다.

3. 그리고 AngularJS 어플리케이션 루트에 해당하는 $rootScope가 있다.

4. 하나의 화면에 여러 컨트롤러를 사용하면 컨트롤러 별로 독립된 $scope가 생성되는데 각 독립된 $scope는 서로 참조할 수 없다.

5. 그래서 컨트롤러 사이에 데이터를 공유해야 할 경우 서비스를 이용한다.




$scope 계층 구조

 - 모든 AngularJS 어플리케이션은 하나의 $rootscope를 가진다. 

 - $rootscope는 ng-app을 생성하며 ng-app이 선언된 DOM 요소가 최상위 노드가 되어 여러 자식 $scope를 가지게 된다.

   즉, DOM과 같은 계층적 구조에서 최상위 계층에 $rootScope가 존재하는 것이다. 

 - 지시자(Directive)는 새로운 $scope을 생성

    ng-controller나 ng-repeat과 같이 별도의 $scope를 생성하는 지시자는 각 지역변수 영역을 가지고 있다고 생각할 수 있다.

 - $scope는 DOM 구조와 가깝게 하이어라키 구조를 갖는다. 


아래 $scope의 계층구조를 갖는 예제 소스를 살펴보자.

  
<!doctype html>
<html ng-app="app">
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
    <script>
      angular.module('app', [])
        .controller('parentCtrl', function($scope){
          $scope.parent={name: "parent Kim"};
        })
        .controller('childCtrl', function($scope){
          $scope.child={name : "child Yoo"};
          $scope.changeParentName=function(){
            $scope.parent.name="another kim";
          };
        });

    </script>
  </head>
  <body>
    <div ng-controller="parentCtrl">
      Hello {{parent.name}}!
        <div ng-controller="childCtrl" style="padding-left:20px;">
          <h2>부모이름 : {{parent.name}}</h2>
          <h2>자식이름 : {{child.name}}</h2>
          <button ng-click="changeParentName()">부모이름 변경</button>
        </div>
    </div>

  </body>
</html>

- 위 코드에서는 ng-app="app" 태그를 사용하면 $rootScope가 하나 만들어진다.

또한 ng-controller="parentCtrl"를 통해 $scope가 만들어지고 ng-controller="childCtrl"를 통해 또 하나의 $scope가 만들어졌다.

총 세 개의 $scope가 만들어 진것이다.

- 위 코드를 실행하면 childCtrl의 $scope가 parentCtrl의 $scope의 모델에 접근을 하며 값까지 변경을 하는데 이는 부모 $scope로부터 프로토타입을 상속 받기 때문에 가능한 것이다.

즉, 자식 $scope에서 없는 모델 즉, 속성을 부모 $scope에서 찾는다. 


아래 그림을 보면, 3개의 scope가 만들어지는 구조를 확인할 수 있다.




$scope 타입의 프로토타입 메서드

 1. apply(표현식 혹은 함수) : 주로 외부 환경에서 AngularJS 표현식을 실행할 때 사용한다. 즉, 외부 라이브러리로 이벤트를 처리할 때나 setTimeout 메서드를 사용할 때 사용한다.

인자로는 표현식이나 함수를 전달할 수 있다. 표현식을 전달하면 해당 표현식을 계산하고 함수를 전달하면 함수를 실행시키다.

그리고 내부적으로 $rootScope의 $digest를 실행해 등록된 모든 $watch를 실행하게 된다.


 2. $broadcast(이벤트 이름, 인자들...) : 첫 번째 인자인 이벤트 이름으로 하는 이벤트를 모든 하위 $scope에게 발생시킨다.

가령 $scope.$broadcast('popup : open', {title : "hello"}); 를 호출하면 $on 메서드를 이용해 해당 이벤트 (popup:open)을 듣고 있는 $scope들에게 {title : "hello"}의 데이터를 전달할 수 있다. 잘 활용하면 $scope들 사이의 참조 관계를 매우 느슨하게 만들어 재활용할 수 있는 UI 컴포넌트 개발에 용이하다.

 

 3. $destroy() : 현재 $scope를 제거할 수 있다. 또한, 모든 자식 $scope까지 파괴한다.


 4. $digest() ; $scope와 그 자식에 등록된 모든 $watch 리스너 함수를 실행시킨다. $watch 리스너 함수가 보는 표현식에 대하여 변화가 없다면 리스너 함수는 실행시키지 않는다.


 5. $emit(이벤트명, 인자들...) : 해당 $scope를 기준으로 상위 계층 $scope에게 이벤트 명으로 인자를 전달한다. 물론 $on으로 이벤트 명을 듣고 있는 상위 계층에 한하여 전파한다.


 6. $eval(표현식, 로케일) : 주어진 표현식을 계산하고 그 결과를 반환한다. 물론 현재 $scope를 기준으로 표현식이 계산된다. 


 7. $evalAsync(표현식) : $eval과 마찬가지나, 표현식의 결과값이 바로 반환되지 않고 나중에 어떠한 시점에서 그 결과가 반환된다. 하지만 적어도 한번의 $digest가 호출된다.


 8. $new(독립여부) : 새로운 자식 $scope를 생성한다. 독립여부를 true, false로 전달하는 데 true일 경우 프로토타입을 기반으로 상속하지 않게 된다.


 9. $on(이벤트 이름, 리스터 함수) : 주어진 이벤트 이름으로 이벤트를 감지하다가 해당 이벤트가 발생하면 리스너 함수를 실행한다. 이벤트 리스너 함수는 첫 번째 인자로 이벤트 객체를 받고 다음으로 $emit이나 $broadcast에서 전달한 값을 인자로 받는다.


 10. $watch(표현식, 리스너 함수, 동등성 여부) : 대상 $scope에 특정 표현식을 감지하는 리스너 함수를 등록한다. 가령 $scope의 data 속성에 특정 객체가 할당되어 있다고 하자.

그리고 $scope.$watch("data", function(){...})로 함수를 호출하면 $scope.data의 레퍼런스가 변경될 때 리스너 함수가 호출된다. 

리스터 함수에는 인자로 새로운 값과 이전 값이 주어진다. 동등성 여부는 변경을 레퍼런스로 감지할 것인지 동등한 여부로 감지할 것인지를 정할 때 사용된다.

기본값은 false이며 레퍼런스 변경시에만 리스너 함수가 호출된다.


 11. $watchCollection(표현식, 리스너 함수) : 기본적으로 $watch와 같은 기능을 하며 대신 배열이나 객체에 대한 변경을 감지할 때 사용한다.

배열일 경우 새로운 배열 요소가 추가되거나 배열 요소들 사이의 순서가 변경되거나 배열요소가 삭제될 때마다 리스너 함수가 호출된다. 

객체일 경우 속성에 변경이 있을 때마다 리스너 함수가 호출된다.








* 참고

http://mobicon.tistory.com/270

http://kshmc.tistory.com/entry/13-rootScope%EC%99%80-scope