블로그 이미지
윤영식
Full Stacker, Application Architecter, KnowHow Dispenser and Bike Rider

Publication

Category

Recent Post

2013. 12. 4. 01:20 My Projects/BI Dashboard

상단의 대메뉴를 선택한 후 본문의 왼쪽에 있는 소메뉴를 클릭하면 오른쪽에 본문의 내용이 나온다. 본문을 보기까지의 라우팅(Routing)을 보면 대메뉴 클릭 -> 소메뉴 클릭 -> 본문 으로 이어지는 DOM의 변경이 일어난다. jqGrid, HigChart등의 json 환경 내역을 관리하는 화면을 만들기 위하여 Routing 방식을 고도화 해보자




1. Nest View

  - 큰메뉴 -> 작은 메뉴 -> 더 작은 메뉴 -> 본문 식의 View가 내제되어 라우팅 될 경우

  - UI-Router 를 이용한 데모, Plunker 데모

  - Route-Segment 를 이용한 데모



2. Admin 화면 구성하기

  - index.html 메뉴 추가하기

  - component에서 jqgrid, highchart와 같은 컴포넌트의 config 값 (json type)을 관리한다

// index.html 메뉴 추가하기 

<div class="collapse navbar-collapse">

  <ul class="nav navbar-nav">

    <li data-ng-class="activeWhen(path()=='/')">

      <a href="" data-ng-click="setRoute('/')">Home</a>

    </li>

    .. 중략 ..

    <li class="dropdown">

        <a href="" class="dropdown-toggle" data-toggle="dropdown">Admin <b class="caret"></b></a>

        <ul class="dropdown-menu">

          <li class="dropdown-header">Project</li>

          <li><a href="">User</a></li>

          <li><a href="">Customer</a></li>

          <li><a href="">Code</a></li>

          <li><a href="">Role</a></li>

          <li class="divider"></li>

          <li class="dropdown-header">MobiconSoft</li>

          <li data-ng-class="activeWhen(path()=='/mc-component')">

      <a href="" data-ng-click="setRoute('/mc-component')">Component</a>

    </li>

          <li><a href="">Dashboard</a></li>

        </ul>

    </li>

  </ul>

</div>  



// Component partial 화면과 모듈 생성 및 등록

// 1) MCAdminCtrl.js 모듈을 만들기 

// 2) views/mc/component.html 화면 만들기

// 3) DashboardApp.js 에 MCAdminCtrl.js 모듈 명칭 등록하기 : DashboardApp.MCAdminCtrl

// 4) index.html 에 MCAdminCtrl.js 모듈 파일 <script> 태그 추가하기

  - 서버 코드로 ComponentController / Component / ComponentService / ComponentMapper(java & xml) 을 만든다 

    + compType : grid, chart, option 등 다양하게 존재할 수 있다 

// ComponentMapper.xml 에서 : 기존 소스는 typeAlias 사용하였음

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- 

id : primary key

mysql -u user1 -p DBName

mysql> CREATE TABLE mc_component(

      id MEDIUMINT NOT NULL AUTO_INCREMENT,

      comp_type VARCHAR(5) NOT NULL,

      comp_cfg TEXT NOT NULL,

      PRIMARY KEY (id) 

    );

 -->

<mapper namespace="com.mobiconsoft.dashboard.mapper.ComponentMapper">

 

    <resultMap id="component" type="com.mobiconsoft.dashboard.domain.Component" >

        <result property="id" column="id"/>

        <result property="compType" column="comp_type"/>

        <result property="compCfg" column="comp_cfg"/>

    </resultMap>

 

    <select id="getComponents" resultType="com.mobiconsoft.dashboard.domain.Component">

        SELECT

        *

        FROM

        component

    </select>

 

    <select id="getComponent" parameterType="Integer" resultType="com.mobiconsoft.dashboard.domain.Component">

        SELECT

        *

        FROM

        component

        WHERE

        id=#{id}

    </select>

 

    <insert id="saveComponent" parameterType="com.mobiconsoft.dashboard.domain.Component"<!-- useGeneratedKeys="true" keyProperty="id"> -->

        INSERT INTO

        component(comp_type, comp_cfg)

        VALUES

        (#{compType}, #{compCfg})

    </insert>

    

    <update id="updateComponent" parameterType="com.mobiconsoft.dashboard.domain.Component"

        UPDATE 

        component

        SET

        comp_type=#{compType}, 

        comp_cfg=#{compCfg}

        WHERE

        id=#{id} 

    </update>

 

    <delete id="deleteComponent" parameterType="Integer">

        DELETE FROM

        component

        WHERE

        id=#{id}

    </delete>

 

</mapper>


// 서버 전체 파일 



3. UI-Router 사용하기

  - 관리 메뉴에서 Component를 선택하면 CRUD 할 수있는 메뉴 구조를 본문에서 가져야 한다

  - UI-Router를 이용하여 기존의 Routing 환경을 고도화한다 

// angular-route 라 반드시 설치되어 있어야 하고, 1.2.* 도 지원함 

// bower 설치 

bower install angular-ui-router --save

bower angular-ui-router#~0.2.0 install angular-ui-router#0.2.0

  - index.html에 route-segment 추가하기 

<!-- build:js scripts/angular-3.js -->

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

    <script src="bower_components/angular-route/angular-route.js"></script>

    <script src="bower_components/angular-ui-router/release/angular-ui-router.js"></script>

    .. 중략 ..

<!-- endbuild -->

  - DashboardApp.js 메인 소스에 ui-router 모듈을 추가한다 (예제 참조)

var DashboardApp = angular.module('DasbhoardApp', [

  'ngRoute', 

  'ui.router',

  .. 중략 ..

  'DasbhoardApp.RestfulSvc'

]);

  - DashboardApp.js 의 config 에서 $routeProvider를 고도화한다 (소스 참조)

DashboardApp.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {


   $urlRouterProvider.otherwise("/main");

   $stateProvider

      .state('main', {

      url: '/main',

        templateUrl: 'views/main.html'

      })

      .state('resttest', {

      url: '/resttest',

        templateUrl: 'views/restTest.html',

        controller: 'RestTestBiz.personCtrl'

      })

      .state('jqgridtest', {

      url: '/jqgridtest',

        templateUrl: 'views/jqGridTest.html',

        controller: 'JqGridBiz.salesCtrl'

      })

      .state('mc-component', {

      url: '/mc-component',

        templateUrl: 'views/mc/component.html',

        controller: 'MCAdminCtrl.componentCtrl'

      });

  }]);


DashboardApp.run(['$rootScope', '$state', '$stateParams',

    function ($rootScope,   $state,   $stateParams) {


      // It's very handy to add references to $state and $stateParams to the $rootScope

      // so that you can access them from any scope within your applications.For example,

      // <li ng-class="{ active: $state.includes('contacts.list') }"> will set the <li>

      // to active whenever 'contacts.list' or one of its decendents is active.

      $rootScope.$state = $state;

      $rootScope.$stateParams = $stateParams;

  }]);

  - index.html 의 링크 내역 수정 (소스 참조)

   + ui-sref 애프리뷰트 directive 사용으로 routing 되는 정보를 현재 html 페이지의 ui-view 로 연결한다 

   + ui-view 애트리뷰트 directive 사용으로 화면이 나타나는 태그 영역

   + 메뉴 active는 $state.includes('<stateName>') 을 이용하여 현재의 state 이면 active로 표현 

  -즉 한페이지에서 ui-sref와 ui-view를 통해 확장해 가는 방식

<div class="collapse navbar-collapse">

  <ul class="nav navbar-nav">

    <li data-ng-class="{ active: $state.includes('main') }"><a ui-sref="main">Home</a></li>

    <li data-ng-class="{ active: $state.includes('resttest') }"><a ui-sref="resttest">RESTTest</a></li>

    <li data-ng-class="{ active: $state.includes('jqgridtest') }"><a ui-sref="jqgridtest">jqGridTest</a></li>

    

    <!-- <li data-ng-class="activeWhen(path()=='/')">

      <a href="" data-ng-click="setRoute('/')">Home</a>

    </li>

    <li data-ng-class="activeWhen(path()=='/resttest')">

      <a href="" data-ng-click="setRoute('/resttest')">RESTTest</a>

    </li>

    <li data-ng-class="activeWhen(path()=='/jqgridtest')">

      <a href="" data-ng-click="setRoute('/jqgridtest')">jqGridTest</a>

    </li> -->

    <li class="dropdown">

        <a href="" class="dropdown-toggle" data-toggle="dropdown">Admin <b class="caret"></b></a>

        <ul class="dropdown-menu">

          <li class="dropdown-header">Project</li>

          <li><a href="">User</a></li>

          <li><a href="">Customer</a></li>

          <li><a href="">Code</a></li>

          <li><a href="">Role</a></li>

          <li class="divider"></li>

          <li class="dropdown-header">MobiconSoft</li>

          <!-- <li data-ng-class="activeWhen(path()=='/mc-component')">

      <a href="" data-ng-click="setRoute('/mc-component')">Component</a>

     </li> -->

     <li data-ng-class="{ active: $state.includes('mc-component') }"><a ui-sref="mc-component">Component</a></li>

          <li><a href="">Dashboard</a></li>

        </ul>

    </li>

  </ul>

</div>


<!-- Add your site or application content here -->

<!-- <div class="container" data-ng-view=""></div> -->

<div ui-view class="container" style="margin-top:30px"></div>



4. Component 관리를 위한 UI-Router 만들기

  - component 관리 화면 만들기

    + step-1 : component.html 으로 컴포넌트의 목록을 보여주고 컴포넌트를 선택하면 component.detail.html 이 우측에 표현된다

    + step-2 : component.read.html 로 컴포넌트 상세 정보를 읽거나 업데이트 또는 삭제한다  

    + step-3 : component.create.html 로 새로운 컴포넌트 정보를 저장한다 

    + step-4 : ComponentMod.js 개발하기 - CRUD Controller와 Service 그리고 index.html에 파일 추가하기 

    + step-5 : DashboardApp.js routing 정보 업데이트 및 ComponentMod.js 모듈 추가하기 

    

// index.html 에서 ui-view에 component.html전체가 표현됨

 <div ui-view class="container" style="margin-top:30px"></div>


// step-1,2,3) *.html 파일 만들기 

// component.html 에서 목록을 받아와서 ng-repeat 하기 

<li ng-repeat="component in components"

    ng-class="{ active: $state.includes('mc-component.detail') && $stateParams.componentId == component.id }">

  <a ui-sref=".detail({componentId:component.id})" style="padding: 2px 2px">{{component.name}}</a>

</li>

.. 중략 ..

// 여기에 component.detail.html 과 component.create.html 내용이 표현됨 

<div ui-view></div>


// component.detail.html : value에 값을 표현하기, ng-model을 통하여 controller에서 변경값 처리하기 (create html도 유사함) 

<form class="form-horizontal" role="form">

<div class="form-group">

<label class="col-sm-3 control-label"> Name</label>

<div class="col-sm-9">

<input type="text" class="form-control" placeholder="Name" ng-model="component.name" value="{{component.name}}" required>

</div>

</div>

<div class="form-group">

<label class="col-sm-3 control-label"> Type</label>

<div class="col-sm-9">

<input type="text" class="form-control" placeholder="Type" ng-model="component.type" value="{{component.type}}" required>

</div>

</div>

<div class="form-group">

<label class="col-sm-3 control-label"> Config</label>

<div class="col-sm-9">

<textarea class="form-control" id="message" name="message" ng-model="component.cfg" value="{{component.cfg}}"

placeholder="Config" rows="5" required></textarea>

</div>

</div>

<div class="form-group last">

<div class="col-sm-offset-3 col-sm-9">

<button type="submit" class="btn btn-success btn-primary" data-ng-click="update()">Update</button>

<button type="submit" class="btn btn-error btn-primary" data-ng-click="deleteComp()">Delete</button>

</div>

</div>

</form>

  - step-4 : CompoonentMod.js 개발 

'use strict';

// 모듈 정의 

var ComponentMod = angular.module('MobiConSoft.ComponentMod', []);

// ui-router의 서비스 추가 

ComponentMod.controller('ComponentMod.componentCtrl', ['$rootScope', '$scope', 'RestfulSvcApi', '$state', '$stateParams', 'componentSvc',

                                                              function ($rootScope, $scope, RestfulSvcApi, $state, $stateParams, componentSvc) {

        // 서브 화면에서 Create, Delete, Update가 일어나면 목록의 components 를 two-way binding으로 업데이트하기 위함

       $scope.$on("updateComponentList", function(event, data){

          console.log('--broadcasting data, ', data);

          $scope.components = data;

       });

var getAll = function() {

console.log('-----all: ', $scope.components);

componentSvc.getAll();

};

var getOne = function() {

console.log('-----one: ', $stateParams.componentId);

RestfulSvcApi.one({ domain: 'component', key: $stateParams.componentId },

function(response) {

console.log('componentCtrl : one data=',response);

   $scope.component = response;

},

function(response) {});

};

if($state.current.name === 'mc-component' || (!$stateParams.componentId && !$scope.components)) {

getAll();

};

if($state.current.name === 'mc-component.detail' && $stateParams.componentId) {

getOne();

};

$scope.update = function() {

console.log('componentCtrl : component=', $scope.component);

RestfulSvcApi.update({ domain: 'component'}, $scope.component,

function(response) {

console.log('---update, ', response);

getAll();

},

function(response) {});

};

$scope.deleteComp = function() {

RestfulSvcApi['delete']({ domain: 'component', key: $scope.component.id },

function(response) {

console.log('---delete, ', response);

$scope.component = {};

getAll();

},

function(response) {});

};

  

$scope.save = function() {

console.log('----> component is ', $scope.component);

RestfulSvcApi.save({ domain: 'component' }, $scope.component,

function(response) {

console.log('---save, ', response);

getAll();

},

function(response) {});

};

 }]);


// 전체 목록에 대해서만 Service로 구현함 : Broadcasting에 대한 별도 추가 테스트 필요함

ComponentMod.service('componentSvc', ['$rootScope', 'RestfulSvcApi', function ($rootScope, RestfulSvcApi) {

this.getAll = function() {

RestfulSvcApi.all({ domain: 'component' },

function(response) {

console.log('componentSvc : all data=', response);

$rootScope.$broadcast('updateComponentList', response);

},

function(response) {});

};

}]);

  - step-5 : DashboardApp.js routing status 업그레이드

'use strict';


var DashboardApp = angular.module('DasbhoardApp', [

  'ngRoute', 

  'ui.router',

  'ngAnimate',

  'ngCookies',

  'ngResource',

  'ngSanitize',

  'DasbhoardApp.CommonCtrl',

  'MobiConSoft.ComponentMod',

  'DashboardApp.JqGridDrtv',

  'DasbhoardApp.RestTestBiz',

  'DasbhoardApp.JqGridBiz',

  'DasbhoardApp.RestfulSvc'

]);


DashboardApp.config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) {


    $urlRouterProvider.otherwise("/main");

    $stateProvider

      .state('main', {

      url: '/main',

        templateUrl: 'views/main.html'

      })

      .state('resttest', {

      url: '/resttest',

        templateUrl: 'views/restTest.html',

        controller: 'RestTestBiz.personCtrl'

      })

      .state('jqgridtest', {

      url: '/jqgridtest',

        templateUrl: 'views/jqGridTest.html',

        controller: 'JqGridBiz.salesCtrl'

      })

      .state('mc-component', {

      url: '/mc-component',

        templateUrl: 'views/mc/component.html',

        controller: 'ComponentMod.componentCtrl'

      })

     .state('mc-component.detail', {

      url: '/detail/{componentId:[0-9]{1,4}}',

       templateUrl: 'views/mc/component.detail.html',

       controller: 'ComponentMod.componentCtrl'

     })

     .state('mc-component.create', {

      url: '/create',

       templateUrl: 'views/mc/component.create.html',

       controller: 'ComponentMod.componentCtrl'

     });

  }]);


DashboardApp.run(['$rootScope', '$state', '$stateParams',

    function ($rootScope,   $state,   $stateParams) {

      $rootScope.$state = $state;

      $rootScope.$stateParams = $stateParams;

  }]);

  - 결과화면 

    


* 소스 : https://github.com/ysyun/SPA_Angular_SpringFramework_Eclipse_ENV/tree/feature_admin_routing



<참조>

  - AngularJS Views and Directives

  - Angular Route Segment

  - UI-Route : 예제 소스

posted by 윤영식
2013. 11. 28. 07:08 My Projects/BI Dashboard

BI Dashboard 인 만큼 엑셀의 그리드 형태의 표현은 필수적이다. 오픈소스중에서 가장 많이 사용하고 있는 jqGrid를 Angular.js에 포팅하고 RESTful API를 호출하여 표현하는 방법을 알아보자.  




1. jqGrid 설치하기 

  - 최신버전 v4.5.4

  - jqGrid만을 MIT 라이센스하에서 사용을 할 수 있다. 

// bower 통하여 설치하기

$ bower install jqgrid --save


// index.html 에 설정하기

<!-- build:css(.tmp) styles/main.css -->

    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">

    <link rel="stylesheet" href="bower_components/jqgrid/css/ui.jqgrid.css">

    <link rel="stylesheet" href="styles/main.css">

<!-- endbuild -->

... 중략 ...

<!-- build:js scripts/plugins.js -->

    <script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>

    <script src="bower_components/respond/respond.src.js"></script>

    <script src="bower_components/jqgrid/js/i18n/grid.locale-en.js"></script>

    <script src="bower_components/jqgrid/js/jquery.jqGrid.js"></script>

<!-- endbuild -->

  - jqgrid의 theme을 위하여 jquery-ui 의 theme을 입힌다 

// bowe 통하여 설치하기 

$ bower install jquery-ui --save


지금까지의 bower components 목록 버전 의존성

$ bower list

BIDashboard

├── angular#1.2.1 (1.2.2 available)

.. 중략 ..

│ └── angular#1.2.1

├─┬ bootstrap#3.0.2

│ └── jquery#1.9.1 (2.0.3 available)

├── console-shim#05b957a4b1

├── es5-shim#2.0.12 (latest is 2.1.0)

├── jqgrid#4.5.4

├── jquery#1.9.1 (latest is 2.0.3)

├── jquery-migrate#1.2.1

├─┬ jquery-ui#1.10.3

│ └── jquery#1.9.1 (2.0.3 available)

├── json3#3.2.5 (3.2.6 available)

├── modernizr#2.7.0

└── respond#1.3.0


// index.html 에 설정하기 

<!-- build:css(.tmp) styles/main.css -->

    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">

    <link rel="stylesheet" href="bower_components/jquery-ui/themes/smoothness/jquery-ui.css">

    <link rel="stylesheet" href="bower_components/jqgrid/css/ui.jqgrid.css">

    <link rel="stylesheet" href="styles/main.css">

<!-- endbuild -->

... 중략 ...

<!-- build:js scripts/plugins.js -->

    <script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>

    <script src="bower_components/respond/respond.src.js"></script>

    <script src="bower_components/jquery-ui/ui/jquery-ui.js"></script>

    <script src="bower_components/jqgrid/js/i18n/grid.locale-en.js"></script>

    <script src="bower_components/jqgrid/js/jquery.jqGrid.js"></script>

<!-- endbuild -->



2. jqGrid 테스트 메뉴 추가하기 

  - 기존 프로그램에서 jqGrid 테스트 메뉴를 추가한다 

    + step-1 : views 폴더 밑으로 jqGridTest.html 파일 생성

    + step-2 : scripts 폴더 밑으로 biz 폴더 만들고 (여기선 sales) jqGridBiz.js 파일 생성 (화면 : 비즈니스모듈 = 1:1)

                   jqGridBiz.js 의 모듈 명칭, 컨트롤러 명칭을 정의한다 

    + step-3 : Main Application (DashboardApp.js) 안에 신규 Biz 모듈 설정 및 Routing 환경 설정

    + step-4 : index.html 안에 jqGridBiz.js 스크립트 태그 추가

    + step-5 : index.html 안의 메뉴설정에 jqGridTest.html이 보일 수 있도록 routing url 설정

// step-1

// views/jqGrid.html 파일 내역

jqGrid Test Page


// step-2

'use strict';

var RestTestBiz = angular.module('DasbhoardApp.JqGridBiz', ['DasbhoardApp.RestfulSvc']);

RestTestBiz.controller('JqGridBiz.salesCtrl', ['$scope', 'RestfulSvcApi', function ($scope, RestfulSvcApi) {


}]);


// step-3

// DashboardApp.js 변경 내역 

'use strict';

var DashboardApp = angular.module('DasbhoardApp', [

  'ngRoute',                                                  

  'ngCookies',

  'ngResource',

  'ngSanitize',

  'DasbhoardApp.CommonCtrl',

  'DasbhoardApp.RestTestBiz',

  'DasbhoardApp.JqGridBiz',

  'DasbhoardApp.RestfulSvc'

]);


DashboardApp.config(['$routeProvider', function ($routeProvider) {

    $routeProvider

      .when('/', {

        templateUrl: 'views/main.html'

      })

      .when('/resttest', {

        templateUrl: 'views/restTest.html',

        controller: 'RestTestBiz.personCtrl'

      })

      .when('/jqgridtest', {

        templateUrl: 'views/jqGridTest.html',

        controller: 'JqGridBiz.salesCtrl'

      })

      .otherwise({

        redirectTo: '/'

      });

  }]);


// step-4

// index.html 에 추가 

<!-- build:js scripts/dashboard-spa.js -->

    <script src="scripts/DashboardApp.js"></script>

    <script src="scripts/common/controllers/CommonCtrl.js"></script>

    <script src="scripts/common/services/RestfulSvc.js"></script>

    <script src="scripts/person/RestTestBiz.js"></script>

    <script src="scripts/sales/JqGridBiz.js"></script>

<!-- endbuild -->


// step-5

// index.html 의 메뉴 부분에 추가 

<ul class="nav navbar-nav">

<li data-ng-class="activeWhen(path()=='/')">

 <a href="" data-ng-click="setRoute('/')">Home</a>

</li>

<li data-ng-class="activeWhen(path()=='/resttest')">

 <a href="" data-ng-click="setRoute('/resttest')">RESTTest</a>

</li>

<li data-ng-class="activeWhen(path()=='/jqgridtest')">

 <a href="" data-ng-click="setRoute('/jqgridtest')">jqGridTest</a>

</li>

</ul>


// final

// 디렉토리 구조

// http://localhost:8080/#/jqgridtest  호출 결과 



3. jqGrid 의 Angular.js 적용전략 - Directive 만들기 

  - jqGridTest.html partial view html 안에 일반적인 jqGrid 표현 내역을 입력하면 화면에 아무것도 출력되지 않는다

    즉, jqGrid 사용하기 와 같은 <table> 태그와 <script> 태그를 jqGridTest.html 넣으면 화면출력이 되지 않는다는 것이다. 

    이는 Angular.js router를 통하여 partial html이 보여질 때 DOMP (DOM Parser)는 html만을 파싱하여 add 할 뿐이고, 

    <script> 태그는 수행되지 않기 때문이다 (참조)

  - jqGrid에 대한  Common Directive 만들기 : JqGridDrtv.js 

// Directive 모듈 JqGridDrtv.js 개발

'use strict';

var jqGridDrtv = angular.module('DashboardApp.JqGridDrtv', [ 'DasbhoardApp.RestfulSvc' ]);


/**

 * mc is Mobile Convergence of MobiConSoft

 * @use : <mc-jq-grid config="config" data="data"></mc-jq-grid>

 * config and data is used in Controller of Angular.js

 */

jqGridDrtv.directive('mcJqGrid', [ 'RestfulSvcApi', function(RestfulSvcApi) {

return {

restrict : 'E',

scope : {

config : '=',

data : '=',

},

link : function(scope, element, attrs) {

var table;

                         // config attribute 값의 변경을 체크하여 반영한다 

scope.$watch('config', function(newValue) {

element.children().empty();

table = angular.element('<table></table>');

element.append(table);

$(table).jqGrid(newValue);

});

                        // data attribute 값의 변경을 체크하여 반영한다 

scope.$watch('data', function(newValue, oldValue) {

var i;

for (i = oldValue.length - 1; i >= 0; i--) {

$(table).jqGrid('delRowData', i);

}

for (i = 0; i < newValue.length; i++) {

$(table).jqGrid('addRowData', i, newValue[i]);

}

});

}

};

} ]);

  - 메뉴에서 동작하도록 적용하기 

    + step-1 : Controller에 JqGridDrtv.js의 config, data attribute에 대한 샘플 데이터 설정을 한다 

    + step-2 : DashboardApp 안에 JqGridDrtv 모듈 의존관계를 설정한다

    + step-3 : index.html 안에 JqGridDrtv.js <script> 태그에 포함시킨다 

    + step-4 : jqGridTest.html 에 <mc-jq-grid> 태그를 포함시킨다 

// step-1 : JqGridBiz.js 에서 config, data 값 설정

'use strict';

var JqGridBiz = angular.module('DasbhoardApp.JqGridBiz', ['DasbhoardApp.RestfulSvc']);


JqGridBiz.controller('JqGridBiz.salesCtrl', ['$scope', 'RestfulSvcApi', function ($scope, RestfulSvcApi) {

$scope.config = {

  datatype: "local",

  height: 150,

    colNames:['Inv No','Date', 'Client', 'Amount','Tax','Total','Notes'],

    colModel:[

    {name:'id',index:'id', width:60, sorttype:"int"},

    {name:'invdate',index:'invdate', width:90, sorttype:"date"},

    {name:'name',index:'name', width:100},

    {name:'amount',index:'amount', width:80, align:"right",sorttype:"float"},

    {name:'tax',index:'tax', width:80, align:"right",sorttype:"float"},

    {name:'total',index:'total', width:80,align:"right",sorttype:"float"},

    {name:'note',index:'note', width:150, sortable:false}

    ],

    multiselect: true,

    caption: "Manipulating Array Data"

   };

   

$scope.data = [

{id:"1",invdate:"2007-10-01",name:"test",note:"note",amount:"200.00",tax:"10.00",total:"210.00"},

{id:"2",invdate:"2007-10-02",name:"test2",note:"note2",amount:"300.00",tax:"20.00",total:"320.00"},

{id:"3",invdate:"2007-09-01",name:"test3",note:"note3",amount:"400.00",tax:"30.00",total:"430.00"},

{id:"4",invdate:"2007-10-04",name:"test",note:"note",amount:"200.00",tax:"10.00",total:"210.00"},

];

}]);


// step-2 : DashboardApp.js에서 의존성 설정

var DashboardApp = angular.module('DasbhoardApp', [

  'ngRoute',                                                  

  'ngCookies',

  'ngResource',

  'ngSanitize',

  'DasbhoardApp.CommonCtrl',

  'DashboardApp.JqGridDrtv',

  'DasbhoardApp.RestTestBiz',

  'DasbhoardApp.JqGridBiz',

  'DasbhoardApp.RestfulSvc'

]);


// step-3 : index.html 에서 <script> 태그 추가 

<!-- build:js scripts/dashboard-4.js -->

    <script src="scripts/DashboardApp.js"></script>

    <script src="scripts/common/controllers/CommonCtrl.js"></script>

    <script src="scripts/common/directives/JqGridDrtv.js"></script>

    <script src="scripts/common/services/RestfulSvc.js"></script>

    <script src="scripts/person/RestTestBiz.js"></script>

    <script src="scripts/sales/JqGridBiz.js"></script>

<!-- endbuild -->


// step-4 : jqGridTest.html 안에 <mc-jq-grid> 태그를 넣는다 

<div id="gridtest" class="container">

<div class="row">

<div class="col-md-8 alert alert-info">

<i class="icon-hand-right"></i> This is jqGrid Directive for AngularJS

</div>

</div>

<mc-jq-grid config="config" data="data"></mc-jq-grid>

</div>


// 최종결과 

// 디렉토리 구조



4. jqGrid에 Bootstrp CSS 적용하기 

  - jqGrid는 기본적으로 jquery-ui theme을 사용한다. Bootstrap으로 기본적인 Layout과 controller를 사용하면 Grid 또한 같은 사용자 경험을 주어야  한다 

  - jqGrid에 Bootstrap과 유사한 CSS 값이 적용된 화면 참조 (소스)

  - 적용 순서

    + styles/main.css 안에 jgGrid.bootstrap.css 내역을 복사하여 넣는다

    + main.css 가 맨밑에 있으므로 jqgrid의 css를 다시 쓰는 역할이다 

<!-- build:css(.tmp) styles/main.css -->

<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">

<link rel="stylesheet" href="bower_components/jquery-ui/themes/smoothness/jquery-ui.css">

<link rel="stylesheet" href="bower_components/jqgrid/css/ui.jqgrid.css">

<link rel="stylesheet" href="styles/main.css">

<!-- endbuild -->

  - 결과 

    



* jQuery UI를 Angular.js에 적용하는 방법

  - Bootstrap 스타일의 CSS를 적용했다면 그다음은 Directive로 만든 jqGrid의 column sorting을 시도해 보자 

  - column우측을 클릭하면 contents 영역에서 "loading..." 메세지만 나오고 sorting이 되지않을 것이다. 해결해 보자 

   


  - jQuery Plugins을 Directive로 만드는 이유 (참조)

// 하기와 같은 코드가 있을 경우 controller scope에서 동작을 하지 않으며, model의 변경도 반영되지 않는다

$('.datepicker').datepicker();


// 안될 경우 Angular.js 의 Directive 생성하여 동작토록 만들어야 한다

// datepicker를 wrapping한 directive 소스 

var directives = angular.module('directives', []);

directives.directive('datepicker', function() {

   return function(scope, element, attrs) {

       element.datepicker({

           inline: true,

           dateFormat: 'dd.mm.yy',

           onSelect: function(dateText) {

               // input field의 jQuery Object

               var modelPath = $(this).attr('ng-model');

               // angular.js $parse와 동일한 역할

               // modelPath = a.b.c , scope = {}, dateText = 2013.11.27 이면 리턴값은 {a : { b : { c: '2013.11.27 }}} 됨

               putObject(modelPath, scope, dateText);  

               scope.$apply();

           }

       });

   }

});


// html 태그에서 datepicker attribute 로 사용한다 

<input type="text" datepicker ng-model="myobj.myvalue" />

  - Directive를 만들어주는 function의 파라미터 값의 의미

    + scope : controller가 관장하는 scope

    + element : jQuery object

    + attrs : 태그의 attribute 속성 key/value

  - datepicker의 callback function을 정의

    + onSelect : 사용자가 date을 선택했을 때 수행할 datepicker의 callback 이다 

    + $apply : 해당 controller scope에 변경값을 적용하기 위하여 scope.$apply를 호출해야 한다 (참조)

  - 데이터가 Object.properties 형태로 관리될 때 좀 더 우아하게 처리하는 방법은 $parse를 이용한다 (참조)

    + step-1 : 선택한 값은 controller의 scope안에 반영시키기 위하여 attrs.ngModel로 가져와서 $parse 파싱한다 

    + step-2 : 날짜의 값을 선택하여 해당 scope의 ngModel에 assign 반영한다 

myApp.directive('datepicker', function ($parse) {

    return function (scope, element, attrs, controller) {

        // step-1

        var ngModel = $parse(attrs.ngModel);

        $(function(){

            element.datepicker({

               ...

               onSelect:function (dateText, inst) {

                    scope.$apply(function(scope){

                        // step-2 Change binded variable

                        ngModel.assign(scope, dateText);

                    });

               }

            });

        });

    }

});

 

function MyCtrl($scope) {

    $scope.userInfo = {

        person: {

            mDate: '1967-10-07'

        }

    };  

 

 * 다른 jQuery UI 들의 Directive 소스

 * 소스 : https://github.com/ysyun/SPA_Angular_SpringFramework_Eclipse_ENV/tree/feature_jqgrid_angular

 


새로운 Directive IE8 에 적용하기

  - IE8에서는 새로운 directive를 만들어 element 태그로 사용할 경우 

  - IE8 이하일 경우 DOM을 만들어준다 (참조, 호환성가이드)

 <head>

    <!--[if lte IE 8]>

      <script>

        document.createElement('mc-jq-grid');

      </script>

    <![endif]-->

</head>



<참조>

  - Partial View html에서 javascript코드가 적용안되는 이유

  - jqGrid Directives 만들기

  - jqGrid에 Bootstrap CSS 적용하기

  - jQuery UI를 datepicker를 Angular.js Directive로 적용하기

  - jQuery UI를 Angular.js Directive로 만든 소스(GitHub) 

  - $parse를 이용하여 a.b.c 으로 시작하는 것을 json object 형태로 바꾸어주는 방법

posted by 윤영식
2013. 11. 27. 04:20 My Projects/BI Dashboard

Angular.js에서 Controller는 View DOM객체의 표현을 담당한다면 Service 모듈은 View단에 표현되는 NonVisible한 비즈니스 업무 데이터를 담당한다. 이전 블로그까지 모든 데이터 핸들링은 Controller에 담겨 있었는데 이를 Service 모듈로 분리 래팩토링 해보자. 또한 모듈분리를 하면서 $http를 추상화한 $resource를 사용해 보자 




1. factory vs service 무엇을 쓸까? (참조)

  - <module>.factory는 객체를 new 해서 return 해 주어야 한다. 

    따라서 별도의 function을 정의하고 해당 펑션을 new해서 return하는 구조일 때 사용

  - <module>.service는 function의 내용을 정의하고 new한 객체를 return할 필요가 없다 

  - <module>.factory 는 공통된 것을 function으로 이미 만들어 놓은 것을 사용할 때. 즉, 공통 모듈 <Module>Svc 에 생성

  - <module>.service 는 업무적으로 $resource/$http 이용하여 RESTful API 호출할  때. 즉 업무 모듈 <Module>Biz 에 생성



2. $resource 사용하기 

  - $resource 객체는 RESTful 클라이언트 애플리케이션 개발을 위한 $http 상위 레벨의 서비스이다

  - http에 대한 보다 정교한 header configuration이 가능하다

  - resource interceptors 가 있다

  - 보다 향상된 promises api를 제공한다 

  - API

// 사용법

$resource(url[, paramDefaults][, actions]);


// 의미 

- url

  string 이고, /user/:username 처럼 :<param> 형태로 입력가능하다. 전체 url을 넣어도 됨 


- paramDefaults 

  url parameters object 이고, /path/:verb 일때 파라미터값이 {verb:'greet', name:'dowon'} 이면

  url 결과는 /path/greet?name=dowon 으로 간다. @가 앞에 붙으면 전달하는 객체(json)에서 값을 추출하여 대입한다

 예) 

  var User = $resource('/user/:userId', {userId:'@id'});

  var user = User.get({userId:123}, function() {

     user.abc = true;

     user.$save();

  });


- actions 

  object.<object> 이고, $http.config의 action 을 상속받으며 사용자 정의하여 action을 추가할 수 있다.

  형식)

  {action1: {method:?, params:?, isArray:?, headers:?, ...},

   action2: {method:?, params:?, isArray:?, headers:?, ...},

   ...}


- 리턴타입

  기본적으로 resource에서 제공하는 action과 사용자 정의 action을 담아서 리턴한다 

  기본 action)

{ 'get':    {method:'GET'},

  'save':   {method:'POST'},

  'query':  {method:'GET', isArray:true},

  'remove': {method:'DELETE'},

  'delete': {method:'DELETE'} };



3. $resource 공통 모듈 만들기

  - $resource의 기본 action을 보면 'update': {method:'PUT'} 이 존재하지 않는다. 이를 custom action으로 포함시키고 공통 모듈로 만들어 보자. 

  - 공통 모듈을 만들기 전에 ngResource를 추가해야 한다. 애플리케이션에 없다는 다음과 과정을 거친다 

// bower로 angular resource 설치 및 bower.json에 추가 

$ bower install angular-resource --save


// index.html 에 추가 

<!-- build:js scripts/core.js -->

    <script src="bower_components/jquery/jquery.js"></script>

    <script src="bower_components/jquery-migrate/jquery-migrate.min.js"></script>

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

    <script src="bower_components/angular-route/angular-route.js"></script>

    <script src="bower_components/angular-resource/angular-resource.js"></script>

    <script src="bower_components/angular-cookies/angular-cookies.js"></script>

    <script src="bower_components/angular-sanitize/angular-sanitize.js"></script>

<!-- endbuild --> 


// ngResource가 필요한 모듈에 추가하기  

var DashboardApp = angular.module('DasbhoardApp', [

  'ngRoute',                                                  

  'ngCookies',

  'ngResource',

  'ngSanitize',

  'DasbhoardApp.CommonCtrl',

  'DasbhoardApp.RestTestBiz',

]);

  - $resource를 이용하여 공통 모듈을 만들어 보자 

// common/services/RestfulSvc.js 파일을 생성한다 

'use strict';

// 모듈 명칭 정의

var RestfulSvc = angular.module('DasbhoardApp.RestfulSvc', []);


RestfulSvc.factory('RestfulSvcApi', ['$resource', function ($resource) {

/**

* Restful API version. it must be attached the called url.

*/

var prefixUrl = '/api/v1';

/**

* @domain : biz object ex) person

* @key : biz object id ex) persion id is 123

* @action : server action name. if it exist, you can define or not.

* ex) http://www.bennadel.com/blog/2433-Using-RESTful-Controllers-In-An-AngularJS-Resource.htm

* add a update action for method of PUT

*/

return $resource( 

                           // 호출하는 url 형식

    prefixUrl + '/:domain/:key/:action',

                           // 호출 url 형식의 :domain :key :action 에 동적으로 받게 되는 파라미터

   {

    domain: "@domain",

    key: "@key",

    action: "@action"

   },

                            // 추가 action들 one, all, update 

   {

     one: { method: 'GET', isArray: false },

     all: { method: 'GET', isArray: true },

      update: {method:'PUT'}

    }

   );

}]);


// 새롭게 서비스 공통 모듈을 생성하였으니 index.html 과 DashboardApp 모듈에 추가한다 
// index.html 에서 
    <script src="scripts/common/controllers/CommonCtrl.js"></script>
    <script src="scripts/common/services/RestfulSvc.js"></script>

// DashboardApp.js 에서 
var DashboardApp = angular.module('DasbhoardApp', [
  'ngRoute',                                                  
  'ngCookies',
  'ngResource',
  'ngSanitize',
  'DasbhoardApp.CommonCtrl',
  'DasbhoardApp.RestTestBiz',
  'DasbhoardApp.RestfulSvc'
]);

 


4. Controller에 Service 모듈의 서비스 호출하기 

  - $http 방식을 $resource에 대한 추상 모듈 서비스로 대체한다

  - 기존의 RestTestBiz.js의 "RestTestBiz.personCtrl" 컨트롤러 로직을 리팩토링한다 

'use strict';

// 의존하는 모듈 주입

var RestTestBiz = angular.module('DasbhoardApp.RestTestBiz', ['DasbhoardApp.RestfulSvc']);


// 사용할 모듈의 서비스 주입 

RestTestBiz.controller('RestTestBiz.personCtrl', ['$scope', 'RestfulSvcApi', function ($scope, RestfulSvcApi) {


  var bizDomain = 'person';

  var load = function () {

    console.log('before calling...');

       // 사용자 정의 액션 all 호출 

    RestfulSvcApi.all(

    // param key domain의 값인 bizDomain는 @domain에 맵핑된다 

    { domain: bizDomain },

    // success

    function (response) {

    $scope.persons = response;

    console.log('persons is ', $scope.persons);

    }, 

    // error

    function (response) {

    });

     };


  load();


  $scope.save = function () {

  console.log('Save Person : ', $scope.person);

  RestfulSvcApi.save(

                        // 파라미터를 열거하면 하나의 json 객체로 합쳐준다 

  { domain: bizDomain }, $scope.person,

  function(response) { load(); },

  function(response) {}

  );

  };


  $scope.update = function () {

  console.log('Update Person : ', $scope.person);

  RestfulSvcApi.update(

  { domain: bizDomain }, $scope.person ,

  function(response) { load(); },

  function(response) {}

  );

  };


  $scope.deletePerson = function(personId) {

  console.log('Delete Person : ', personId);

  RestfulSvcApi['delete'](

  { domain: bizDomain, key: personId }, 

  function(response) { load(); },

  function(response) {}

  );

  };


}]);

  - RestfulSvcApi 와 같은 모듈 서비스를 해주는 추상화 공개 모듈이 존재한다. (참조)

    + Restangular

    + ModelCore

    + Angular-ServiceStack

    + Angular-ActiveRecord

    + Breezejs


  * 소스 : https://github.com/ysyun/SPA_Angular_SpringFramework_Eclipse_ENV/tree/feature_angular_service



<참조>

  - Angular.js v1.2 에서 향상된 내역들

  - ngResource를 통하여 트위터 연동하기 예제

  - $resource를 통한 RESTful API 구현하기 (필독)

  - rest 스타일의 ORM 모듈들

posted by 윤영식
2013. 11. 22. 03:53 My Projects/BI Dashboard

Angular.js를 하면서 모듈단위로 개발을 할 수 있고, 여러명이 동시에 관련 모듈을 개발하여 합칠 수도 있다. 즉, 엔터프라이즈급 애플리케이션 개발 공수가 든다면 모듈을 나누고 어떻게 합쳐서 하나의 SPA로 만드는지 알아보자 




1. 모듈 단위 나누기

  - 기존의 하나의 애플리케이션 모듈로 진행되는 것을 여러 모듈로 나어 큰 규모의 애플리케이션을 만들자 (참조)

    + 하나의 애플리케이션 .js 파일을 하나의 모듈로 한다

    + 모듈은 앞글자가 대문자로 시작한다 (App, Biz / Ctrl, Svc, Flt, Drtv 등 접미어를 붙여서 구분한다)

    + 모듈파일이 많이 커질때는 Lazy Loading을 고려한다 (참조1, 참조2)

    + 일반 기능들의 명칭은 소문자로 시작한다 (Ctrl, Svc, Flt, Drtv 등 접미어를 붙인다)

    + 공통으로 사용되는 것은 $rootScope 또는 Factory를 이용한다 

    + 파일과 모듈이 분리되어 테스트 코드를 쉽게 만들어 테스트 가능하다

  - 업무 모듈 유형 

    + Application Main 모듈 : <ProjectName>App.js (주로 routing 관련 설정)

    + Biz 모듈 : <UI명칭>Biz.js (화면과 맵핑되는 모듈 = n controller + n service/factory 존재)

  - 공통 모듈 유형 

    + Controller 모듈 : CommonCtrl.js (메뉴, 로그인/로그아웃 UI 핸들링)

    + Service 모듈 : CommonSvc.js (메뉴, 로그인/로그아웃 서버통신 처리)

    + Directives 모듈 : CommonDrtv.js (일반적인 지시자들), UtilDrtv.js (유틸리티), UXDrtv.js (UI), <User Defined>Dtrv

    + Filters 모듈 : CommonFlt (공통), UtilFlt (유틸) 필터링 관련 모듈 

  - /src/main/scripts의 기능별 폴더를 생성하여 모듈을 분리한다. 또는 업무적으로 폴더를 분리할 수도 있다

// /src/main/scripts 에서 

// 기존 폴더

/controllers/main.js

app.js 


// 추가 폴더 및 명칭변경 

/controllers

/directives

/filters

/services

DashboardApp.js

  - 주로 많이 개발하는 것이 화면단위의 <BizName>Ctrl/Svc.js 가 될 것이다 

  - vendor가 제공하는 컴포넌트는 Bower를 통하여 설치 관리되며, 배포시에 Grunt를 통하여 minification 된다 



2. 공통 모듈 분리하기 

  - 폴더를 만들었으면 이제 .js 안에 모듈명을 지정하고, 의존관계를 설정한다 

  - 모듈 명칭은 namespace 구분을 위하여 자바의 패키지처럼 <ApplicatonName>.<구분>.<FileName> 이라고 대소문자 작명한다

  - 모듈 분리 순서

    + app.js 파일명칭을 DashboarApp.js 로 변경하고, 모듈명칭을 DashboardApp로 시작한다

    + controllers/main.js 파일명칭을 CommonCtrl.js 로 변경하고, 모듈명칭을 <ApplicationName>.CommonCtrl로 변경한다

// DashboardApp.js 에서 

// 두번째 파라미터 배열에서 의존관계에 있는 모듈을 정의한다

var DashboardApp = angular.module('DasbhoardApp', [

  'ngCookies',

  'ngResource',

  'ngSanitize',

  'DasbhoardApp.CommonCtrl'

]);


// CommonCtrl.js 에서 

var CommonCtrl = angular.module('DasbhoardApp.CommonCtrl', []);

    + 분리된 파일은 index.html 에 추가한다 : 향후 파일이 늘어나면 Lazy loading을 고려한다 

// index.html 에서 

<!-- build:js scripts/dashboard-spa.js -->

<script src="scripts/DashboardApp.js"></script>

<script src="scripts/controllers/CommonCtrl.js"></script>

.. 이곳에 추가를 한다 ..

<!-- endbuild -->



3. 업무 모듈 분리하기 

  - Person 테이블을 보여주는 restTest.html 파일에 대한 별도 모듈을 만든다. (resttest.html을 restTest.html로 명칭 변경했음)

  - 1 view : 1 module 로 구성하고, 1 module :  n controller + n service 를 넣는다. 즉 1 module : 1 biz 로 한다

// step-1 : 기존 scripts 에 나열 되었던 controllers, directives, filters, services 폴더를 common 폴더를 만들어 옮긴다 

// step-2 : 업무적인 폴더는 scripts 폴더에 정의한다. 여기서는 person 폴더를 생성함 

// step-3 : scripts/person 폴더 밑에 RestTestBiz.js 파일을 만든다 

// step-4 : index.html 내역을 다음과 같이 수정한다


<!-- build:js scripts/dashboard-spa.js -->

<script src="scripts/DashboardApp.js"></script>

<script src="scripts/common/controllers/CommonCtrl.js"></script>

<script src="scripts/person/RestTestBiz.js"></script>

<!-- endbuild -->


// 최종 모습 

  - CommonCtrl.js 에 들어있는 restTestCtrl 코드를 RestTestBiz.js 파일로 옮기고, 명명규칙에 따라 수정한다 

var RestTestBiz = angular.module('DasbhoardApp.RestTestBiz', []);


RestTestBiz.controller('RestTestBiz.personCtrl', ['$scope', '$http', function ($scope, $http) {

    .. 중략 ..

    $scope.deletePerson = function(id) {

    console.log('Delete Person : ', id);

        $http['delete'](actionUrl + id).success(function () {

            load();

        });

    };

  }]);


// RestTestBiz.service or RestTestBiz.factory 를 정의한다 

  - DashboardApp.RestTestBiz 모듈을 DashboardApp.js에서 의존성을 등록하고, Routing시의 controller 명칭도 수정 등록한다 

var DashboardApp = angular.module('DasbhoardApp', [

  'ngRoute',                                                  

  'ngCookies',

  'ngResource',

  'ngSanitize',

  'DasbhoardApp.CommonCtrl',

  'DasbhoardApp.RestTestBiz',

]);


DashboardApp.config(['$routeProvider', function ($routeProvider) {

    $routeProvider

      .when('/', {

        templateUrl: 'views/main.html'

      })

      .when('/resttest', {

        templateUrl: 'views/restTest.html',

        controller: 'RestTestBiz.personCtrl'

      })

      .otherwise({

        redirectTo: '/'

      });

  }]);



* Angular.js v1.2.1 업그레이드 

  - 가장 큰점은 Angular의 route 가 별도로 분리되었다는 것이다 (참조1, 참조2)

  - v1.2.1 버전을 설치해 보자 

// bower를 통하여 기존 모듈 제거 및 재설치 작업 수행

$ bower uninstall angular angular-resource angular-cookies angular-mocks angular-sanitize angular-scenario

bower uninstall     angular

bower uninstall     angular-resource

bower uninstall     angular-cookies

bower uninstall     angular-mocks

bower uninstall     angular-sanitize

bower uninstall     angular-scenario


// bower.json에서 버전 정보를 삭제한다 

// --save 설치 

$ bower install angular angular-route angular-resource angular-cookies angular-sanitize --save


// --save-dev 설치 

$ bower install angular-mocks angular-scenario --save-dev


// 설치 버전 의존성 정보

$ bower list

bower check-new     Checking for new versions of the project dependencies..

SolarDasbhoard#0.0.0 /Users/nulpulum/development/eclipse_workspace/lg_solar/SPA_ASE

├── angular#1.2.1

├─┬ angular-cookies#1.2.1

│ └── angular#1.2.1

├─┬ angular-mocks#1.2.1

│ └── angular#1.2.1

├─┬ angular-resource#1.2.1

│ └── angular#1.2.1

├─┬ angular-route#1.2.1

│ └── angular#1.2.1

├─┬ angular-sanitize#1.2.1

│ └── angular#1.2.1

├─┬ angular-scenario#1.2.1

│ └── angular#1.2.1

├─┬ bootstrap#3.0.2

│ └── jquery#1.9.1 (2.0.3 available)

├── es5-shim#2.0.12 (latest is 2.1.0)

├── jquery#1.9.1 (latest is 2.0.3)

├── jquery-migrate#1.2.1

├── json3#3.2.5

└── respond#1.3.0

  - routing 관련 설정 변경

// index.html 에서 angular.js 밑으로 angular-route.js 추가 

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

<script src="bower_components/angular-route/angular-route.js"></script>


// DashboardApp.js 파일에서 ngRoute 모듈 추가 

var DashboardApp = angular.module('DasbhoardApp', [

  'ngRoute',                                                  

  'ngCookies',

  'ngResource',

  'ngSanitize',

  'DasbhoardApp.CommonCtrlMod'

]);



* HTML5 지원하는 모듈 넣기 

  - html5shiv를 포함한 modernizr를 설치하여 오랜된 브라우져에서는 HTML5가 지원토록 한다 (참조)

  - IE 9 이하에서 선택적으로 넣자 

// bower install

$ bower install modernizr --save


// index.html 에서 

    <!--[if lt IE 9]>

      <script src="bower_components/modernizr/modernizr.js"></script>

      <script src="bower_components/es5-shim/es5-shim.js"></script>

      <script src="bower_components/json3/lib/json3.min.js"></script>

    <![endif]-->



* 메뉴 동작방식 변경하기 

  - data-ng-click 방식으로도 변경해 본다

// index.html 에서 

// 기존 코드 

<div class="collapse navbar-collapse">

  <ul class="nav navbar-nav">

    <li data-ng-class="activeWhen(path()=='/')"><a href="#/">Home</a></li>

    <li data-ng-class="activeWhen(path()=='/resttest')"><a href="#/resttest">RESTTest</a></li>

  </ul>

</div>


// 변경 코드

<div class="collapse navbar-collapse">

  <ul class="nav navbar-nav">

    <li data-ng-class="activeWhen(path()=='/')">

      <a href="" data-ng-click="setRoute('/')">Home</a>

    </li>

    <li data-ng-class="activeWhen(path()=='/resttest')">

      <a href="" data-ng-click="setRoute('/resttest')">RESTTest</a>

    </li>

  </ul>

</div>

  - 모듈안에서 사용되는 controller, service, filter, directive의 명칭은 <Module명>.<기능명칭> 처음에는 소문자로 시작한다 

// index.html 에서 

<div class="navbar navbar-inverse navbar-fixed-top"  data-ng-controller="CommonCtrlMod.menuCtrl">

..중략..

</div> 

  - menuCtrl안에 setRoute 메소드를 정의한다 

CommonCtrlMod.controller('CommonCtrlMod.menuCtrl', ['$scope', '$location', function ($scope, $location) {

    $scope.activeWhen = function (value) {

    return value ? 'active' : '';

    };

    $scope.path = function () {

        return $location.url();

    };

    $scope.setRoute = function (url) {

        $location.path(url);

    }    

  }]);


* 소스 : https://github.com/ysyun/SPA_Angular_SpringFramework_Eclipse_ENV/tree/feature_angular_modules



<참조>

  - Angular.js 기반 큰규모 애플리케이션 구조 만들기

  - Angular.js v1.2 에서 변경 추가된 사항들-Outsider

  - Angular.js v1.2 에서 변경된 것들

  - HTML5 지원을 위한 Modernizr vs Html5shiv 에 대하여 

posted by 윤영식
2013. 11. 16. 05:46 My Projects/BI Dashboard

Spring MVC의 RESTful 방식을 사용하면서 AngularJS를 Eclipse에서 개발하기 위한 제반 설정 환경을 만들어 보도록 한다. 핵심은 Java를 위한 라이브러리 의존성 관리와 빌드를 위하여 Maven을 사용하고 JavaScript는 의존성 관리는 bower를 사용하고 빌드도구는 grunt를 사용하게 된다. 따로 따로 사용하지 않고 하나의 프로젝트로 합치는 과정을 생각해 보자 



1. Maven을 이용한 Spring MVC 환경 만들기 

  - 이미 Eclipse Juno 기반에서 Maven + Spring MVC 환경 설정 블로깅을 하였다

  - pom.xml 에 logback 관련 내용 추가 (참조)

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.ysyun</groupId>

<artifactId>DashboardTest</artifactId>

<version>0.0.1-SNAPSHOT</version>

<packaging>war</packaging>

<url>http://maven.apache.org</url>


<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<spring.version>3.2.0.RELEASE</spring.version>

<junit.version>4.11</junit.version>

<jdk.version>1.6</jdk.version>

</properties>


<dependencies>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-core</artifactId>

<version>${spring.version}</version>

<exclusions>

<exclusion>

<artifactId>commons-logging</artifactId>

<groupId>commons-logging</groupId>

</exclusion>

</exclusions>

</dependency>


<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-web</artifactId>

<version>${spring.version}</version>

</dependency>


<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>${spring.version}</version>

<exclusions>

<exclusion>

<groupId>commons-logging</groupId>

<artifactId>commons-logging</artifactId>

</exclusion>

</exclusions>

</dependency>


<!-- use logback logger -->

<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-access</artifactId>

<version>1.0.13</version>

</dependency>

<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-core</artifactId>

<version>1.0.13</version>

</dependency>

<dependency>

<groupId>ch.qos.logback</groupId>

<artifactId>logback-classic</artifactId>

<version>1.0.13</version>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId>

<version>1.7.5</version>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>jcl-over-slf4j</artifactId>

<version>1.7.5</version>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>log4j-over-slf4j</artifactId>

<version>1.7.5</version>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-jcl</artifactId>

<version>1.7.5</version>

</dependency>


<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>${junit.version}</version>

<scope>test</scope>

</dependency>

</dependencies>


<build>

<finalName>DashboardTest</finalName>

<plugins>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>3.0</version>

<configuration>

<source>${jdk.version}</source>

<target>${jdk.version}</target>

</configuration>

</plugin>

</plugins>

</build>

</project>

  - 다음에 logback.xml 환경파일을 resource에 놓아 classpath에 잡아준다. 개발/배포 따로 구성한다 

<?xml version="1.0" encoding="UTF-8"?>

<configuration scan="true">

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">

        <encoder>

            <charset>utf-8</charset>

            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>

        </encoder>

    </appender>

    <logger name="ch.qos.logback" level="WARN"/>

    <logger name="org.apache" level="WARN"/>

    <logger name="org.springframework" level="WARN"/>

    <logger name="org.springframework.web" level="WARN"/>

    <logger name="org.springframework.security" level="WARN"/>

    <logger name="org.springframework.cache" level="WARN"/>


    <root level="DEBUG">

        <appender-ref ref="CONSOLE"/>

    </root>

</configuration>




2. SPA 환경 잡아주기 

  - Yeoman을 통하여 angular 프로젝트 만들기 (참조)

$ yo angular SolarDasbhoard


  - 위의 파일을 각각 다음과 같이 이동한다 

    + app/ 폴더에 있는 것을 src/main/webapp로 복사한다 

    + node_modules 및 test 폴더 그리고 나머지 모든 파일을 eclipse 프로젝트 root로 복사한다 

      여기서 node_modules안의 모듈은 grunt와 karma 테스트 모듈들로 운영에 배포될 필요가 없다. 

      운영 배포는 webapp/ 밑에 있는 파일들이고, 사용하는 모듈은 src/main/webapp/bower_components/ 밑에 위치한다 

      


  - app폴더 밑의 내용이 src/main/webapp로 이동하였으므로 관련 환경설정 내역을 수정한다 

// bower 설정파일인 .bowerrc  변경 

{

    "directory": "src/main/webapp/bower_components"

}


// karm.conf.js 파일 내역 수정

    files: [

      'src/main/webapp/bower_components/angular/angular.js',

      'src/main/webapp/bower_components/angular-mocks/angular-mocks.js',

      'src/main/webapp/bower_components/angular-resource/angular-resource.js',

      'src/main/webapp/bower_components/angular-cookies/angular-cookies.js',

      'src/main/webapp/bower_components/angular-sanitize/angular-sanitize.js',

      'src/main/webapp/scripts/*.js',

      'src/main/webapp/scripts/**/*.js',

      'test/mock/**/*.js',

      'test/spec/**/*.js'

    ],


// grunt build를 위한 Gruntfile.js 내역 수정 

// 소스의 위치와 grunt build시에 dist 폴더에 .html과 WEB-INF/* 파일까지도 copy 하도록 수정 

 yeoman: {

      // configurable paths

      app: require('./bower.json').appPath || 'src/main/webapp',

      dist: 'dist'

    },

... 중략 ...

 copy: {

      dist: {

        files: [{

          expand: true,

          dot: true,

          cwd: '<%= yeoman.app %>',

          dest: '<%= yeoman.dist %>',

          src: [

            '*.{ico,png,txt,html}',

            '.htaccess',

            'bower_components/**/*',

            'images/{,*/}*.{gif,webp}',

            'styles/fonts/*',

            'WEB-INF/*'

          ]

        }, {

          expand: true,

          cwd: '.tmp/images',

          dest: '<%= yeoman.dist %>/images',

          src: [

            'generated/*'

          ]

        }]

      },

      styles: {

        expand: true,

        cwd: '<%= yeoman.app %>/styles',

        dest: '.tmp/styles/',

        src: '{,*/}*.css'

      }

    },



3. .war 배포파일 만들기 

  - Maven(java 서버)와 Yo(javascript 클라이언트) 환경설정을 완료하였다면 빌드했을 때 최종파일을 .war파일로 묶어보자 

  - grunt build를 했을 경우 프로젝트의 root/dist 디렉토리가 신규 생성되어 src/main/webapp/* 파일이 minification 되어 들어있다

  - 해당 파일을 .war안에 묶기 위하여 pom.xml 에 플러그인 설정 추가

<build>

<finalName>DashboardTest</finalName>

<plugins>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>3.0</version>

<configuration>

<source>${jdk.version}</source>

<target>${jdk.version}</target>

</configuration>

</plugin>

<!-- grunt build 수행하여서 생성되는 폴더인 dist 를 지정한다 -->

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-war-plugin</artifactId>

<version>2.4</version>

<configuration>

<warSourceDirectory>dist</warSourceDirectory>

</configuration>

</plugin>

</plugins>

</build>


  - 배포파일 war 만들기 순서

$ grunt build (if error ocurred, option --force)

$ mvn clean install

[INFO] Scanning for projects...

[INFO] ------------------------------------------------------------------------

[INFO] Building DashboardTest 0.0.1-SNAPSHOT

[INFO] ------------------------------------------------------------------------

.. 중략 ..

[INFO] ------------------------------------------------------------------------

[INFO] BUILD SUCCESS

[INFO] ------------------------------------------------------------------------

[INFO] Total time: 5.109s

[INFO] Finished at: Fri Nov 15 15:43:09 EST 2013

[INFO] Final Memory: 12M/81M

[INFO] ------------------------------------------------------------------------

  * eclipse 의 "Run As..." 의 "5 Maven Build"를 해도 같은 결과를 받음 


이제 eclipse 상에서 Spring Framework + AngularJS Framework 으로 개발을 해보자 

GitHub URL : https://github.com/ysyun/SPA_Angular_SpringFramework_Eclipse_ENV



<참조>

  - SLF4J를 통하여 logback 사용하기

  - Yeoman을 통한 AngularJS + Express 환경구성하기

  - Maven 기본 디렉토리 layout, pom.xml 에서 변경하는 방법

posted by 윤영식
2013. 11. 15. 04:42 My Projects/BI Dashboard

자바기반 서버 환경 구축을 위한 기반 기술 설정에 대하여 알아보자. 우선 빌드환경인 Maven과 Spring Framework을 설치하고 간단히 RESTful 호출 테스트를 해보자 



1. Eclipse 플러그인 설치 

  - Eclipse Juno를 다운로드하여 설치 및 JDK1.6 사용

  - Sublime text 사용에 익숙하다면 http://eclipsecolorthemes.org/ 플러그인을 설치후 에서 Sublime Theme으로 바꾸어 사용한다 

  - Eclipse Marketplace에서 

    + STS (Spring Tool Suite - juno 4.2)를 설치

    + Maven (Maven Integration for Eclipse - juno and newer)를 설치

    + Git 사용한다면 EGit - Git Team Provider를 설치

    + SVN 사용한다면 Subversive - SVN Team Provider 설치 (참조1, 참조2)

       > "Help/Install New Software..."에서 

       > SVN Connector - http://community.polarion.com/projects/subversive/download/eclipse/3.0/juno-site/ 통해 설치

          Mac의 HL 부분은 unselect 

       > SVN Kit 1.7.8 선택한다. 

      


  

2. Spring MVC 프로젝트 구성

  - Maven으로 Simple project를 만든다. jar 배포 프로젝트로 만듦

  - 프로퍼티에서 Project facets에서 "Dynamics Web Module"를 선택하고 "Further configuration available..."에서 webapp 설정

  - webapp를 설정하였으면 src/main 밑으로 강제 이동시킴 

    

  - 프로퍼티에서 Deployment Assembly에서 add... 하여 Maven Denpendencies... 라이브러리가 WEB-INF/lib에 포함되도록 설정

    (참조)

    


  - pom.xml 에서 <packaging>jar</packaging>에서  <packaging>war</packaging>로 바꾼다 

  - 하기 의존성 라이브러리를 추가한다 

    + Spring Framework : v3.2.0

    + JDK : v1.6

    + JUnit : v4.11

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.ysyun</groupId>

<artifactId>DashboardTest</artifactId>

<version>0.0.1-SNAPSHOT</version>

<packaging>war</packaging>


<properties>

<spring.version>3.2.0.RELEASE</spring.version>

<junit.version>4.11</junit.version>

<jdk.version>1.6</jdk.version>

</properties>


<dependencies>


<!-- Spring 3 dependencies -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-core</artifactId>

<version>${spring.version}</version>

</dependency>


<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-web</artifactId>

<version>${spring.version}</version>

</dependency>


<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>${spring.version}</version>

</dependency>


<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>${junit.version}</version>

<scope>test</scope>

</dependency>

</dependencies>

<build>

<finalName>DashboardTest</finalName>

<plugins>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>3.0</version>

<configuration>

<source>${jdk.version}</source>

<target>${jdk.version}</target>

</configuration>

</plugin>

</plugins>

</build>

</project>



3. RESTful 테스트 하기 

  - Controller 추가하기 : RESTful uri 설정

package com.lge.dashboard;


import org.springframework.stereotype.Controller;

import org.springframework.ui.ModelMap;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;


@Controller

@RequestMapping("/")

public class HelloController {

 

@RequestMapping(value="/welcome", method = RequestMethod.GET)

public String welcome(ModelMap model) {

 

model.addAttribute("message", "Maven Web Project + Spring 3 MVC - welcome()");

 

//Spring uses InternalResourceViewResolver and return back index.jsp

return "index";

}

 

@RequestMapping(value="/welcome/{name}", method = RequestMethod.GET)

public String welcomeName(@PathVariable String name, ModelMap model) {

 

model.addAttribute("message", "Maven Web Project + Spring 3 MVC - " + name);

return "index";

 

}

 

}

  - web.xml 설정 : spring dispatcher를 설정한다 

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"

id="WebApp_ID" version="3.0">

<display-name>Counter Web Application</display-name>


<servlet>

<servlet-name>mvc-dispatcher</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>


<servlet-mapping>

<servlet-name>mvc-dispatcher</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>


<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>

</context-param>


<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

</web-app>

  - mvc-dispathcer-servlet.xml 스프링 환경설정 

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="

        http://www.springframework.org/schema/beans     

        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

        http://www.springframework.org/schema/context 

        http://www.springframework.org/schema/context/spring-context-3.0.xsd">

 

<context:component-scan base-package="com.lge.*" />

 

<bean

class="org.springframework.web.servlet.view.InternalResourceViewResolver">

<property name="prefix">

<value>/WEB-INF/pages/</value>

</property>

<property name="suffix">

<value>.jsp</value>

</property>

</bean>

 

</beans>

  - WEB-INF/pages폴더를 만들고 index.jsp 파일 생성 

<html>

<body>

<h2>Hello World!</h2>

 

<h4>Message : ${message}</h1>

</body>

</html>

  - 전체 설정 정보 

    

  - Tomcat v7.0 설정이 되었다면 Run As 에서 "Run on Server"로 DashboardTest 컨텍스트 추가하여 Tomcat 기동

    /<contextName>/welcome/<value> 넣어서 호출하면 하기와 같이 나온다 

    

  - maven Goal에 대한 명령을 수행할 경우 

Goal은 ant의 target과 같은 개념으로, mvn의 경우 pre-defined goal을 가지고 있다.

mvn compile : 컴파일 수행

mvn package : jar 파일로 패키징

mvn test : JUnit 테스트 수행

mvn install : local repository (PC내의 디렉토리)에 해당 jar 파일을 저장 

mvn deploy  : remote repository (Nexus)에 jar 파일 저장

mvn clean : 컴파일 내용 모두 삭제

mvn build : 위의 예제는 일반적은 spring-java 코드로 build goal을 제공하지 않는다. 

(Eclipse에서 run as하면 나와서 헷갈리게 만드는데) build goal을 지원하는 프로젝트를 만들거나 또는 pom.xml안에 인위적으로 build goal을 지정해줘야 한다. 보통 clean install 등 여러 명령을 스페이스로 구분하여 파라미터를 줄 수 있다 

  - 만들어진 pom.xml 파일 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.ysyun</groupId>

<artifactId>DashboardTest</artifactId>

<version>0.0.1-SNAPSHOT</version>

<packaging>war</packaging>

<url>http://maven.apache.org</url>


<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<spring.version>3.2.0.RELEASE</spring.version>

<junit.version>4.11</junit.version>

<jdk.version>1.6</jdk.version>

</properties>


<dependencies>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-core</artifactId>

<version>${spring.version}</version>

</dependency>


<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-web</artifactId>

<version>${spring.version}</version>

</dependency>


<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>${spring.version}</version>

</dependency>


<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>${junit.version}</version>

<scope>test</scope>

</dependency>

</dependencies>

<build>

<finalName>DashboardTest</finalName>

<plugins>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>3.0</version>

<configuration>

<source>${jdk.version}</source>

<target>${jdk.version}</target>

</configuration>

</plugin>

</plugins>

</build>

</project>



4. Spring 에 대한 Enterprise 환경 설정 원칙 

  - Enterprise Spring Best Practices

  - Bean Post Processors를 등록하는 어노테이션

RequiredAnnotationBeanPostProcessor – (@Required)

CommonAnnotationBeanPostProcessor – (@PostConstruct, @PreDestroy, @Resource, etc.)

AutowiredAnnotationBeanPostProcessor – (@Autowired, @Value, @Inject, etc.)

PersistenceAnnotationBeanPostProcessor – (@PersistenceUnit, @PersistenceContext, etc.)

  - annotation-config 지시자 설정

<context:annotation-config/>

  - 애플리케이션 컴포넌트 scanning 지시자 설정

<context:component-scan base-package="com.gordondickens.enterprisespring" use-default-filters="false">

<context:exclude-filter 

   expression="org.springframework.stereotype.Controller"

   type="annotation"/>

</context:component-scan>

  - 스캐닝 원칙 

+ Component scanning should ONLY be configured in the bootstrap config file, not in every XML config file

+ Do NOT also include the <context:annotation-config/> directive, it is automatically included by component scan

+ Do NOT start scanning from “com” and/or “org”, as this will scan ALL sub packages in all of the project and jars for candidates!

+ Be as specific as possible with the packages

+ Do NOT cross application boundaries with component-scan

  + Create an applicationContext-services.xml for scanning services

  + Create an applicationContext-persistence.xml for persistence and entity beans

  + Create an applicationContext-webmvc.xml for persistence and entity beans

  + Create an applicationContext-webservice.xml for web service beans

  + Import these references into the applicationContext-bootstrap.xml to these elements


Why separate the discovery files into layer specific configuration?

  + Unit testing is easier as discovery of beans is more specific

  + Allows the project to be separated into multiple Jar/Wars

  + Lowers risk of widely scoped discovery issues, overscanning and beans being replaced by multiple scanners



<참조>

  - Eclipse Theme 사용방법

  - STS + myBatis + RESTful 설정 및 테스트

  - Eclipse Juno기반 Spring MVC with maven으로 만드는 과정

  - Maven Console 기반 webapp RESTful 서비스 만드는 과정

  - AngularJS + SpringMVC 샘플 프로젝트

  - Eclipse에서 Maven 개발환경 설정

  - Spring에서 Enterprise 환경 설정 원칙

posted by 윤영식
prev 1 next