정보공간_1

[6기 대구 허정욱] AngularJS #1. Scope 본문

IT 놀이터/Elite Member Tech & Talk

[6기 대구 허정욱] AngularJS #1. Scope

알 수 없는 사용자 2014. 11. 19. 06:32


1. AngularJS란?


자바스크립트 코드 몇 줄로 웹 페이지에 단순한 기능을 구현하던 시절은 지난날의 추억과 같습니다. 최근 웹 페이지의 자바스크립트의 줄의 수는, 유지보수를 할 수 있는 수준을 넘어버렸습니다. 제이쿼리가 생기고, Ajax가 생기면서 크로스 브라우징에 관한 코드를 더는 작성할 필요가 없어졌습니다. 하지만 그렇더라도, 유지보수를 위해 자바의 Spring처럼, 결합도거 낮고, 응집도가 높은 코드를 작성할 필요가 많아졌습니다.



TodoMVC(http://todomvc.com)사이트

수많은 자바스크립트 프레임워크들이 존재하며, 그중에서 근래에 AngularJS의 인기는 계속 상승중입니다.



이전에, banbone.js도 MVC패턴을 예를 들며 설명하였지만, 이렇게 많은 프레임워크중에서 AngularJS를 쓰는 이유를 알 필요가 있습니다. 왜 이렇게 AngularJS의 관심이 날이 갈수록 높아질까요?


선택해야되는 이유를 다섯가지로 추릴 수 있습니다.


(1). 작성해야 하는 자바스크립트 코드량을 줄일 수 있습니다.

컨트롤러와 모델 등 단순 자바스크립트 객체 및 함수이며, 모델과 뷰 사이는 양방향 데이터 바인딩이 제공되므로 동기화 코드를 작성할 필요가 없습니다. 또, MVC 모델을 제공하므로, DOM을 선택하고 조작하는 자바스크립트를 작성하지 않아도 됩니다. 지금까지 반복했던 수많은 코드를 작성할 필요가 없으므로 시간절약과 비즈니스 코드에 더욱 집중할 수 있습니다.


(2). AngularJS의 데이터 모델은 단순 (자바스크립트)객체입니다. 

여러 자바스크립트 MVC패턴에서는, 모델을 제공하기위해 프레임워크의 모델 클래스를 상속합니다. (BanboneJS도 그랬었죠.) 하지만, AngularJS에는 별도의 모델 클래스가 없습니다. 그저 단순 자바스크립트 객체입니다. 이것을 통해 알 수 있는 다른 프레임워크와의 차이점이자 장점을 볼 수 있습니다. 그것은 양방향 데이터바인딩입니다. 단순한 자바스크립트 객체의 값이 변경되는, 객체의 모델의 데이터와 뷰 데이터가 양방향 데이터 바인딩이 되는 것입니다. 


(3). 재사용할 수 있는 UI 컴포넌트를 만들 수 있습니다.

웹 애플리케이션을 제작하면서, 폼 태그를 많이 사용합니다. 우린 <form>태크를 재사용하여 UI를 만들어내죠.

하지만 HTML5가 등장하고, 새로운 태그가 생기고 복잡성이 증가했고, 과거에 썼던 태그들 말고 새로운 태그의 필요성이 간절했습니다. 그래서 AngularJS는 <MAP>, <GRAPH>, <TABLE sortable='true'>와 같이, HTML 태그를 만들 수 있게 해주며, 이러한 태그들이, 자바스크립트 <script src="javascript.js"></script>와 같은 호출이 아닌, HTML 태그 이름이나 태그 속성을 이용해 사용할 수 있게 해줍니다. 이러한 UI 컴포넌트는 UI 개발자가, 비즈니스 코드는 로직 개발자가 개발하여, 개발 역할에 따라 여러 개발자가 동시에 개발할 수 있게 해주고 서로의 역할을 명확하게 분리해줍니다.


(4). 의존관계 주입을 이용해, 웹 애플리케이션 자바스크립트 개발을 할 수 있습니다.

최근 부트스트랩 등, 더 좋은 UX를 위해 많은 자바스크립트 코드를 작성해야 했고, 자바스크립트 코드는 엉킨 실타래 마냥 더 복잡해지기 십상이였습니다. 재사용이 불가능해진 코드가 여기저기서 볼 수 있었습니다.

AngularJS는 여기서, 의존관계 주입을 통해,웹 애플리케이션을 개발할 수 있게 해줍니다. 서비스 프로바이더를 이용해 특정 서비스 컴포넌트를 구현하고 해당 서비스 컴포넌트가 사용하는 다른 서비스 컴포넌트를 직접 참조하는 것이 아니라, 의존관계 주입을 이용해 사용하게 됩니다. 단위 테스트가 가느하며, 관심사가 분리된 재사용할 수 있는 컴포넌트를 개발할 수 있으므로, 엉킨 실타래 같은 자바스크립트 코드를 작성하지 않을 수 있습니다.


(5). HTML & CSS 개발자와 자바스크립트 개발자가 서로 사이 좋게 지낼 수 있게 해줍니다.

jQuery를 이용해 웹 애플리케이션을 개발하다 보면, 자바스크립트에서 특정 DOM을 선택하기 위해 태그에 수많은 Class와 ID 속성을 추가하고, 내용이 빈 태그로 가득 찬 HTML을 작성하곤 합니다.  그렇다보니, HTML 구조만 봐서는 어떤 이벤트가 적용됐는 지 알 수가 없습니다. HTML 코드를 수정하면 자바스크립트가 안 돌아가는게 태반이였습니다. 하지만, AngularJS는 HTML을 이용해 뷰 코드를 작성하고, 자바스크립트에서 비즈니스 로직 코드만을 작성하게 하여, 뷰 코드와 로직 코드를 명확하게 분리했습니다. 즉, 자바사크립트가 HTML의 구조를 알아야 할 필요가 없는 것입니다.


AngularJS는 미스코 헤브리가 만들었으며, 구글에서 관리하고 있습니다.

제이쿼리와 같은 여타의 자바스크립트 라이브러리와 다른 점은, 프레임워크이므로 일련의 정형화된 구조를 가진 구현체를 제공하고 세부 구현만 우리가 구현하면 됩니다. 


AngularJS에서 제공하는 주요 기능은 다음과 같습니다.


(1) 양방향 데이터 바인딩

(2) MVC 구조

(3) 지시자를 이용한 HTML 확장

(4) 의존관계 주입

(5) 단일 페이지 웹 애플리케이션을 위한 라우터

(6) $q를 이용한 자바스크립트 비동기 프로그래밍 지원

(7) 자바스크립트 테스팅 지원


2. Scope

AngularJS의 프로그래밍 중, 가장 자주 등장하고, 그래서 가장 자주 다루게 되는 요소는Scope입니다.


(1) $rootScope와 $scope

AngularJS에서 Scope는 컨트롤러나 디렉티브의 유효범위내에 저장공간이라고 해석할 수 있습니다. 그리고 이것은 양방향 데이터 바인딩의 핵심이라고 할 수 있습니다. 사실 $scope는 그저 단순한 자바스크립트 객체에 불과합니다. 하지만 이 자바사크립트 객체는 연결된 DOM 요소에서 표현식이 계산되는 실행환경이며 뷰와 컨트롤러에서 사용되는 데이터와 기능이 살아 숨쉬는 공간입니다.


scope의 계층구조 (http://www.nextree.co.kr/p8890/) 참조


&scope는 그림과 같이 계층적 구조를 가집니다. 이때, 중요한 것은 $scope는 자바스크립트의 Prototypical 상속 구조로 인해 단순히 형태만 계층적 구조를 이루는 것이 아닌, 실제로 뷰가 $scope내의 모델에 접근하는 방식에 있어서도 계층적 구조를 따르고 있다는 것입니다. 


모든 AngularJS 애플리케이션은 하나의 $rootScope를 가집니다. 이 $rootScope는 ng-app을 생성하며, ng-app이 선언된 DOM 요소가 최상위 노드가 되어 여러 자식 &scope를 가지게 됩니다. 즉, DOM과 같은 계층적 구조에서 최상위 계층에 $rootScope가 존재하는 것입니다. 이는 어쩌면 window와 같은 글로벌 변수 영역이라고 생각할 수 있습니다. 또한 ng-repeat, ng-controller와 같이 별도의 $scope를 생성하는 지시자는 각 지역변수 영역을 가지고 있다고 생각할 수 있습니다. 


function Ctrl($scope) {         ---> name : Nextree

  $scope.name = 'Nextree';

}

function Ctrl2($scope) {        ---> name : 존재안함.

  $scope.year = '2014';

}

<html ng-app>                           

<div ng-controller="Ctrl">              

    {{name}}</br>               ----> Nextree 출력

    <div ng-controller="Ctrl2">         

         {{name}}               ----> Nextree 출력

    </div>

</div>

</html>

자식 scope에서 부모 scope의 데이터 접근 (http://www.nextree.co.kr/p8890/)


자식 $scope에는 name프로퍼티가 존재하지 않습니다. 즉 자식 $scope에서 없는 모델 즉, 속성을 부모 $scope에서 찾습니다. 이러한 계층적 구조는 유용하기도하지만, 의도치 않은 값을 출력하게 되는 경우도 있기마련입니다. 이러한 경우에는 AngularJS에서 isolate scope를 지원함으로써 원치않은 부모 scope에 있는 값을 읽게되어 비정상적으로 작동하는 것을 막아줍니다.


isolate scope (http://www.nextree.co.kr/p8890/)


function fooCtrl( $scope )

{

    $scope.myModel = "abcd";

 

    var childScope = $scope.$new(); //일반 scope 생성

    console.log( childScope.myModel ); // abcd 출력

    console.log( childScope.$parent === $scope ); //true 출력

 

    var isolateScope = $scope.$new( true ); //isolate scope 생성

    console.log( isolateScope.myModel ); // undefined 출력

    console.log( isolateScope.$parent === $scope ); //true 출력

AngularJS의 scope

isolate scope은 모델의 검색만 상위로 진행되지 않을 뿐이지, 다른 scope와 똑같이 유지되며, 다른 이벤트도 당연히 받게 됩니다.

<!doctype html>

<html ng-app>

  <head>

    <meta charset="utf-8">

    <style>

      .ng-scope {border: 1px solid red; padding: 5px;}

      .msg-list-area { margin: 10px; height: 400px; border: 1px solid black;}

    </style>

    <script src="./angular/angular.js"></script>

    <script type="text/javascript">

      function mainCtrl($scope) {

        $scope.broadcast = function(noticeMsg) {

          $scope.$broadcast("chat:noticeMsg", noticeMsg);

          $scope.noticeMsg="";

        };

      }

      

      function chatMsgListCtrl($scope, $rootScope) {

        $scope.msgList = [];

        $rootScope.$on("chat:newMsg", function(e, newMsg) {

          $scope.msgList.push(newMsg);

        });

        

        $scope.$on("chat:noticeMsg", function(e, noticeMsg) {

          $scope.msgList.push("[공지] " + noticeMsg);

        });

      }

      

      function chatMsgInputCtrl($scope) {

        $scope.submit = function(newMsg) {

          $scope.$emit("chat:newMsg", newMsg);

          $scope.newMsg = "";

        };

      }

      

    </script>

  </head>

  <body ng-controller="mainCtrl">

    <input type="text" ng-model="noticeMsg">

    <input type="button" value="send" ng-click="broadcast(noticeMsg)">

    <div class="msg-list-area" ng-controller="chatMsgListCtrl">

      <ul>

        <li ng-repeat="msg in msgList track by $index">{{msg}}</li>

      </ul>

    </div>

    <div ng-controller="chatMsgInputCtrl">

      <input type="text" ng-model="newMsg">

      <input type="button" value="전송", ng-click="submit(newMsg)">

    </div>

  </body>

</html>

시작하세요! AngularJS 프로그래밍 예제 2.21


다음과 같이 세 개의 컨트롤러 함수가 정의가 되어 있는 것을 볼 수 있습니다. 각 컨트롤러는 각자의 $scope를 생성하게 됩니다. mainCtrl은 아래의 두 개의 컨트롤러의 부모 컨트롤러입니다. 그래서 이전의 $scope처럼, 부모는 자식의 컨트롤러에 접근이 가능합니다.


이벤트를 발생시키는 API는 &scope 객체의 &broadcast와 &emit가 있으며, &on 메서드를 통해 특정 이벤트 이름에 해당하는 이벤트 리스너 함수를 등록하여 반응할 수 있습니다.

위의 예제에서는 char:noticeMsg 이벤트를 들고 있다가, 공지사항 내용을 [공지] 문자열과 함께 메시지 목록 화면에 반영하는 예제입니다. 

하단의 charMsgInputCtrl 컨트롤러 함수의는 chat:newMsg라는 이벤트 객체를 생성합니다. 그러면 이것은 [공지]라는 문자열이 포함하지 않은 메시지를 목록에 반영할 수가 있습니다.

이와 같이 이벤트 기반으로 작성하게 되면 Scope가 기존 영역을 수정하지 않고 새로운 화면 영역에 추가하거나 수정할 수 있습니다.


(2) 데이터 바인딩

<input name="firstName" value="John">

<p>Hello {{firstName}}!</p>


AngularJS는 양방향 데이터바인딩이라는 장점을 가지고 있습니다. 이러한 양방향 데이터바인딩을 사용하기 위해서 AngularJS는 매우 간단한 표현식인 {{expression}}을 사용함으로써 해결할 수 있습니다.

실제 javascript나 jQuery를 사용함에 있어서도 양방향 데이터바인딩을 구현할 수 있지만, 이 경우 첫 번째로 DOM을 조작하기 위해 특정 DOM에 id나 class 속성을 부여해야 하며, 두 번째로 javascript 코드에서 해당 DOM을 제어하는 코드를 만들어주어야합니다. 이러한 개발비용을 절약하고 편의성을 제공해주는 것이 AngularJS의 양방향 데이터바인딩입니다.


다음은 데이터바인딩의 내부 분석과 AngularJS 템플릿 시스템에 대해 알아보겠습니다.