toggle menu

[AngularJS] 간단한 MVC 디자인 패턴 구현

2012. 10. 29. 17:49 AngularJS
AngularJS의 가장 큰 특징은 앞서 이야기했듯이 프론트엔드에서 MVW 디자인 패턴을 구현했다는 점이다.

MVW 디자인 패턴을 구현한 간단한 예를 살펴보자.

<html ng-app>
<head>
  ...
  <script src="lib/angular/angular.js"></script>
  <script src="js/controllers.js"></script>
</head>
<body ng-controller="PhoneListCtrl">
 
  <ul>
	 <li ng-repeat="phone in phones">
		{{phone.name}}
		<p>{{phone.snippet}}</p>
	 </li>
  </ul>
</body>
</html>



위의 예제에서 가장 먼저 눈에 들어오는 것은 역시 ng-app 라는 지시어이다.
ng-app 라는 지시어을 통해 앵귤러JS가 어디에서부터 DOM을 컴파일 해야하는지 명시하고 있다.

다음 앵귤러JS 라이브러리를 로드하고, 이후 controller.js 파일을 읽어들인다.
controller.js 파일에는 하단의 ng-controller 속성의 값인 PhoneListCtrl 에 해당하는 함수가 들어있을 것이다!!
컨트롤러를 다른 파일로 제외한 것을 주목해서 볼 필요가 있다.

이제 뷰가 남았다.
뷰는 UL 아래 LI가 ng-repeat 속성을 통해 phone 객체 안의 이름과 스니펫에 대한 속성을 반복적으로 출력하는 부분을 통해 살펴볼 수 있다.


MVC 패턴이 자바스크립트에도 적용되었다!



대략적인 구조는 위와 같다.
아직 컨트롤러에 대한 부분은 빠져있지만, 먼저 왼편부터 HTML 로 만든 전체적인 틀이다.

모델(데이터 모델)은 여기에서는 단순하게 Phone 객체들의 배열로 표현된다. 이 배열들은 PhoneListCtrl 컨트롤러에 의해 제어된다.

앵귤러JS에서 는 데이터 모델의 값들이 HTML 템플릿을 통해 보여진 결과라고 볼 수 있다. 언제든지 모델이 변하면 앵귤러JS는 적절하게 연결된 요소를 반영하여 뷰를 업데이트한다.


function PhoneListCtrl($scope) {
  $scope.phones = [
	 {"name": "Nexus S",
	  "snippet": "Fast just got faster with Nexus S."},
	 {"name": "Motorola XOOM™ with Wi-Fi",
	  "snippet": "The Next, Next Generation tablet."},
	 {"name": "MOTOROLA XOOM™",
	  "snippet": "The Next, Next Generation tablet."}
  ];
}



컨트롤러는 여기에서 아직 굉장한 작업을 하진 않지만 결정적인 역할을 수행한다. 컨트롤러는 뷰와 데이터 모델 사이를 연결해주는 작업을 수행한다. 데이터 바인딩을 설정하게 되는 것이다.

PhoneListCtrl — 컨트롤러 함수의 이름으로 controllers.js 라는 임의의 이름으로 만든 자바스크립트 파일에 위치하고 있다.
body 태그 안에 있는 ng-controller 지시어는 이름이 동일한 이 함수와 서로 매칭시킨다.

폰 데이터는 그 후에 컨트롤러 함수에 익젝션된 scope(혹은 $scope) 라는 곳에 붙여지게 된다.
컨트롤러 scope는 어플리케이션이 시작될 때 만들어진 루트 스코프를 상속받은 형태이다.
이 컨트롤러 스코프는 <body ng-controller="PhoneListCtrl"> 와 같이 컨트롤러를 설정해주면,
이 컨트롤러 선언부 아래에서는 {{ }} 를 통해 데이터 모델과 바인딩하여 사용할 수 있다.

앵귤러JS에서 스코프라는 개념은 매우 결정적이다. (살짝 보면서도 그런 느낌이 많이 드는데 어떻게 된게 결정적인 설명은 없네)
스코프는 템플릿, 모델, 컨트롤러가 함께 연동되게 하는 접착제처럼 보이기도 한다. 

앵귤러는 모델들과 뷰는 분리하고 싱크는 유지한채 템플릿과 데이터모델, 컨트롤러를 포함한 정보와 마찬가지로 사용한다.
모델에서 일어나는 어떤 변화든지 뷰에 반영되고 뷰에서 일어나는 어떤 변화든지 모델에 반영된다.

다시 정리해보면, 스코프는 컨트롤러를 실제 함수로 구현할 때 파라메터로 반드시 넣어주어야한다. $scope...
그리고 이렇게 파라메터로 들어간 스코프는 자동으로 루트 스코프를 상속받는 형태가 되고,
이 $scope에 데이터 모델들을 할당함으로써 외부에서도 컨트롤러 안의 데이터 모델에 접근할 수 있게 된다.
컨트롤러 안의 데이터 모델에 접근할 때는 {{  }} 앵귤러 표현식을 사용해 접근하게 된다.

완성된 예제를 살펴보자.
컨트롤러 부분을 완벽하게 분리해야하는데, 소스 가독성을 위해 컨트롤러부분은 외부 스크립트가 아닌 내부 함수로 처리했다.

아래 플런커 링크를 통해 직접 소스를 수정해가며 테스트해볼 수도 있다.
플런커 링크의 오른쪽 눈 모양 아이콘을 선택하면 실시간으로 실행된다.

Plunker
http://plnkr.co/edit/iZ0mnNis4QTbYSMOTsLb



<html ng-app> <!-- html 태그에 ng-app 지시어을 부여하면, 하위 모든 요소는 모두 앵귤러 어플리케이션으로 다뤄진다. 앵귤러는 ng-app와 같은 대쉬가 포함된 형태의 작명법을 지원한다. --> <head> <meta charset="utf-8"> <title>My HTML File</title> <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js"> </script> <!-- 이 태그는 당연하게도 앵귤러js를 로드하고, 더불어 HTML페이지가 모두 로드된 후에 브라우저에 의해 실행되는 콜백을 등록한다. 콜백이 실행되면, 앵귤러는 ng-app 지시어를 찾게된다. 다이렉티브를 찾으면 ng-app 지시어가 정의된 요소가 존재하는 어플리케이션 돔의 최상위에 부트스트랩을 건다. --> <script type="text/javascript" charset="utf-8"> //BODY 태그 안에 ng-controller 에서 할당해준 컨트롤러의 이름과 동일한 //자바스크립트 함수가 자동으로 매칭된다. //ng-controller에서 호출할 때는 scope 파라메터를 따로 넣지 않았지만, //실제 컨트롤러 함수에서는 아래와 같이 $scope 를 파라메터로 받는다. //$scope는 앵귤러js에서 매우 중요한 요소이다. function PhoneListController($scope) { //이 컨트롤러의 $scope에 할당된 변수는 이 컨트롤러 영향에 있는 //어느 곳에서도 접근할 수 있다. //현재까지 컨트롤러에서는 어떤 결정적인 역할을 하지는 않는다. //단순히 모델의 값을 배열 형태로 선언해줄 뿐이다. //하지만 적절한 연산과 조작에 의해 모델 값을 유동적으로 변환하는 형태가 된다면, //여기에서 컨트롤러의 중요성이 더욱 커지게 될 것이다. $scope.phones = [{ "name": "Nexus S", "snippet": "Fast just got faster with Nexus S." }, { "name": "Motorola XOOM™ with Wi-Fi", "snippet": "The Next, Next Generation tablet." }, { "name": "MOTOROLA XOOM™", "snippet": "The Next, Next Generation tablet." }]; //이렇게 선언된 배열 형태의 데이터 모델은 후에 NG-REPEAT에 의해 사용된다. } </script> </head> <body> <ul ng-controller="PhoneListController"> <li ng-repeat="phone in phones"> 이름 : {{phone.name}}<br /> 설명 : {{phone.snippet}} </li> <!-- 이 부분이 매우 재미있는 부분이다. 사실 컨트롤러 단에 phones 라는 배열은 존재한다. 하지만 phone이라는 객체명은 따로 존재하지 않는데, phone in phones 즉 phones 안에 phone 이란 명령으로 안의 내부 객체를 phone 이라는 이름으로 접근하게 되는 모습을 볼 수 있다. 사실 많이 보던 느낌이 나는 문법인데, for(var i in array) 와 동일한 효과를 주는 방식이다. 따라서 ng-repeat은 아래와 같이도 활용할 수 있다. ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]" --> </ul> </body> </html>


AngularJS 관련 포스팅 더보기