-
[AngularJS] 5. MVC - 모델, 뷰, 컨트롤러 - AngularJS 강좌Web/AngularJS 2016. 4. 1. 12:51
MVC - Model, View, Controller
AngularJS는 자바스크립트 MVC 프레임워크 중 하나입니다.
아래는 기본적인 MVC 패턴에 대한 설명입니다.
- Model
도메인에 해당하는 정보를 나타내는 오브젝트. 대체로 애플리케이션의 데이터와 행위를 포함하고 있음.
- View
모델의 정보를 UI에서 보여주는 역할. 하나의 모델을 다양한 뷰에서 사용할 수도 있고, 여러 모델을 하나의 뷰에서 사용할 수 있음
- Controller
애플리케이션에서 사용자의 입력을 받아 모델에 변경된 상태를 반영. 이는 모델을 변하게 하여 결국 뷰를 갱신. 컨트롤러는 직접 뷰를 변경하는 것이 아니라 로직을 통해 모델을 변경하고 그 결과가 뷰로 뿌려지는 것.
MVC 패턴을 이용하면 애플리케이션 개발과 유지보수에 있어서 몇 가지 이점이 있습니다.
첫째, 사용자 인터페이스와 비지니스 데이터를 분리할 수 있습니다. 따라서 비지니스 데이터에 해당하는 모델을 다른 뷰에서도 사용하여 모델을 재사용할 수 있게 됩니다.
둘째, MVC 패턴으로 개발함으로써 팀 사이에 표준화된 개발 방식을 제공할 수 있습니다.
1. 모델(Model)
AngularJS에서는 사용자 정보, 도서 정보, 북마크 정보 등 하나의 엔티티(Entity)나 여러 개의 엔티티가 모델이 될 수 있습니다. 그리고 다른 프레임워크와의 차이점은 별다른 상속 없이 순수 자바스크립트 객체가 모델이 될 수 있다는 점 입니다.
하지만 중요한 점은 이러한 모델 객체는 AngularJS의 $scope 객체로부터 접근할 수 있어야 한다는 것입니다.
모델은 컨트롤러에서 $scope 객체에 선언하거나 템플릿에서 선언할 수 있습니다. 다음 코드는 컨트롤러에서 모델을 선언한 코드입니다.
1234function mainCtrl($scope) {$scope.userId: "palpit";$scope.bookmark: { name: "내 블로그", url: "palpit.tistory.com", image: "palpit.png" };}cs 위 코드처럼 컨트롤러에서 모델을 선언하고 있습니다. $scope의 속성명은 모델명을 나타내고 값은 모델이 됩니다. 여기서 모델은 단순한 문자열이나 객체, 배열이 될 수 있습니다.
즉, 모델은 평범한 자바스크립트 객체 입니다. 모델은 HTML 템플릿에서도 선언 할 수 있습니다.
2. 뷰(View)
AngularJS에서 뷰는 문서 객체 모델(DOM: Document Object Model)입니다.
브라우저에서 HTML 문서를 읽어서 DOM을 생성하는데 AngularJS에서는 이 DOM이 뷰가 되는 것입니다. 템플릿과 뷰를 혼동할 수 있는데 AngularJS에서 HTML 문서는 템플릿이고 이 템플릿을 AngularJS가 읽어서 뷰를 생성합니다. 뷰를 생성하는 과정은 다음과 같습니다.
a. HTML로 작성한 템플릿을 브라우저가 읽음
b. 브라우저는 DOM 생성
c. <script src="angular.js">가 실행되어 AngularJS 소스 실행
d. DOM 생성 시 DOM Content Loaded 이벤트가 발생하면서 정적 DOM을 읽고 뷰를 컴파일, 컴파일 시 확장 태그나 속성을 읽고 처리한 후 데이터 바인딩
e. 컴파일 완료되면 동적 DOM, 뷰를 생성
3. 컨트롤러(Controller)
AngularJS에서 컨트롤러는 많은 일을 하지 않습니다. 애플리케이션의 로직을 담당하는 역할만 합니다.
결국 $scope 객체에 데이터나 행위를 선언하는 것입니다. 그리고 컨트롤러는 인자로 $scope를 전달받는 단순한 자바스크립트 함수입니다.
컨트롤러 코드를 작성할 때 주의할 사항이 있습니다. 컨트롤러는 단 하나의 뷰에 해당하는 애플리케이션의 로직만을 담당해야 합니다. 화면상의 로직이 아니라 애플리케이션의 비즈니스 로직입니다.
즉, DOM을 조작하는 행위와 같은 화면 상의 로직은 다음에 설명한 지시자에서 구현하고 컨트롤러에서는 애플리케이션의 비즈니스 로직만을 구현해야 합니다.
또한, 컨트롤러는 하나의 컨트롤러에 하나의 $scope만을 갖게 됩니다. 컨트롤러 당 각 $scope를 갖게 된다는 것 입니다. 각각 독립된 $scope가 생성되고 서로 참조할 수 없게 됩니다.
4. $rootScope와 $scope
$scope는 양방향 데이터 바인딩의 핵심이자 뷰와 컨트롤러를 이어주는 징검다리이기도 합니다. 다음 목록은 AngularJS 애플리케이션에서의 $scope의 특징을 보여줍니다.
- 뷰와 컨트롤러를 이어주는 다리
- 연결된 DOM에서의 실행환경
- 양방향 데이터 바인딩 처리
- 이벤트 전파 처리
- 계층적 구조
4-1. $scope 계층 구조
모든 AngularJS 애플리케이션은 하나의 $rootScope를 가집니다. 이 $rootScope는 ng-app을 생성하며 ng-app이 선언된 DOM 요소가 최상위 노드가 되어 여러 자식 $scope를 가지게 됩니다. 즉, DOM과 같은 계층적 구조에서 최상위 계층에 $rootScope가 존재하는 것입니다.
다음은 ng-controller를 계층적으로 가지는 예제입니다.
* ajsarch.html
12345678910111213141516171819202122232425262728<!doctype html><html ng-app="arch"><head><meta charset="UTF-8"/><script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script><script type="text/javascript">var app = angular.module("arch", []);app.controller("parentCtrl", function($scope) {$scope.parent = {name: "parent Moon"};});app.controller("childCtrl", function($scope) {$scope.child = {name: "child Moon"};$scope.changeParentName = function() {$scope.parent.name = "Another Jack";};});</script></head><body ng-controller="parentCtrl"><h1>부모 이름: {{parent.name}}</h1><div ng-controller="childCtrl" style="padding-left: 20px;"><h2>부모 이름: {{parent.name}}</h2><h2>자식 이름: {{child.name}}</h2><button ng-click="changeParentName()">부모 이름 변경</button></div></body></html>cs 4-2. Scope 타입
지금까지 본 $scope 객체나 $rootScope 객체는 AngularJS 내부에서 정의하는 Scope 타입의 인스턴스입니다. 즉, 다음과 같이 별도의 생성자 함수가 AngularJS 내부에 정의되어 있습니다.
1234function Scope() { ... }Scope.prototype.$apply = function () {};Scope.prototype.$digest = function() {};...cs AJS는 초기 부트스트랩 시 프레임워크 내부에서 $rootScope를 new Scope()로 생성한 뒤 해당 $rootScope를 서비스로 제공합니다. 그리고 ng-controller나 웹 앱에서는 다음과 같이 $rootScope를 이용해서 자식 $scope 객체들을 만들 수 있습니다.
1var $scope = $rootScope.$new();cs 서비스는 나중에 다루기로 하고, scope 타입의 프로토타입 메서드를 살펴보도록 합시다.
$apply(표현식 또는 함수)
주로 외부 환경에서 AJS 표현식을 실행할 때 사용
$broadcast(이벤트 이름, 인자들...)
첫 번째 인자인 이벤트 이름으로 하는 이벤트를 모든 하위 $scope에게 발생시킴
$destroy()
현재 $scope를 제거, 모든 자식 $scope까지 제거
$digest()
$scope와 그 자식에 등록된 모든 $watch 리스너 함수를 실행. $watch 리스너 함수가 보는 표현식에 대해 변화가 없다면 리스너 함수는 실행시키지 않음
$emit(이벤트명, 인자들...)
해당 $scope를 기준으로 상위 계층 $scope에게 이벤트 명으로 인자를 전달. 물론 $on으로 이벤트 명을 듣고 있는 상위 계층에 한하여 전파
$new(독립여부)
새로운 $scope를 생성. 독립여부를 true, false 로 전달하는데 true일 경우 프로토타입을 기반으로 상속하지 않게 됨
$on(이벤트 이름, 리스너 함수)
주어진 이벤트 이름으로 이벤트를 감지하다가 해당 이벤트가 발생하면 리스너 함수 호출
$watch(표현식, 리스너 함수, 동등성여부)
대상 $scope에 특정 표현식을 감지하는 리스너 함수를 등록
위 함수를 사용 시점별로 묶으면 데이터 바인딩 처리 시 $apply, $digest, $watch를 사용하고 사용자 정의 이벤트 처리 시 $broadcast, $emit, $on 을 사용합니다.
$$new와 $destroy는 $scope의 생성과 파괴 처리 시 사용합니다. 컨트롤러에서 사용하는 $scope 객체는 scope 타입의 인스턴스이므로 프로토타입 상속에 의해 위 메서드를 사용할 수 있습니다.
4-2. $scope에서 사용자 정의 이벤트 처리
AJS는 웹 앱에 애플리케이션 이벤트를 정의하고 이런 이벤트 처리에 대한 일련의 메카니즘을 제공합니다. 이러한 사용자 정의 이벤트는 모두 $scope 객체를 통하여 처리되는데 $scope 객체에서 특정 이벤트를 발생시키면 이벤트를 발생한 $scope 객체의 자식이나 부모 $scope에서 해당 이벤트를 듣고 있다 처리할 수 있는 것 입니다.
다음 예제를 통해서 직접 살펴보도록 합시다. 이 예제는 메시지를 입력하여 전송을 하면 공지가 되는 식의 예제입니다.
* ajsevent.html
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152<!doctype html><html ng-app="event"><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="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script><script type="text/javascript">var app = angular.module("event", []);app.controller("mainCtrl", function($scope) {$scope.broadcast = function(noticeMsg) {$scope.$broadcast("chat:noticeMsg", noticeMsg);$scope.noticeMsg = "";};});app.controller("chatMsgListCtrl", ['$scope', '$rootScope', function($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);});}]);app.controller("chatMsgInputCtrl", function($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="공지 전송" 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>cs 위 예제에는 3개의 컨트롤러 함수가 정의되었습니다. mainCtrl, chatMsgListCtrl, chatMsgInputCtrl 컨트롤러 함수가 정의되어 있습니다.각 컨트롤러와 연결된 DOM에는 각 컨트롤러가 생성하는 $scope 객체가 연결됩니다.end
* 이 포스팅은 '시작하세요! AngularJS 프로그래밍'을 바탕으로 작성하였습니다.
'Web > AngularJS' 카테고리의 다른 글
[AngularJS] 7-2. 지시자(Directive) 응용 - AngularJS 강좌 (0) 2016.04.05 [AngularJS] 7-1. 지시자(Directive) 기본 - AngularJS 강좌 (0) 2016.04.04 [AngularJS] 6. 모듈(Module) - AngularJS 강좌 (0) 2016.04.01 [AngularJS] 4. 템플릿 시스템과 데이터 바인딩 - 2 - AngularJS 강좌 (0) 2016.02.06 [AngularJS] 4. 템플릿 시스템과 데이터 바인딩 - 1 - AngularJS 강좌 (0) 2016.01.29 [AngularJS] 3. AngularJS 부트스트랩(Bootstrap) - AngularJS 강좌 (0) 2016.01.15