3. View 개발 with angularJs
3.1. 소개
이 문서는 프론트엔드 개발 중 View를 개발하는 방법에 대한 설명과 예제를 담고 있습니다.
3.2. View 개발
View는 .NET MVC 기술과 함께 DOM의 제어를 위해 angularJs MVW 디자인패턴을 사용하고 있습니다.
따라서, .NET MVC 전통적인 View 개발 패턴을 사용하여 DOM을 구성하고, 구성된 DOM은 angularJs를 이용하여 컨트롤 합니다. 정적일 필요가 있는 페이지는 서버사이드 스크립트만을 이용하여 구성하되, 화면에서 동적인 반응이 필요한 경우 angularJs를 사용하여 페이지를 다시 랜더링하지 않고 빠른 응답을 할 수 있도록 처리하고 있습니다. 그리고 모든 View는 모바일 친화적으로 만들어 지며, SPA 기술을 사용할 수도 있습니다.
아래 모듈타입별 실제 파일 구성표를 참고하여 모듈타입에 따라 Controller와 View, Scripts 파일을 구성하고 개발하십시오.
📁Areas
├── 📁Page
| ├── 📁Controllers
| | └── SimpleUserInfoManagementController.cs
| └── 📁Model
| └── 📁SimpleUserInfoManagement
| └── 📁i18n
| | └── Index-locale.js
| └── Index.cshtml /* Page의 Action 라우팅은 기본 Index로 연결된다. */
├── 📁PageSub
| ├── 📁Controllers
| | └── MyPageSubController.cs
| └── 📁Model
| └── 📁MyPageSub
| └── 📁i18n
| | └── MyPageSub-locale.js
| └── MyPageSub.cshtml /* PageSub의 Action 라우팅은 기본 모듈명으로 연결된다. */
└── 📁Plugins
├── 📁Controllers
| └── MyPluginsController.cs
└── 📁Model
└── 📁MyPlugins
└── 📁i18n
| └── MyPlugins-locale.js
└── MyPlugins.cshtml /* Plugins의 Action 라우팅은 기본 모듈명으로 연결된다. */
3.2.1. Controller 작성
Controller는 모듈 타입에 맞는 지원객제를 상속받아 생성하게 됩니다. 아래 각 Areas 지원객체별 Route규칙과 특징 확인하십시오.
Controller 지원객체 | Route규칙 | 특징 |
---|---|---|
AbstractPageController | {controller}/{action}/{?id} | 유일하게 라우팅되는 페이지를 구성할 수 있는 지원객체 입니다. 이 컨트롤러 만이 ViewResult를 반환할 수 있습니다. {action} 라우팅 기본값은 Index 입니다. {?id} 라우팅은 옵션입니다. |
AbstractPageSubController | PageSub/{controller}/{action}/{?id} | PageSub모듈의 부분 페이지를 구성할 수 있는 컨트롤러 입니다. 이 컨트롤러는 ViewResult를 반환할 수 없습니다. View의 반환은 오직 PartialViewResult 만을 반환합니다. {action} 의 라우팅 기본값은 라우팅되는 controller명과 같습니다. {?id} 라우팅은 옵션입니다. |
AbstractPluginsSubController | Plugins/{controller}/{action}/{?id} | Plugins모듈의 부분 페이지를 구성할 수 있는 컨트롤러 입니다. 이 컨트롤러는 ViewResult를 반환할 수 없습니다. View의 반환은 오직 PartialViewResult 만을 반환합니다. {action} 의 라우팅 기본값은 라우팅되는 controller명과 같습니다. {?id} 라우팅은 옵션입니다. |
다음 Controller 작성 예제에서 모듈별 ReturnType에 주의 해 주십시오. (ActionResult or ViewResult, PartialViewResult)
public class SimpleUserInfoManagementController : AbstractPageController
{
// ActionResult or ViewResult
public ActionResult Index()
{
return View();
}
}
public class MyPageSubController : AbstractPageSubController
{
public PartialViewResult MyPageSub()
{
return PartialView();
}
}
public class MyPluginsController : AbstractPluginsController
{
public PartialViewResult MyPlugins()
{
return PartialView();
}
}
3.2.2. View 작성
.NET MVC(Razor) 기술을 사용하여 페이지를 랜더링할 수 있습니다. 다만, .NET MVC기술에서 Model을 사용할 때 다음의 규칙을 준수해야 합니다.
- Controller는 세션을 사용하지 않아야 합니다. 세션사용을 허용하지 않습니다.
- Model은 ViewBag만을 이용하여 View에 전달합니다.
- ViewBag 그대로나 동적인 속성에는 여러 타입을 사용할 수 있지만 다음의 제약을 준수해야 합니다.
- 객체의 경우 VO같은 리터럴한 객체를 사용해야 합니다.
- 백엔드에서 사용하는 객체는 엔티티객체나 모델객체만을 사용할 수 있습니다.
- Model은 정적일 필요가 있는 페이지를 개발할 때 주로 사용합니다. (ex, DOM의 변화가 없는 컨텐츠 표시)
3.2.2.1. 모듈에 다른 모듈을 포함시키기
적재 패턴을 가지는 Page, PageSub, Plugins 모듈들은 다른 모듈을 포함 시킬 수 있습니다.
V2모듈의 적재구조
V2모듈의 적재구조에 따른 다른 모듈을 포함시키는 방법
메인 모듈 \ 포함시킬 모듈 | Page모듈 | PageSub모듈 | Plugins모듈 |
---|---|---|---|
Page모듈 | × | ○Html.PageSub(string pageSubModuleName) |
○Html.Plugin(string pluginModuleName) |
PageSub모듈 | × | × | ○Html.Plugin(string pluginModuleName) |
Plugins모듈 | × | × | △Html.Plugin(string pluginModuleName - 불가Html.ServicePlugin(string servicePluginModuleName) - 가능 |
Plugins 모듈은 랜더링될 뷰가 존재하는 타입(기본)과 뷰가 없이 스크립트 라이브러리만 제공되는 타입으로 존재합니다. 기본적으로 Plugins 모듈은 다른 Plugins모듈을 포함할 수 없으나 스크립트 라이브러리만 제공되는 서비스 형태의 플러그인은 예외적으로 포함시킬 수 있습니다.
Html 확장 함수
다른 모듈을 포함시키기 위해 Html 확장 함수를 지원합니다. 이 함수를 사용하여 포함 시킬 모듈의 경로, 라우팅 조건 등의 설정에서 자유로워 지십시오.
Html.PageSub(string pageSubModuleName)
Page 모듈에 PageSub 모듈을 포함시킬 때 사용합니다. 대상 모듈은 RenderAction 형태로 포함되게 됩니다.
@Html.PageSub("MyPageSub")
Html.Plugin(string pluginName)
Page 모듈과 PageSub 모듈에 Plugins 모듈을 포함시킬 때 사용합니다. 대상 모듈은 RenderAction 형태로 포함되게 됩니다. Plugin 모듈에서는 다른 Plugin 모듈을 포함할 수 없습니다. (ServicePlugins은 허용)
@Html.Plugin("MyPlugin")
Html.ServicePlugin(string servicePluginName)
Page, PageSub, Plugins 모듈에 ServicePlugins 모듈을 포함시킬 때 사용합니다. 대상 모듈은 RenderPartial 형태로 포함되게 됩니다. 주로 DOM을 제어하는 스크립트, 언어, CSS 등의 서비스 패키지를 가져올 때 사용합니다.
@Html.ServicePlugin("MyPluginService") // 서비스 플러그인은 항상 Service를 접미어를 포함하여 개발됩니다.
3.2.3. angularJs 작성
각각 랜더링되는 모듈의 DOM의 제어를 위해 angularJs 기술을 사용합니다. V2아키텍처의 View모듈들은 항상 자신의 이름과 같은 Javascript를 자동으로 로드합니다. 때문에, angularJs로 작성되는 모듈과 여기서 만들어지는 controller, service, directive 등은 해당 모듈에 종속적이게 됩니다.
angularJs의 작성은 약속된 구현패턴이 있습니다. 일관된 구현패턴은 코드 리딩과 유지보수에 도움을 줍니다. 그리고 자주 작성되는 코드의 양을 줄일 수 있습니다. 패턴을 사용할 때, bootstrap될 메인 app에 작성된 서브 모듈들이 동작할 수 있도록 자동으로 주입되고, 외부에서 작성된 공통기능들이 작성되는 모듈에서 사용할 수 있도록 처리되는 기술적 지원이 있습니다.
$tq
는 그렇게 하기위한 스크립트 개발의 시작입니다.
3.2.3.1. $tq로 angularJs 모듈 작성 시작하기
;$tq(function(module){
'use strict';
module.controller('Page.SimpleUserInfoManagement', ['$scope', function($scope){
var ctrl = this; // this 처리
// event
$scope.evt = {
};
// fn
// init
$scope.$onInit = function(){
// model 초기화
// 추가 초기화 작업
}
}]);
})(angular.module('Page.SimpleUserInfoManagement', [])); // 모듈정의
$tq 작성 규칙
$tq로 부터 angulaJs script를 작성할 때, module과 controller, service에 대해 명명규칙이 존재 합니다. 이 것은 여러 모듈들이 중복되지 않게 하기 위해서 중요합니다. 위의 예제 처럼 긴 명명규칙은 스크립트 용량에 악영향을 줄 수 있지만, 현재는 퍼포먼스 보다 관리가능한 규칙에대해 더 가치를 두고 있습니다.
angularJs 모듈의 명명규칙
angularJs 모듈 | 명명 규칙 |
---|---|
module | {V2아키텍처모듈타입}.{V2아키텍처모듈명} ex) angular.module('Page.SimpleUserInfoManagement', [])' 속성설명: V2아키텍처모듈타입, 모듈명: 모듈이 위치하는 Areas명 입니다. |
controller | {V2아키텍처모듈타입}.{V2아키텍처모듈명}{추가명명}Ctrl ex) angular.controller('Page.SimpleUserInfoManagementCtrl', [...])' 속성설명: *_추가명명*: 모듈에 컨트롤러가 2개 이상 필요한 경우 접두어 ‘‘와 함게 간략한 컨트롤러명을 명명하십시오. controller 들은 접미어로 Ctrl을 갖습니다. |
service | {V2아키텍처모듈타입}.{V2아키텍처모듈명}.svc ex) angular.service('Page.SimpleUserInfoManagement.svc', [...])' 모듈에서 $http를 사용하는 모든 호출 로직은 이 하나의 서비스에 구현하십시오. |
directive, component, filtter etc.. | {유니크한명명} ex) angular.directive('myDirective', {...})' 속성설명: 유니크한명명: directive와 component는 보통 그 존재만으로 다른 모듈에 중복되지 않고 유일성을 갖게 됩니다. 때문에 중복에 대한 고민보다 짧은 명칭을 선호합니다. |
i18n value | {V2아키텍처모듈타입}.{V2아키텍처모듈명}.Locale ex) angular.module('Page.SimpleUserInfoManagement.Locale', [])' i18n 언어팩은 접미어로 .Locale을 포함합니다. |
controller 구현 규칙
controller, directive, component 에서 사용되는 controller 함수의 구현 규칙을 설명합니다. controller 함수는 this 처리, init 구현, event 와 function 구현으로 나누어지며 규칙이 존재 합니다.
1. 함수를 처음 선언하면 바로 this 처리를 해야 합니다. angularJs의 model은 $scope가 아닌 함수 자체에 처리합니다. 이런 방식은 angularJs 디자인 패턴에서 많이 사용하는 방식이기도 하며, 다른점은 티쿤에서는 변수명을 vm이 아닌 ctrl로 한다는 것 입니다.
function SimpleUserInfoManagementCtrl($scope){
var ctrl = this;
}
2. init 초기화 로직 구현 처음 초기화 될 때, model(ctrl) 과 $scope의 속성을 정의합니다. 또, 필요한 첫 작업들을 $onInit함수에서 구현합니다.
function SimpleUserInfoManagementCtrl($scope){
var ctrl = this;
// fn
function initNowDate(){
ctrl.now = new Date();
};
// init
$scope.$onInit = function(){
// model 초기화
$scope.scopeName = 'myScope';
ctrl.searchParam = { target: 'ALL', searchValue: '' };
ctrl.now;
// 추가 초기화 작업
initNowDate();
}
}
3. function 구현 $scope에 노출될 함수나 내부에서 사용될 함수를 한 곳에 정리하십시오.
function SimpleUserInfoManagementCtrl($scope){
var ctrl = this;
// fn - 함수는 모두 이곳에 정리됩니다.
$scope.getFunctionName(){
return "SimpleUserInfoManagementCtrl";
};
function initNowDate(){
ctrl.now = new Date();
};
}
3. event function 구현 이벤트에 연결될 이벤트 연결 함수는 $scope.evt에 구현됩니다.
function SimpleUserInfoManagementCtrl($scope){
var ctrl = this;
// evt - 이벤트에 연결될 이벤트 연결 함수는 이곳에 정의 하십시오.
$scope.evt = {
remove: function(item){
/* ... */
},
/* ... */
};
}
3.2.4. i18n 언어처리
View의 다국어 언어처리는 View가 랜더링 된 후, angularJs를 통해 처리하고 있습니다. 때문에 보통의 javascript i18n처리의 그것처럼 V2아키텍처도 동일한 언어처리를 하고 있습니다.
V2아키텍처 모듈은 다국어 처리를 위해 다국어 언어 팩을 모듈의 하위 i18n 폴더에서 관리합니다.
언어팩을 만들고 $scope.L
에 주입하십시오. 언어팩은 서비스 국가에 맞추어 자동으로 로드 됩니다.
// SimpleUserInfoManagement-Locale_KR.js
$tq(function(module){
module.value('Page.SimpleUserInfoManagement.Locale', {
Title: '간단한 고객정보 관리',
TableHeaders: {
Id: '번호',
FullName: '이름',
Age: '나이',
BirthDay: '생년월일'
},
Buttons: {
Reg: '등록',
Del: '삭제',
Mod: '수정'
}
});
})(angular.module('Page.SimpleUserInfoManagement.Locale', []));
// 컨트롤러 구현부
var $inject = ['$scope', 'Page.SimpleUserInfoManagement.Locale'];
function SimpleUserInfoManagementCtrl($scope, Locale){ // Locale == 'Page.SimpleUserInfoManagement.Locale'
var ctrl = this;
$scope.$onInit = function(){
$scope.L = Locale;
}
}
<!-- SimpleUserInfoManagement.cshtml -->
<h2>{{::L.Title}}</h2>
...
<button>{{::L.Buttons.Reg}}</button>
3.2.5. 구현예제 보기
아래는 plnker를 통해 상당히 실제 구현과 비슷하게 만든 예제 입니다.