Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

Angular.js에서 HTML 구조없이 컨트롤러 상속받기

Angular.js의 스코프 상속

Angular.js에서는 스코프가 상당히 중요한데 예를 들어 컨트롤러를 생성할 때마다 스코프가 생기고 이 스코프는 중첩 관계를 가져서 부모의 스코프와 자동으로 상속구조를 만들 수 있다.(컨트롤러에서만 스코프를 새로 생성하는 건 아니다.) 이 때 스코프는 기본적으로 HTML 중첩구조를 그대로 따라서 상속구조를 가지게 된다.

<body ng-app>
  <div ng-controller="MainCtrl">
    {{greet}}!! {{whoami}}
    <div ng-controller="NavCtrl">
      {{greet}}!! {{whoami}}
    </div>
    <div ng-controller="SideCtrl">
      {{greet}}!! {{whoami}}
    </div>
  </div>
</body>

위와 같은 구조의 HTML 주고를 생각해 보자. 이때 JS 파일은 다음과 같다.

function MainCtrl($scope) {
  $scope.whoami= "Main Controller";
  $scope.greet = "Hello";
}

function NavCtrl($scope) {
  $scope.whoami= "Nav Controller";
}

function SideCtrl($scope) {
  $scope.whoami= "Side Controller";
}

이를 실행하면 다음과 같이 나온다.

이때 스코프는 다음과 같은 구조를 갖게 된다.

예제 어플리케이션의 Scope의 중첩관계

여기서 스코프 003MainCtrl이고 004005가 각각 NavCtrlSideCtrl이다. 즉, NavCtrlSideCtrlMainCtrl의 스코프를 상속받는 구조가 되기 때문에 NavCtrlSideCtrl에 없는 greet라는 값을 그대로 쓸 수 있다. 즉, 이 상속구조를 잘 이용하면 공통되는 값이나 기능(함수)를 부모 스코프에 두고 밑에서는 상속받아서 그대로 사용할 수 있다.

HTML없이 상속하기

위에서 보았듯이 Angular.js는 HTML 구조에 기반해서 스코프 상속을 하게 된다. 하지만 때에 따라서는 HTML 구조가 필요없는 경우가 있다. ControllerA와 ControllerB가 있는데 공통적으로 가지는 구조가 많아서 이를 한 곳으로 모으고 싶을 때 상속으로 공통화하려는 목적외에는 필요없는 컨트롤러를 굳이 불필요하게 HTML에 넣을 필요는 없고 상황에 따라서는 둘의 위치가 달라서 HTML에 상위에 넣는 것이 애매한 경우도 있을 것이다. 스코프 처리에 대한 내부 로직을 자세히는 모르지만 이 경우에 굳이 불필요하게 스코프를 따로 생성할 필요도 없을 것이다.

이를 위해서 Angular.js는 angular.extend함수를 지원하고 있다.(문서는 무척 빈약하다.) 앞에서 본 예제에서 HTML은 변경하지 않고 greet만 다른 컨트롤러(상속받을)로 추출하려면 다음과 같이 할 수 있다.

function ParentCtrl($scope) {
  $scope.greet = "Hello";
}

function MainCtrl($scope) {
  angular.extend(this, new ParentCtrl($scope));
  $scope.whoami= "Main Controller";
}

function NavCtrl($scope) {
  angular.extend(this, new ParentCtrl($scope));
  $scope.whoami= "Nav Controller";
}

function SideCtrl($scope) {
  angular.extend(this, new ParentCtrl($scope));
  $scope.whoami= "Side Controller";
}

angular.extend()를 사용해서 ParentCtrl을 상속받아서 공통 기능을 가지는 컨트롤러를 HTML을 수정하지 않고 만들었다.

여기서 주의할 점은 ParentCtrl가 다른 컨트롤러와는 약간 다르다는 것이다. 다른 컨트롤러는 Angular 앱이 컨트롤러로 인식하므로 파라미터에 의존성을 주입해 주시만 ParentCtrl은 모양만 똑같을 뿐 그냥 함수일 뿐이다.(다른 컨트롤러도 그냥 함수이지만 Angualr 앱이 인식하지 못한다는 의미다.) 그래서 ParentCtrl의 파라미터인 $scope는 의존성 주입으로 받는 것이 아니라 다른 컨트롤러에서 new ParentCtrl($scope)할 때 전달받은 파라미터이다. 그래서 $location, $http, $timeout처럼 Angular 앱이 의존성 주입해주는 객체를 직접 받으면 안되고 $scope처럼 상속받는 객체에서 파라미터로 전달해 주어야 한다.

function ParentCtrl($scope, $location) {
  $scope.greet = "Hello";
}

function MainCtrl($scope, $location) {
  angular.extend(this, new ParentCtrl($scope, $location));
  $scope.whoami= "Main Controller";
}

즉 위와 같이 상속받는 곳에서 의존성 주입을 해서 상속받을때 전달해 주어야 한다.

2013/09/18 23:30 2013/09/18 23:30