블로그 이미지
Peter Note
Web & LLM FullStacker, Application Architecter, KnowHow Dispenser and Bike Rider

Publication

Category

Recent Post

2014. 4. 30. 17:28 Study Frameworks/Liferay Portal

Liferay 포털의 개발은 결국 기존 플러그인들의 확장 및 신규 개발 관건이다. 툴에 대한 이해를 하고 설치 사용해 보자 



Liferay IDE

  - Eclipse 플러그인으로 Eclipse Market에서 검색하여 설치가능 : "liferay ide"

  - Plugin SDK 또는 Maven과 Liferay runtime 환경과 결합하여 개발한 것을 배포 테스트 가능케 해준다  

  - Eclipse Indigo(3.7.x), Juno(4.2.x), Kepler(4.3.x) 에서 설치가능 

  - Plugin SDK와 Runtime 환경 설정 블로깅 참조 



Liferay Project 생성 

  - Eclipse의 Liferay IDE Perspective를 띄운다 

  - "New" -> "Liferay Plugin Project"를 선택한 후 하기와 같이 입력하고 "Service Builder"를 선택하고 생성한다 

    

  - 프로젝트 생성 내역

> docroot : 실제로 서비스하는 영역이다 

> view.jsp : 포틀릿으로 보여지는 웹 화면

> *xml : 포틀릿 설정 화면 


- liferay-display.xml 

  포틀릿 카테고리로 sample에 해당 포틀릿이 들어간다 

<display>

  <category name="category.sample">

    <portlet id="event-listing" />

  </category>

</display> 


  - 포틀릿 배포하기 

> 포틀릿을 생성하면 위치는 sdk/portlets 폴더밑으로 생성된다 


> Eclipse에서 Tomcat Server를 등록하여 기동한 상태라면 "And and New.." 컨텍스 메뉴를 선택하여 event-listing-porlet을 Add 하면 자동으로 Hot Deploy이 되어 반영된다 


- 포탈 화면에서 확인해 보자 

Sample 밑의 카테고리에 "Event Listing" 포틀릿이 존재하여 드래그앤드롭하여 원하는 위치에 놓을 수 있다


  - 프로젝트를 만든 후 기본 제공하는 항목을 제거하고 플러그인을 만들것이다. 기본 제공 설정내역을 삭제해 보자

1) WEB-INF/liferay-display.xml  에서 <portlet>..</portlet> 부분 제거 

2) WEB-INF/liferay-portlet.xml 에서  <portlet>..</portlet> 부분 제거 

3) WEB-INF/portlet.xml 에서  <portlet>..</portlet> 부분 제거  

4) view.jsp 파일 제거 


삭제까지 진행되었으면 이제 해당 프로젝트에 새로운 플러그인을 만들 준비가 되었다. 



Liferay Plugin 개발 

  - 프로젝트에는 여러개의 플러그인이 포함될 수 있다. 위에 만들어 놓은 "Event Listing" 프로젝트안에 여러개의 플러그인을 넣어본다 

  - Location Listing과 Event Listing 포틀릿 플러그인을 만들어 보자 

  - Location Listing Portlet 생성

1) 프로젝트명을 선택하고 context menu -> New -> "Liferay Portlet"을 선택하고 LocationListing 포틀릿을 입력


2) Next로 이동하여 포틀릿 정보를 입력


3) 카테고리 정보를 입력한다 "MobiconSoft"


  - event-listing 포틀릿을 생성한다 

> location-listing 과 같은 방식으로 생성

  MVCPortlet을 상속 받으면서 같은 java package에 속한다 


> 최종 생성된 Eclipse Project 모습

   + com.mobiconsoft.event의 클래스들은 MVCPortlet을 상속받아서 자동 생성된다 

   + liferay-display.xml, liferay-portlet.xml, portlet.xml 안에 위저드로 설정한 내용이 반영된다 


  - 프로젝트의 컨텍스트가 이미 Server에 반영되어서 위저드로 생성시에 Hot Deploy가 된다. 다음으로 "Mobiconsoft" 카테고리에 EventListing과 LocationListing이 반영되었는지 확인한다. 




다음 블로그에서는 "Service Builder" 도구를 사용하여 어떻게 Model, Persistence, Service Layer를 만들 수 있는지 살펴보도록 한다 


posted by Peter Note
2014. 4. 30. 17:20 Study Frameworks/Liferay Portal

기본 설치할 때 기본데이터를 체크하였다면 "Joe Bloggs" 가 나오고 몇가지 직관적인 메뉴를 통해서 포털의 화면을 구성할 수 있다. 가장 기본적인 것이라서 개발자 입장에서 기존에 제공하는 것의 변경 및 필요한 기능의 개발은 어떻게 하는지 Liferay Developer Guide (v6.2 기준) 를 통해 보도록 하자. 



가이드 내역 

  - Developing Application for Liferay

  - Extending and Customizing Liferay

  - 개발툴 선택하기 



Liferay를 위해 어플리케이션 개발 

  - liferay에서 어플리케이션을 연동하는 두가지 방법

    + Portlets 

    + OpenSocial gadget : 

  - Portlets

    + 자바로 쓰여진 어플리케이션 모듈로 포틀릿 컨테이너안에서 구동됨 

    + liferay 서버에 hot deploy가 가능하여 서버 재기동이 필요없음

    + 하나의 플러그인에 여러개의 포틀릿을 두어서 화면을 구성할 수 있다 

    + Liferay에서 정의한 프레임워크 : MVC 포틀릿, Alloy 포틀릿으로 구현된다 

  - OpenSocial gadget

    + 여러 언어로 개발가능

    + remote / local gadget 으로 구분

  - AlloyUI

    + v6 부터 클라이언트에서 사용

    + YUI3 이용

    + 다른 자바스크립 라이브러리 추가 가능



Liferay 기능 확장 및 커스터마이징

  - 여러 프러그인을 하나로 묶은 WAR로 배포하여 사용할 수 있다

  - Theme 사용자 정의 

    + Look and Feel에 대한 제어가 가능

    + Velocity 또는 FreeMarker와 CSS를 혼용하여 사용

    + 업무로직과 별개의 RWD(Responsive Web Desgin)이 가능

    + AlloyUI를 사용

  - Layout Templates

    + theme과 유사하지만 look and feel이 아니라 화면 페이지에 포틀릿 배치 정의에 대한 것이다 

    + Velocity를 사용하여 hot deploy 된다 

  - Hook Plugin

    + Liferay의 core function에 대하여 hooking 처리 할 수 있는 기능

    + 로그인, 세션관리, 코어 서비스들

  - Ext Plugin

    + Liferay core를 큰규모의 변경을 가하고자 할 때 사용

    + 친숙하고 정말 필요할 때만 사용. 서버 재기동 필요

 


개발 툴 선택 

  - CLI 방식 : ANT + Plugin SDK 

  - GUI 방식 : Eclipse 기반 Liferay IDE + Plugin SDK

  - Maven을 사용할 수 있다 

 

다음 단락에서는 3가지를 통해 개발하는 방법을 알아본다 

 > Developing Apps with Eclipse Liferay IDE

 > Leveraging the Plugins SDK

 > Developing Plugins Using Maven



<참조>

  - Liferay Developer Guide

  - Ext Plugin 개발 가이드

posted by Peter Note
2014. 4. 30. 12:33 Study Frameworks/Liferay Portal

Liferay CE v6.2 GA2 버전을 다운로드 받고 설치하는 과정과 Eclispse ID와 Liferay Plugins SDK도 설치해 보자. 



Liferay 다운로드 

  - JDK : v7.* 버전을 권장하고 없다면 V6.* 도 가능하다

  - 다운로드 파일 :  the liferay + tomcat bundle

  - 다운로드후 적당한 위치에 zip 압축을 푼다. (linux 기준)

// 압축을 풀었을 때 폴더명칭을 portal-6.2-ce-ga2 로 변경하였다 (사용자 정의함)

~/development/liferay_portal/portal-6.2-ce-ga2


  - data : lucene 데이터 파일

  - deploy : 플러그인 배포 .war 파일 놓는곳

  - tomact : 톰캣 위치 


// portal-ide.properties

auto.deploy.tomcat.conf.dir=/Users/xxxx/development/liferay_portal/portal-6.2-ce-ga2/tomcat-7.0.42/conf/Catalina/localhost


// portal-setup-wizard.properties 및 liferay와 mysql 연결 정보 (username : liferay, database : liferaydb)

liferay.home=/Users/xxxx/development/liferay_portal/portal-6.2-ce-ga2
admin.email.from.address=test@liferay.com
jdbc.default.driverClassName=com.mysql.jdbc.Driver
jdbc.default.username=liferay
jdbc.default.url=jdbc:mysql://localhost:3306/liferaydb?autoReconnect=true



Liferay Tomcat 설정

  - tomcat 위치

~/development/liferay_portal/portal-6.2-ce-ga2/tomcat-7.0.42

  - 디폴트 웹 어플리케이션 위치

~/development/liferay_portal/portal-6.2-ce-ga2/tomcat-7.0.42/webapps/ROOT

  - 웹 어플리케이션 위치를 옮길 경우 tomcat의 환경설정

// 변경위치를 www 로 할 경우

~/development/liferay_portal/portal-6.2-ce-ga2/www


// ~/development/liferay_portal/portal-6.2-ce-ga2/tomcat-7.0.42/conf/server.xml 수정

<Host appBase="/Users/xxxx/development/liferay_portal/www" autoDeploy="true" name="localhost" unpackWARs="true">

    <Context docBase="ROOT" path="" reloadable="true"/>

    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t &quot;%r&quot; %s %b" prefix="localhost_access_log." suffix=".txt"/>

</Host>

  - tomcat 시작

~/development/liferay_portal/portal-6.2-ce-ga2/tomcat-7.0.42/bin/startup.sh


몇분을 기다리면 브라우져가 자동 시작하여 http://localhost:8080/ 을 호출하고 초기 셋업페이지가 나온다. 

이때 mysql 연결로 바꾸자 (portal-setup-wizard.properties 참조)



Liferay Plugins SDK 설치 

  - 플러그인 소스 다운로드받자 : 나중에 eclipse ide에서 debugging 하거나 수정할 때 사용

$ cd  ~/development/liferay_portal/

$ mkdir sources && cd sources


// 플러그인 소스 다운로드 

$ git clone https://github.com/liferay/liferay-plugins.git

  - 다운로드 Liferay CE v6.2 ga2 Plugins SDK   

// ~/development/liferay_portal/ 위치에 sdk 압축을 푼다 

~/development/liferay_portal/plugins-sdk-6.2


// 최종 디렉토리 구조

  - ~/development/liferay_portal/plugins-sdk-6.2/build.<사용자명>.properties 내용 수정

app.server.tomcat.lib.global.dir = /Users/nulpulum/development/liferay_portal/portal-6.2-ce-ga2/tomcat-7.0.42/lib/ext

#app.server.tomcat.deploy.dir = /Users/nulpulum/development/liferay_portal/portal-6.2-ce-ga2/tomcat-7.0.42/webapps

app.server.tomcat.deploy.dir = /Users/nulpulum/development/liferay_portal/www

app.server.parent.dir = /Users/nulpulum/development/liferay_portal/portal-6.2-ce-ga2

app.server.tomcat.dir = /Users/nulpulum/development/liferay_portal/portal-6.2-ce-ga2/tomcat-7.0.42

app.server.type = tomcat

#app.server.tomcat.portal.dir = /Users/nulpulum/development/liferay_portal/portal-6.2-ce-ga2/tomcat-7.0.42/webapps/ROOT

app.server.tomcat.portal.dir = /Users/nulpulum/development/liferay_portal/www/ROOT



Eclipse IDE 설치

  - Eclipse Juno 버전 이상에서 "Eclipse Marketplace..." 에 들어가서 "liferay" 검색하면 "liferay IDE" 이클립스 플러그인이 나오면 Install

    


  - Liferay perpective를 띄웠을 때 화면 

    툴바에서 파란색 아이콘 3가지가 Liferay 기능이다. 

    


  - Liferay의 tomcat 서버를 등록한다 

    


  - 다음으로 Plugins SDK를 등록한다 

    



*** Liferay IDE를 통한 개발 (주의사항)

  - 기본 {TOMCAT_HOME}/webapps/ROOT 를 사용한다. 

  - 기존 ~/liferay_portal/www 에서 다시 원위치로 설정한다.

   (단, ant CLI 방식은 괜찮다)

  - 사용자가 xxx 일 경우

1) {TOMCAT_HOME}/conf.xml 에서 위치 변경

<Host appBase="/Users/xxx/development/liferay_portal/portal-6.2-ce-ga2/tomcat-7.0.42/webapps" autoDeploy="true" name="localhost" unpackWARs="true">


2) {PLUGIN_SDK}/build.<username>.properties

app.server.tomcat.lib.global.dir = /Users/xxx/development/liferay_portal/portal-6.2-ce-ga2/tomcat-7.0.42/lib/ext

app.server.tomcat.deploy.dir = /Users/xxx/development/liferay_portal/portal-6.2-ce-ga2/tomcat-7.0.42/webapps

app.server.parent.dir = /Users/xxx/development/liferay_portal/portal-6.2-ce-ga2

app.server.tomcat.dir = /Users/xxx/development/liferay_portal/portal-6.2-ce-ga2/tomcat-7.0.42

app.server.type = tomcat

app.server.tomcat.portal.dir = /Users/xxx/development/liferay_portal/portal-6.2-ce-ga2/tomcat-7.0.42/webapps/ROOT



<참조>

  - Liferay 환경 구성 - 한글

  - Liferay 초기 환경 설정

  - Liferay 개발환경 구성

  - Liferay tomcat 시작하기

  - A to Z Liferay 개발환경 설정하기 (필독)

posted by Peter Note
2014. 4. 30. 12:32 Study Frameworks/Liferay Portal

Liferay는 Enterprise Portal을 만들기 위한 솔루션이다. 커뮤니티 버전(CE)과 엔터프라이즈 버전(EE)이 존재하고, 오픈소스버전은 CE버전을 사용하면 된다. Liferay를 들어가기전에 Portal에 대한 올바른 이해와 설치 및 개발환경 설정 그리고 플로그인 개발을 보고 요즘 가장 핫한 Angular.js와 Node.js를 접목하여 클라이언트단 포틀랫(Portlet) 개발생산성 향상과 Node.js의 Socket.io를 이용한 실시간(Realtime) 구현에 대해서도 연재를 통하여 알아보자.





엔터프라이즈 포탈(EP)이란 무엇인가?

기업이 전술상 또는 전략상으로 필요로 하는 업무용 어플리케이션을 적은 비용으로 빠르게 구현하여 비즈니스 유저들에게 제공함으로써 IT는 업무 어플리케이션 구축 비용 및 시간을 절약하고 비즈니스 유저들은 자신의 업무에 필요한 어플리케이션을 신속히 제공받음으로써 사업의 기회비용을 줄일 수 있도록 지원하는 시스템. -인용-


  - 보다 자세한 이해는 엔터프라이즈 포탈이란 무엇인가? 연재글을 참조한다 

  - EP를 위하여 통합적으로 운영되어야 할 부분

    + 통합 검색 : 문서 검색 뿐만아니라 서비스/애플리케이션 단위의 검색도 가능

    + 콘텐츠 관리 : 디지털 컨텐츠/미디어의 Publish/Delivery 가능

    + 협업 기능 : SNS, 메신저, 게시판과 같은 온라인 소셜활동 가능 

    + 기타 : 다양한 어플리케이션/시스템 통합 프레임워크 제공 (SSO, KMS, BPM, ESB, SNS 등의 연동)



Liferay Portal이 제공하는 기능

  - 메일 : SMS Text Messenger

  - 블로그 : Blog, Asset Publisher (파일공유), RSS, Twitter

  - 위키 : Wiki

  - 문서 관리 : Document Library 문서공유 

  - 일정 관리 : Calendar

  - 게시판 : Message Boards (게시판) 

  - 투표 : Polls

  - 이미지 관리 : Image Gallery

  - 기사 : Journal, News

  - 공지 : Announcements, Alerts



Liferay 기술 스택

  - 서버 : Java(JDK v7 권장), Spring, Hibernate, EJB(optional)

  - 클라이언트 : AlloyUI (YUI3 + Twitter Bootstrap)



Liferay 버전 및 파일 다운로드

  - CE v6.2 GA2 (2014.4월 현재)

  - Portal 과 Plugins 파일로 구성됨

    + Portal은 메인 어플리케이션

    + Plugin은 확장 모듈이며 6가지(theme, portlet, EXT, layout template, hooks, web modules) 타입이 존재

  - GitHub : https://github.com/liferay/



다음글에서는 Liferay를 설치하고 설정하는 방법에 대해 알아보자 



참조

  - 엔터프라이즈 포탈이란 무엇인가?

  - Liferay GitHub Repository

posted by Peter Note
2014. 4. 25. 00:15 NodeJS/Concept

Node.js를 제대로 배워보고 싶다면 이렇게 시작해보자


1. Node.js 사용 영역

  - fast, real-time 어플리케이션 개발

  - scalable 확장가능성이 높은 어플리케이션 개발

  - data-driven modern web 어플리케이션 개발

  * 클라우드 환경 이해



2. 잘 못 된 코스 

  - 온라인 및 동영상 강좌들부터 보지 말기

  - 서점에서 서평들 읽고 책 고르지 말기 



3. 제대로 된 코스

  - 봐야할 리소스 

    + 초보 : The Node Beginner Book 번역본

    + Felix's NodeJS Guide 번역본

   + Node.js 소개 자료 -여름으로 가는 문

   + Mastering NodeJS (비젼미디어)


  - JavaScript 를 배운다 : JavaScript Graden 번역본


  - 설치 : Professional Node.js 1장 보고 개발환경 셋업

  - 이해 : Beginner Book 보고서 간단히 프로그램들 돌려보기 

  - 모듈 : CommonJS에 대하여 이해하기

  - Professional Node.js 3장부터 쭉 읽자. 그러면 서버단 개발에 대해서 이해할 수 있다

  - 우리 목적은 modern web application개발이니 front-end의 backbone.js 를 배운다 



4. Node.js & Backbone.js를 익혔다면 

  - http://dailyjs.com/web-app.html 여기서 다양한 어플리케이션의 모험을 해보시라

  - 더 나아가서는 템플릿을 위한 Handlebars.js 와 MongoDB를 익힌다


  * 2013.2.20 일에 작성한 블로그라서 backbone.js가 나왔다. 이제는 angular.js로 옮겨가는 분위기

    3주를 목표로 잡고 시작하시라~~~ Good Luck!



<참조>

  - 원문 : http://javascriptissexy.com/learn-node-js-completely-and-with-confidence/

posted by Peter Note
2014. 2. 4. 10:43 AngularJS/Start MEAN Stack

메뉴의 우측에 로그인을 생성하고 로그인 화면을 트위터 부트스트랩을 이용하여 만들고 로그인 처리를 한다. 


사전 준비 

  트위터 부트스트랩을 이용하여 먼저 화면 프로토 타입을 만든다. 그리고 앵귤러 Controller를 코딩한 후 서버와 통신을 위한 앵귤러 Service를 만들자. 그리고 필요에 따라 Framework 요소로 필요한 것을 만들어 간다. 하나의 업무처리를 위하여 HTML+Controller+Service 를 기억억한다.  


  - 비즈니스 HTML View 작성

  - 비즈니스 Controller 코딩

  - 비즈니스 Service 코딩



1. 로그인 메뉴 추가하기

  메뉴는 menu.html 로 별도 구성하였고, 맨 하단에 로그인이 안되었을 때와 로그인이 되었을 때를 나타내는 <li> 태그를 넣어준다. currentUser는 로그인 성공후 $rootScope에 저장되는 사용자 객체이다. <li> 태그는 ng-hide/ng-show에의해 보여졌다 안보여졌다 한다.

     ... 중략 ...

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

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

          <li data-ng-class="{ active: $state.includes('gurumember') }">

            <a ui-sref="gurumember"><i class="fa fa-users "></i> 멤버소개</a>

          </li>

        </ul>

      </li>

    </ul>


    <ul class="nav navbar-nav navbar-right">

      <li ng-hide="false">

        <a href="#" ui-sref="login">Login</a>

      </li>

      <li ng-show="false">

        <a href="#" ng-click="logout()">

          [{{ currentUser.name }}] Logout

        </a>

      </li>

    </ul>


  </div>

</div>



2. 로그인 화면 구성

  ng-hide="false" 이므로 현 상태에서는 "Login" 메뉴가 나오고, 메뉴 클릭시 ui-sref="login" 에 따라 views/login.html 화면으로 이동을 한다.  Bootsnipp에서 login 을  (http://bootsnipp.com/search?q=login) 검색하면 다양한 로그인 창 디자인이 나오므로 활용해 본다.


  - 로그인을 할 때 컨트롤러에서 처리할 메서드를 정의한다 

    submitLogin()

  - 정보 객체를 정의한다  

    login = { email: 'xxx', password: 'xxx' }

  - type 과 required 지정을 통해 필수항목 유효성 체크를 한다 

<div class="row">

  <div class="col-md-4 col-md-offset-4">

    <h3 class="text-center">LOGIN</h3>

    <form name="loginForm" ng-submit="submitLogin()">

      <div class="form-group">

        <div class="input-group">

          <span class="input-group-addon">

            <i class="fa fa-envelope-o"></i>

          </span>

          <input id="login_email" type="email" name="email" class="form-control input-lg" placeholder="Email" ng-model="login.email" required>

        </div>

      </div>


      <div class="form-group">

        <div class="input-group">

          <span class="input-group-addon">

            <i class="fa fa-asterisk"></i>

          </span>

          <input id="login_password" type="password" name="password" class="form-control input-lg" placeholder="Password" ng-model="login.password" required>

        </div>

      </div>


      <button type="submit" class="btn btn-primary btn-lg btn-block btn-shadow">Login</button>

    </form>

  </div>

</div>


  우측 상단의 "Login" 메뉴 클릭시 출력되는 최종화면이다 



3. 로그인 앵귤러 Controller 개발 

  로그인 화면에 대한 처리를 하는 컨트롤러는 LoginCtrl (app/scripts/controllers/login.js) 이다. 앵귤러의 컨트롤러는 뷰와 모델 객체를 주고 받으며 처리하는 역할만을 담당하도록 만든다. 즉, 서버와 연결하고 처리하는 것은 앵귤러 서비스를 만들고 주입(DI)을 받아 사용한다. 로그인이 성공했다고 가정하여 UI-Router의 $state를 이용하여 main 화면으로 이동하는 코드를 넣었다. 

 'use strict';


angular.module('meanstackApp')

  .controller('LoginCtrl', [

  '$scope', 

  '$state', 

  function ($scope, $state) {

   $scope.submitLogin = function() {

    console.log($scope.login);

     $state.go("main");

   }

  }]);



로그인 앵귤러 Service 개발 

  로그인 정보를 받아서 서버와 통신하는 모듈을 만들도록 한다. Node.js와 RESTful 방식으로 요청을 처리한다. 앵귤러에서는 HTTP 요청 처리를 위하여 $http와 이를 보다 추상화한 $resource를 제공한다. 하지만 클라이언트와 서버사이의 통신을 위하여 몇가지 공통모듈을 만들 필요가 있다. 


  - 서버와 주고받는 요청/응답을 추상화한 객체 

  - $resource를 추상화한 서비스   



1. 서버 응답/요청 객체 추상화 

  자바의 J2EE 스펙중 Servlet 스펙을 보면 요청/응답을 처리하는 서블릿은 HTTPServletResponse/HTTPServletRequest가 있다. 이와 유사하게 앵귤러와 Node.js에 사이에 주고 받는 JSON 객체를 추상화 한다. MS를 MEANStack의 약어로 공통모듈의 경우 앞에 붙여 사용한다. 요청객체는 앵귤러 Factory 로 개발한다.  


  - 요청객체 : MSRequest 를 통해 정형화된 형태의 JSON 객체를 생성한다 

  - 응답객체 : MSResponse 는 서버로 부터 전달되는 정형화된 형태의 JSON 객체이다 


  RESTful API를 만들기 위한 몇가지 원칙을 지키고 그외에는 변형하여 사용토록 한다. URI 정의시 몇가지 원칙만을 가지고 진행토록한다. 복잡하게 하지 말고 Stateless하면서 Resource 접근에 대해서 명사를 사용하며 동사적 의미는 HTTP POST, GET, PUT, DELETE를 사용하며, Collection의 경우는 복수를 사용한다. 즉 더 복잡한 부분이 나오면 그때 가서 확장하거나 별도 구성하여 사용하고 몇가지 원칙을 정해서 사용토록 한다 


  - prefix 로 버전을 반드시 붙인다 : api/v1

  - 3단계의 정의를 한다 : /:area/:resource/:id 

    + area : authentication, person, tech, guru 등의 resource 영역 구분

    + resource : login, mylevel(myfollowing), angularjs(nodejs, expressjs, mongodb), month(member)같은 좀 더 구체적인 구분

    + id : GET, PUT, DELETE의 경우 옵션적으로 사용

    + 그외의 데이터는 Key=Value 파라미터 값으로 넘긴다 


// MSRequest 생성 및 개발 

$ yo angular:factory ms-factory

   create app/scripts/services/ms-factory.js

   create test/spec/services/ms-factory.js


// ms-factory.js

angular.module('meanstackApp')

  .factory('msRequestFactory', function () {

  

  var createRequest = function(area, resource, id, request) {

    if (!request)

      request = {};


    return {

      "area" : area,

      "resource" : resource,

      "id" : id,

      "request" : request

    };

  };


  return {

    createRequest : createRequest

  };


});


// 공통 호출 팩토리 생성

$ yo angular:factory ms-restful-api

   create app/scripts/services/ms-restful-api.js

   create test/spec/services/ms-restful-api.js


// ms-restful-api.js 

angular.module('meanstackApp')

  .factory('msRestfulApi', ['$resource', function ($resource) {

  var prefixUrl = '/api/v1';


  return $resource(

      prefixUrl + '/:area/:resource/:id', 

     {

       area : "@area",

       resource : "@resource",

       id : "@id"

     },

     {

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

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

      'update': {method:'PUT'},

      'delete': {method:'DELETE'},

      'login':  {method:'POST'}

    });

  }]);


  $resource의 파라미터는 첫번째 URI 두번째 URI의 구분자에 맵핑되는 JSON, 세번째는 사용자 정의 메소드이다. 



2. 로그인 Service 개발 

  컨트롤에서 로그인 관련 서버호출을 위하여 MSRequest 팩토리와 RESTful하게 호출할 수 있는 팩토리를 만들었다. 이를 활용하는 앵귤러 Service 코딩해 보자. 


  - 사용자 로그인을 처리하는 서비스를 만든다.

  - 서비스는 View에서 Controller를 통하여 로그인 정보(params)를 받아서 Service로 넘긴다.

  - 성공적으로 완료되면 Controller에서 넘겨준 펑션(successCallback)을 수행한다. 


Service는 순수하게 서버와의 요청 처리만을 담당하고 Controller는 HTML View에서 넘어온 Model 값의 핸들링과 이후 Service에서 성공을 하면 실행할 successCallback을 갖는다. successCallback은 UI Routing 또는 View의 변경을 수행한다. Service와 Controller 서로의 역할을 정확히 나눔으로써 유지보수성을 높이도록 하였다. 


// 로그인 관련 서비스를 만든다 

$ yo angular:service session-service

   create app/scripts/services/session-service.js

   create test/spec/services/session-service.js


// session-service.js

angular.module('meanstackApp')

  .service('SessionService', [

  'msRequestFactory', 

  'msRestfulApi', 

  '$log',

  function SessionService(msRequestFactory, msRestfulApi, $log) {

    

    this.login = function(paramssuccessCallback) {

    // 1) create request

    var request = msRequestFactory.createRequest('authentication', 'login', '');

   

    // 2) set params

    request.params = {

    email: params.email,

    password: params.password

    }


    // 3) call ajax 

    msRestfulApi.login(request, 

    function(response) {

    // success

    successCallback(response);

    }, 

    function(error){

    // fail

    $log.error('Server Exception is ', error);

    })

  };


  }]);


  사용자가 로그인을 한후 사용자 정보를 담는 SessionInfo 객체를 관리하는 서비스를 만들어 보자. 

  - 사용자 정보 저장소로 HTML5의 localStorage를 우선 사용한다 

  - 현재 사용자 정보를 $rootScope.currentUser에 담아서 애플리케이션 전체 영역에서 사용토록 한다 

// 세션 정보 생성 

$ yo angular:service session-info

   create app/scripts/services/session-info.js

   create test/spec/services/session-info.js


// session-info.js

angular.module('meanstackApp')

  .service('SessionInfo', ['$rootScope', function SessionInfo($rootScope) {

    this.localStorageKey = "__SESSION_INFO";

    try {

      $rootScope.currentUser = JSON.parse(localStorage.getItem(this.localStorageKey) || "{}");

    } catch(e) {

      $rootScope.currentUser = {};

    }


    this.getCurrentUser = function() {

      return $rootScope.currentUser;

    }


    this.isUserSignedIn = function() {

      if(this.getCurrentUser() && this.getCurrentUser().id) {

        return true;

      } else {

        return false;

      }

    };


    this.setUserInfo = function(info) {

      angular.extend($rootScope.currentUser, info);

      localStorage.setItem(this.localStorageKey, JSON.stringify($rootScope.currentUser));

    };


    this.reset = function() {

      $rootScope.currentUser = {};

      localStorage.setItem(this.localStorageKey, JSON.stringify($rootScope.currentUser));

    };

  }]);



  SessionService에서 로그인 요청이 성공하면 SessionInfo를 호출하는 코드를 넣도록 한다. 

// SessionInfo를 사용하는 SessionService 수정 

angular.module('meanstackApp')

  .service('SessionService', [

   'msRequestFactory', 

   'msRestfulApi', 

   'SessionInfo',

   '$log',

   function SessionService(msRequestFactory, msRestfulApi, SessionInfo, $log) {

    

    this.login = function(params, successCallback) {

     // 1) create request

     var request = msRequestFactory.createRequest('authentication', 'login', '');

    

     // 2) set params

     request.params = {

     email: params.email,

     password: params.password

     }


     // 3) call ajax 

     msRestfulApi.login(request, 

     function(response) {

       // success

        SessionInfo.reset();

                   SessionInfo.setUserInfo(response);

        successCallback(response);

     }, 

     function(error){

       // fail

                  alert('Login Failed');

       $log.error('Server Exception is ', error);

     })

   };


  }]);



3. 로그인 Controller 개발

  이제 앵귤러 Service가 만들어 졌으니 Service를 사용하는 Controller를 코딩해 보자. 로그인 Service 가 성공하면 메인화면으로 이동한다.  

// login.js 의 기존 코드 

angular.module('meanstackApp')

  .controller('LoginCtrl', [

  '$scope', 

  '$state', 

  function ($scope, $state) {

   $scope.submitLogin = function() {

    console.log($scope.login);

     $state.go("main");

   }

  }]);


// SessionService를 사용하는 수정 코드 

angular.module('meanstackApp')

  .controller('LoginCtrl', [

  '$scope', 

  '$state', 

  'SessionService',

  'SessionInfo',

  '$log',

  function ($scope, $state, SessionService, SessionInfo, $log) {


   $scope.submitLogin = function() {

    SessionService.login($scope.login, changeState);

   }


   function changeState(response){

    alert(SessionInfo.getCurrentUser().name + '님 반갑습니다 :)');

        $state.go("main");

   }

  }]);


최종적으로 Chrome 개발자 도구의 console에 아무런 에러가 없으면 모든 서비스와 컨트롤러가 앵귤러 초기화가 잘 되었다는 신호이다. 로그인을 해보면 아마도 404에러가 날 것이다. 아직 서버가 준비되지 않았기 때문인데, 이는 다음장에서 테스트 코드를 작성해서 확인해 보록한다. 다음과 같인 나오면 성공이다. 



4. 로그인 html 수정

  SessionInfo에는 사용자가 로그인 상태인지 체크하는 메소드가 존재한다. 이를 menu.html 에 적용하자 

<ul class="nav navbar-nav navbar-right">

<li ng-hide="SessionInfo.isUserSignedIn()">

<a href="#" ui-sref="login">Login</a>

</li>

<li ng-show="SessionInfo.isUserSignedIn()">

<a href="#" ng-click="logout()">

[{{ currentUser.name }}] Logout

</a>

</li>

</ul>


위의 코드는 해당 브랜치에 존재한다. 

https://github.com/MEAN-STACK/MEANStack-Bookwork/tree/angular_step03_login-ctrl-service

posted by Peter Note
2014. 1. 30. 11:15 AngularJS/Start MEAN Stack

UI-Router를 이용한 화면 라우팅 설정을 app/scripts/app.js 메인 애플리케이션 파일에 설정하였다. 설정 내용중 각 라우팅되는 HTML View의 제어를 담당하는 Controller를 yeoman을 사용해서 생성해 보자 



HTML과 Controller 

  앵귤러는 MVW 프레임워크라고 하는데 여기서 M은 model 이고 V는 View, W은 Whatever를 의미하여 무엇이든지 올 수 있다는 의미이다. Whatever에는 싱글톤패턴의 서비스, 팩토리 또는 필터, 디렉티브(Directive)등이 올 수 있다. 


  - Model : Controller의 $scope를 통한 자동 two-way binding 을 한다 

  - View : HTML에 $scope의 모델들을 설정하거나 앵귤러의 디렉티브를 이용하여 직관적인 설정한다  


  앵귤러의 데이터 모델은 바로 Scope에 의해서 연결이 되며, app.js 가 최초 수행 되면 $rootScope가 생성되고 그 하위로 $scope가 생성된다. 이들은 계층적으로 연결되어 상위의 scope 데이터에 접근할 수 있다. 만일 Chrome 브라우져를 사용한다면 scope 객체들의 계층구조를 보기 위하여 별도의 크롬도구를 설치해야 한다. 


 1) AngularJS Batarang: https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk 설치 

 2) 크롬 개발자도구 : 윈도우(Ctrl+Shift+i), 맥(Command+Option+i) 연다 

 3) 맨 우측의 AngularJS를 선택하고 Enable 옵션을 체크한다 

 4) < Scope (003) 에서 003은 앵귤러가 Scope를 생성하고 부여한 아이디이고 클릭하면 우측에 Scope에 저장된 정보를 볼 수 있다.



1. 화면별 컨트롤러 생성하기 

  Yeoman에서 앵귤러 프레임워크의 기능을 자동 생성해 주는 것은 yo 명령이다. 우리는 meanstack 애플케이션 생성을 위하여 "generator-angular"를 설치하였는데 yo 뒤의 명령은 관례에 따라 "yo angular:<기능> <명칭>"으로 사용할 수 있다. 


  - yo의 generator가 만일 generator-mean 이고 geneartor-mean안에 <기능>으로 controller가 있다면 

  - yo mean:controller sample 을 수행하면 보통은 컨트롤러 코드와 컨트롤러 테스트 코드가 자동 생성된다

  -즉 "yo <generator- 두의 명칭>:<genertor가 제공하는 기능명칭>  <사용자정의 명칭>" 으로 명령은 구성된다 

  - <사용자정의 명칭> 에서 이름을 지정할 때는 "user-biz" 라고 명칭을 주면 index.html에는 "user-biz.js" 파일이 추가 되고, 기능의 명칭은 "UserBiz<기능약어>" 식으로 명칭이 만들어 진다.


  generator-angular 기능 목록은 홈페이지를 참조한다 (https://github.com/yeoman/generator-angular) 컨트롤러 생성시 테스트 코드 파일이 자동으로 생성된다 

// yo angular:controller <명칭> 으로 각 메뉴의 컨트롤러를 생성한다.

$ yo angular:controller my-level

   create app/scripts/controllers/my-level.js

   create test/spec/controllers/my-level.js


$ yo angular:controller my-qna

   create app/scripts/controllers/my-qna.js

   create test/spec/controllers/my-qna.js


$ yo angular:controller my-following

   create app/scripts/controllers/my-following.js

   create test/spec/controllers/my-following.js


$ yo angular:controller tech-area

   create app/scripts/controllers/tech-area.js

   create test/spec/controllers/tech-area.js


$ yo angular:controller ranking

   create app/scripts/controllers/ranking.js

   create test/spec/controllers/ranking.js


$ yo angular:controller member

   create app/scripts/controllers/member.js

   create test/spec/controllers/member.js


$ yo angular:controller login

   create app/scripts/controllers/login.js

   create test/spec/controllers/login.js


// index.html에 자동으로 추가된 것을 볼 수 있다.

<!-- build:js({.tmp,app}) scripts/scripts.js -->

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

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

<script src="scripts/controllers/my-level.js"></script>

<script src="scripts/controllers/my-qna.js"></script>

<script src="scripts/controllers/my-following.js"></script>

<script src="scripts/controllers/tech-area.js"></script>

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

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

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

<!-- endbuild -->


  컨트롤러를 생성했는데 - 또는 . 를 사용하면 앞뒤의 문자들의 첫글자가 대문자로 변형되어 설정이 되고 뒤에 "Ctrl" 이 자동으로 붙는다 

// app/scripts/controllers/my-level.js 소스

'use strict';


angular.module('meanstackApp')

  .controller('MyLevelCtrl', function ($scope) {

    $scope.awesomeThings = [

      'HTML5 Boilerplate',

      'AngularJS',

      'Karma'

    ];

  });

  

  이제 app/scripts/app.js에 컨트롤러를 설정해 보자 

$stateProvider

      .state('main', {

        url: '/',

        templateUrl: 'views/main.html',

        controller: 'MainCtrl'

      })

      .state('login', {

        url: '/login',

        templateUrl: 'views/login.html',

        controller: 'LoginCtrl'

      })

      .state('mylevel', {

        url: '/mylevel',

        templateUrl: 'views/mylevel.html',

        controller: 'MyLevelCtrl'

      })

      .state('myqna', {

        url: '/myqna',

        templateUrl: 'views/myqna.html',

        controller: 'MyQnaCtrl'

      })

      .state('myfollowing', {

        url: '/myfollowing/{userId:[0-9]{1,4}}',

        templateUrl: 'views/myfollowing.html',

        controller: 'MyFollowingCtrl'

      })

      .state('techarea', {

        url: '/techarea/{techId:[0-9]{1,4}}',

        templateUrl: 'views/techarea.html',

        controller: 'TechAreaCtrl'

      })

      .state('gururanking', {

        url: '/gururanking',

        templateUrl: 'views/gururanking.html',

        controller: 'RankingCtrl'

      })

      .state('gurumember', {

        url: '/gurumember',

        templateUrl: 'views/gurumember.html',

        controller: 'MemberCtrl'

      });


  my-following이나 tech-area를 보면 아이디를 설정하였고 myfollowing.html에서 $stateParam을 사용하기위해 meanstackApp 모듈의 run() 에서 $rootScope에 $stateParams를 저장한다. 그리고 페이지가 전환 되기 시작할 때와 전환하여 로딩 완료되었을 때의 이벤트를 처리하기 위한 Listener를 등록한다. 

// myfollowing.html 

<div class="row">

  <div class="col-md-12">

    <h1>myfollowing.html</h1>

     User ID: {{$stateParams.userId}}

  </div>

</div>


// app/scripts/app.js

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

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

    $rootScope.$state = $state;

    $rootScope.$stateParams = $stateParams;

  }]);


  - html에서 $stateParams 를 찾기위하여 자신의 $scope.$stateParams를 찾는다 없으면 다시 상위의 $rootScope.$stateParams을 찾게 된다. 따라서 클라이언트 애플리케이션 전역에서 사용할 내용들이 있다면 $rootScope에 저장한다. 

  - myfollowing 메뉴를 선택하면 Scope (009) 의 아이디값이 변경된다. 메뉴를 누를 때마다 Scope의 아이디가 변경되는데 이는 클라이언트 화면이 변경될 때 이전 Scope는 destroy되고 새로운 Scope가 생성되기 때문이다. 즉, 앵귤러가 화면 라우팅이되고 해당 화면의 컨트롤러에 신규 $scope 을 생성하여 DI (Dependency Injection)해준다 



2. 컨트롤러 코드 변경하기 

  애플리케이션 배포시에는 "grunt build"를 수행하는데 이때 .js에 대한 단축화(minification)이 수행되어 변수명을 알 수 없는 코드로 바꾸어준다. 앵귤러에서는 $로 시작하는 명칭이 변경이 되면 안되기 때문에 yo로 생성된 코드를 그대로 사용할 수 없다.  '$scope' 문자를 지정해 주면 function($scope) {} 가 최소화 되어 function($ab){} 로 바뀌어도 DI시에 '$scope'로 지정된 $scope 객체가 주입된다.

 angular.module('meanstackApp')

 .controller('MyFollowingCtrl', [

  '$scope',

  function ($scope) {

    ... 중략 ...

  }]);

  

  다른 컨트롤러도 해당 방식으로 전부 수정한다. 소스는 anuglar_setp02_insert-ctrl-menu 브랜치를 체크아웃한다 

https://github.com/MEAN-STACK/MEANStack-Bookwork/tree/anuglar_setp02_insert-ctrl-menu

posted by Peter Note
2014. 1. 29. 23:56 AngularJS/Start MEAN Stack

CSS Framework과 클라이언트 UI Routing을 위한 라이브러리 설치가 끝났으니 이제 본격적으로 메인화면을 만들어 보도록 하죠. 메인 화면의 디자인은 CSR에서 내용을 참조하여 Twitter Bootstrap의 반응형 웹 디자인 메뉴로 구성한다. 



전체 구조 이해하기 

  우선 메뉴를 만들기 전에 index.html의 전체 구조를 이해하자. SPA 방식의 싱글페이지 애플리케이션 개발이란 index.html의 일부 DOM을 변경함으로써 화면을 전화하여 보여주는 것이다. 초기 index.html 레이아웃은 단순화 하여 사용토록 한다. 


  - 메뉴 html을 별도 파일로 분리 

  - 메뉴 html에 링크 설정하기 

  - 메뉴에 링크된 화면의 html 파일 생성 


// index.html 기존 설정

<body ng-app="meanstackApp">


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

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


</body>



1. 메뉴관련 html 파일 분리

  우선 메뉴관련 파일을 분리해서 시작한다. app/views/menu.html 파일을 생성하고 index.html 파일에 include 한다.

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

<!-- menu -->

<div ng-include="' /views/menu.html '"></div>

  menu.html 안에 이제 부트스트랩의 반응형 웹 디자인 메뉴를 만들어 보자. 반응형 웹 디자인 메뉴란 해상도에 따라 메뉴의 형태가 자동으로 변하는 것을 말한다. 주로 데스크톱과 모바일기기의 해상도에 맞게 메뉴 형태가 변한다. 처음에 부트스트랩을 직접 수작업을 해보는 것이 도움이 되지만 여기서는 부트스트랩용 저작도구인  Bootply(http://bootply.com/)를 사용해서 만들어 본다.  


  - Bootply의 "Drag-and-Drop" 가운데 메뉴를 선택한다

  - Bootstrap 3.0 에서 "Basic starter"를 선택한다 

  - html에서 메뉴에 대한 부분만 취한다 



 menu.html 에서 <div class="navbar navbar-inverse navbar-fixed-top"> 내용은 index.html의 ng-include가 있는 <div>에 class설정한다. 

// index.html

<div class="navbar navbar-inverse navbar-fixed-top" ng-include="' /views/menu.html '"></div>


// menu.html 

  <div class="container">

    <div class="navbar-header">

      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">

        <span class="icon-bar"></span>

        <span class="icon-bar"></span>

        <span class="icon-bar"></span>

      </button>

      <a class="navbar-brand" href="#">Brand</a>

    </div>

    <div class="collapse navbar-collapse">

      <ul class="nav navbar-nav">

        <li class="active"><a href="#">Home</a></li>

        <li><a href="#about">About</a></li>

        <li><a href="#contact">Contact</a></li>

      </ul>

    </div><!--/.nav-collapse -->

  </div>

  

  "grunt serve"로 수행을 해보면 입력한 메뉴 구조를 볼 수 있다. 화면의 사이즈를 줄이면 메뉴형태가 변화는 것 또한 볼 수 있다. 

  - 데스크톱 해상도

  - 모바일 해상도


2. 메뉴의 링크 설정하기 

  menu.html 파일안에 링크에 대해 설정한다. 요구사항 정의를 보면 MEANStack을 위한 Q&A 서비스를 만드는 것이다. 하기와 같은 대분류 메뉴와 소분류 메뉴를 링크설정해 본다 

  

  - 나의 Q&A : 나의 질문 답변, 내가 팔로잉한 사람

  - Tech 글 : MEAN Stack 관련 메뉴

  - 명예의 전당 : 이번달 명예의 전당(인기글), 멤버소개 


부트스트랩은 기본적인 사항은 홈페이지를 통해 몇시간 정도 공부하거나 오픈 튜토리얼 사이트에서 익히고 시작하는 것이 좋다. 그리고 응용에 관련된 부분은 Bootsnipp (http://bootsnipp.com/) 에서 유용한 형태를 취하여 사용해 본다. 여기서는 부트스트랩의 기본적인 Navigation bar 구조를 참조한다.

참조: http://getbootstrap.com/components/#navbar


// menu.html 변경 내용

<div class="container">

  <div class="navbar-header">

    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">

      <span class="icon-bar"></span>

      <span class="icon-bar"></span>

      <span class="icon-bar"></span>

    </button>

    <a class="navbar-brand" href="#"> MEANStack.net</a>

  </div>

  <div class="collapse navbar-collapse">

    <ul class="nav navbar-nav">

      

      <li class="dropdown">

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

        <ul class="dropdown-menu">

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

          <li>

            <a href="#"> My Level: 평민</a> 

          </li>

          <li>

            <a href="#"> 질문: 3, 답변: 1</a>

          </li>

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

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

          <li>

            <a href="#"> 윤영식</a>

          </li>

        </ul>

      </li>


      <li class="dropdown">

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

        <ul class="dropdown-menu">

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

          <li>

            <a href="#"> Angular.js</a>

          </li>


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

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

          <li>

            <a href="#"> Node.js</a>

          </li>

          <li>

            <a href="#"> Express.js</a>

          </li>


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

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

          <li>

            <a href="#"> MongoDB</a>

          </li>


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

          <li class="dropdown-header">Eco Tools</li>

          <li>

            <a href="#"> Yeoman</a>

          </li>

        </ul>

      </li>


      <li class="dropdown">

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

        <ul class="dropdown-menu">

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

          <li>

            <a href="#"> 명예의 전당</a>

          </li>

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

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

          <li>

            <a href="#"> 멤버소개</a>

          </li>

        </ul>

      </li>


    </ul>

  </div><!--/.nav-collapse -->

</div>


 다시 "grunt serve"를 통하여 메뉴가 정상적으로 만들어 졌는지 확인한다 



3. 메뉴 링크에 라우팅 설정하기 

  메뉴구조를 만들었으니 이제 UI-Router(https://github.com/angular-ui/ui-router) 방식에 따라 라우팅을 설정해 보자. 앵귤러 라우팅 순서는 먼저 서버에서 HTML파일을 불러온 후 앵귤러에서 HTML파일을 파싱하여 앵귤러 코드를 앵귤러 컨텍스트에 포함시키고 DOM을 변경한다. 

  

  - 서버에 HTML 파일 요청하기 

  - HTML 파일 파싱하여 앵귤러 컨텍스트에 포함시키기

  - DOM을 변경하여 화면에 표현하기 


먼저 "My Level: 평민" 메뉴를 대상으로 설정을 하면 나머지는 동일 과정을 반복하게 된다. 

1) mylevel.html 파일을 app/views 폴더 밑에 생성한다 

<div class="row">

  <div class="col-md-12">

    <h1>mylevel.html</h1>

  </div>

</div>


2) <li> 태그에 현재 선택이 되면 class="active" 부트스트랩 클래스를 추가하기 위하여 앵귤러의 ng-class를 사용한다. 

  - 라우킹 명칭을 "mylevel" 로 정한다 

  - (data-)ng-class 안에 표현식을 넣어서 현재 라우팅 명칭이 mylevel (true)이면 active 클래스를 적용하고 false이면 적용하지 않는다

  - ui-sref 는 ui-router에서 사용하는 라우팅 변경 링크 속성으로 href를 대체한다 

// 기존 설정

<li>

    <a href="#"> My Level: 평민</a> 

</li>


// 변경 설정 

<li data-ng-class="{ active: $state.includes('mylevel') }">

   <a ui-sref="mylevel"> My Level: 평민</a> 

</li>

  

3) 이제 HTML 파일의 위치와 기타 정보를 설정한다

  - app/scripts/app.js 가 meanstack 서비스의 메인 애플리케이션 파일이 된다. 앵귤러가 최초 수행이 되면 애플리케이션에서 사용하는 $rootScope를 만들게 되는데 app.js 메인 애플리케이션 레벨에서 사용하는 Global Scope 이다. 단 브라우져를 재로딩하면 초기화 된다. 

  - ui-router 설정은 홈페이지(https://github.com/angular-ui/ui-router)를 참조한다.

'use strict';


angular.module('meanstackApp', [

  'ngCookies',

  'ngResource',

  'ngSanitize',

  'ngRoute',

  'ui.bootstrap',

  'ui.router'

])

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


    $urlRouterProvider.otherwise("/");


    $stateProvider

      .state('main', {

        url: '/',

        templateUrl: 'views/main.html',

        controller: ''

      })

      .state('mylevel', {

        url: '/mylevel',

        templateUrl: 'views/mylevel.html',

        controller: ''

      });

  }])


  - $urlRouterProvider.otherwise('/') 는 설정되지 않은 uri 요청이 들어올 경우를 처리한다. 여기서는 main 으로 간다 

  - main.html은 meanstack을 만들 때 생성된 html이다 

  - 메뉴에서 "나의 레벨"을 클릭하면 "mylevel" state의 url로 변경되고 templateUrl에 설정한 html을 서버에 요청한다 

  - HTML을 파싱하여 앵귤러 컨텍스트에 포함시키고 앵귤러 코드에 대한 처리는 controller에서 담당하나 아직은 설정하지 않았다

  - mylevel.html의 파싱된 DOM은 index.html의 "<div ui-view class="container"></div>" ui-view 하위 <div>에 자동 주입된다. 


설정이 완료되었으면 "나의 레벨" 메뉴를 클릭해 본다 


ui-view 속성이 있는 <div>에 mylevel.html 파일의 내역의 표현이 약간 위로 올라갔다. index.html과 main.css를 수정한다 

// app/index.html 기존 

<div class="navbar navbar-inverse navbar-fixed-top" ng-include="' /views/menu.html '"></div>


// app/index.html 수정

<div class="navbar navbar-default navbar-static" ng-include="' /views/menu.html '"></div>


// app/views/main.html 수정 

<div class="row">

  <div class="col-md-12">

    <h1>main.html</h1>

    <h2> Welcome to MEANStack.net</h2>

  </div>

</div>


// app/sytles/main.css 의 모든 내역을 삭제함 


  최종 결과 화면




4. 메뉴에 적절한 아이콘 설정하기 

 다른 메뉴들도 라우팅 명칭을 정하고 menu.html과 app.js에 라우팅 설정을 하고 각 메뉴의 기본 파일을 app/views/ 폴더 밑에 생성하여 연결한다. 그리고 메뉴에 FontAwesome의 아이콘을 설정한다. 


  - FontAwesome 사이트에서 원하는 아이콘을 선택한다 : http://fortawesome.github.io/Font-Awesome/icons/

  - 아이콘 태그를 넣는다 : 예) <i class="fa fa-check-square"></i> fa-check-square

// app/views/menu.html

<div class="container">


  <div class="navbar-header">

    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">

      <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span>

    </button>

    <a ui-sref="main" class="navbar-brand"><i class="fa fa-th-large"></i> MEANStack.net</a> 

  </div>

  

  <div class="collapse navbar-collapse">

    <ul class="nav navbar-nav">

      <li class="dropdown">

        <a href="" class="dropdown-toggle" data-toggle="dropdown">

          <i class="fa fa-spinner fa-spin"></i> My Area <b class="caret"></b>

        </a>

        <ul class="dropdown-menu">

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

          <li data-ng-class="{ active: $state.includes('mylevel') }">

            <a ui-sref="mylevel"><i class="fa fa-eye"></i> My Level: 평민</a> 

          </li>

          <li data-ng-class="{ active: $state.includes('myqna') }">

            <a ui-sref="myqna"><i class="fa fa-comments"></i> 질문: 3, 답변: 1</a>

          </li>

          

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

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

          <li data-ng-class="{ active: $state.includes('myfollowing') && $stateParams.userId == '1' }">

            <a ui-sref="myfollowing({userId:'1'})"><i class="fa fa-thumb-tack"></i> 박유진</a>

          </li>

          <li data-ng-class="{ active: $state.includes('myfollowing') && $stateParams.userId == '2' }">

            <a ui-sref="myfollowing({userId:'2'})"><i class="fa fa-thumb-tack"></i> 이규원</a>

          </li>

          <li data-ng-class="{ active: $state.includes('myfollowing') && $stateParams.userId == '3' }">

            <a ui-sref="myfollowing({userId:'3'})"><i class="fa fa-thumb-tack"></i> 윤영식</a>

          </li>


        </ul>

      </li>


      <li class="dropdown">

        <a href="" class="dropdown-toggle" data-toggle="dropdown">

        <i class="fa fa-pencil-square-o"></i> Tech Area <b class="caret"></b></a>

        <ul class="dropdown-menu">

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

          <li data-ng-class="{ active: $state.includes('techarea') && $stateParams.techId == '1' }">

            <a ui-sref="techarea({techId:'1'})"><i class="fa fa-file-text-o"></i> Angular.js</a>

          </li>


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

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

          <li data-ng-class="{ active: $state.includes('techarea') && $stateParams.techId == '2' }">

            <a ui-sref="techarea({techId:'2'})"><i class="fa fa-file-text-o"></i> Node.js</a>

          </li>

          <li data-ng-class="{ active: $state.includes('techarea') && $stateParams.techId == '3' }">

            <a ui-sref="techarea({techId:'3'})"><i class="fa fa-file-text-o"></i> Express.js</a>

          </li>


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

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

          <li data-ng-class="{ active: $state.includes('techarea') && $stateParams.techId == '4' }">

            <a ui-sref="techarea({techId:'4'})"><i class="fa fa-file-text-o"></i> MongoDB</a>

          </li>


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

          <li class="dropdown-header">Eco Tools</li>

          <li data-ng-class="{ active: $state.includes('techarea') && $stateParams.techId == '5' }">

            <a ui-sref="techarea({techId:'5'})"><i class="fa fa-file-text-o"></i> Yeoman</a>

          </li>

        </ul>

      </li>


      <li class="dropdown">

        <a href="" class="dropdown-toggle" data-toggle="dropdown">

        <i class="fa fa-smile-o"></i> GuruGuru <b class="caret"></b></a>

        <ul class="dropdown-menu">

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

          <li data-ng-class="{ active: $state.includes('gururanking') }">

            <a ui-sref="gururanking"><i class="fa fa-sitemap"></i> 명예의 전당</a>

          </li>

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

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

          <li data-ng-class="{ active: $state.includes('gurumember') }">

            <a ui-sref="gurumember"><i class="fa fa-users "></i> 멤버소개</a>

          </li>

        </ul>

      </li>

    </ul>

  </div>

</div> 



// app/scripts/app.js 

angular.module('meanstackApp', [

  'ngCookies',

  'ngResource',

  'ngSanitize',

  'ngRoute',

  'ui.bootstrap',

  'ui.router'

])

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


    $urlRouterProvider.otherwise("/");


    $stateProvider

      .state('main', {

        url: '/',

        templateUrl: 'views/main.html',

        controller: ''

      })

      .state('login', {

        url: '/login',

        templateUrl: 'views/login.html',

        controller: ''

      })

      .state('mylevel', {

        url: '/mylevel',

        templateUrl: 'views/mylevel.html',

        controller: ''

      })

      .state('myqna', {

        url: '/myqna',

        templateUrl: 'views/myqna.html',

        controller: ''

      })

      .state('myfollowing', {

        url: '/myfollowing/{userId:[0-9]{1,4}}',

        templateUrl: 'views/myfollowing.html',

        controller: ''

      })

      .state('techarea', {

        url: '/techarea/{techId:[0-9]{1,4}}',

        templateUrl: 'views/techarea.html',

        controller: ''

      })

      .state('gururanking', {

        url: '/gururanking',

        templateUrl: 'views/gururanking.html',

        controller: ''

      })

      .state('gurumember', {

        url: '/gurumember',

        templateUrl: 'views/gurumember.html',

        controller: ''

      });


  }])


  - ui-router에서 myfollowing({userId:'1'}) 는 파라미터를 넘기는 방식이다 형식) ui-sref='stateName({param:value, param:value})

  - ui-router API : https://github.com/angular-ui/ui-router/wiki/Quick-Reference


현재까지 진행된 내역을 GitHub에 올려 놓았다. 다음은 router에 앵귤러 controller를 만들고 router에 설정해 보자 

https://github.com/MEAN-STACK/MEANStack-Bookwork/tree/angular_step01_making-index


posted by Peter Note
2014. 1. 29. 07:48 AngularJS/Start MEAN Stack

Git, Node.js, Yeoman등 기본적인 개발환경을 설치하였다면 소프트웨어 요구사항 정의서 (Software Requirements Specification, SRS) 에서 정의한 화면을 CSS Framework을 사용하여 만들어 보자. 여기서는 가장 많이 사용하고 있는 Twitter Bootstrap을 사용토록 한다. 



사전준비

  이전 글에서 yeoman의 yo 명령을 통하여 angular 프로젝트를 "meanstack"이름으로 생성하였다. meanstack 하위에 app 폴더가 앵귤러 기반의 SPA (Single Page Application) 프론트앤드 애플리케이션의 ROOT 폴더가 된다. 


  - 반응형 웹 디자인 기반의 메뉴구조를 구성한다 

  - 메뉴를 클릭하면 해당하는 html 파일로 라우팅한다 

  - 메뉴에 아이콘을 추가한다 




1. Twitter Bootstrap 설치

 Twitter Bootstrap은 meanstack을 생성하면서 app/index.html에 자동 추가된다. 

  1) <!-- build:css ... --> : 주석은 제거하면 안된다. grunt build 시에 css 압축할 때 사용하는 주석이다. (build:js 도 동일) 

  2) <!-- bower:css .. --> : 주석은 제거하면 안된다. grunt build 시에 자동으로 bower install 한 내역을 추가한다. (bower:css 도 동일) 

// index.html

    <!-- build:css styles/vendor.css -->

    <!-- bower:css -->

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

    <!-- endbower -->

    <!-- endbuild -->


   .. 중략 ..


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

    <!-- bower:js -->

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

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

    <script src="bower_components/bootstrap/dist/js/bootstrap.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>

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

    <!-- endbower -->

    <!-- endbuild -->

  * "yo angular meanstack" 명령으로 twitter bootstrap이 자동 설치가 되면 twitter bootstrap이 사용하는 fonts 폴더를 app/fonts로 자동 복사해 준다. 이것은 grunt build시에 모든 css가 app/styles/*.css로 압축되고 bootstrap의 css가 사용하는 font는 ../fonts를 참조하기 때문이다. 따라서 배포를 위해서는 app/fonts 폴더가 존재 해야한다.




2. Angular UI-Bootstrap 설치

  Twitter Bootstrap을 html에 적용하려면 class 정보를 입력해야 한다. 이를 좀 더 직관적인 html tag 형식으로 쓸 수 있도록 AngularUI 팀에서 Directives를 만들었다. 예로 tab을 적용하가 위하여 <tab> 태그를 html에 사용하면 angular-bootstrap 모듈이 <tab> 를 class="tab" 형태로 바꿔서 DOM에 적용하는 것이다. html을 좀 더 직관적으로 작성할 수 있다는 장점이 있다. 

$ bower install angular-bootstrap --save

angular-bootstrap#0.10.0 app/bower_components/angular-bootstrap

└── angular#1.2.10


  bower를 통해 angular-bootstrap을 설치 후 "grunt build" 또는 "grunt serve" 를 실행하면 index.html에 필요 파일이 자동 추가된다. 자동 추가되는 파일의 정보는 bower_components/angular-bootstrap/bower.json 파일에 지정되어 있다.

// index.html 파일 

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

    <!-- bower:js -->

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

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

    <script src="bower_components/bootstrap/dist/js/bootstrap.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>

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

    <script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>

    <!-- endbower -->

    <!-- endbuild -->


  angular-bootstrap은 별도의 앵귤러 모듈이므로 애플리케이션에서 사용하려면 모듈 의존성을 설정해야 한다. meanstack 서비스의 메인 소스는 app/scripts/app.js 이다. 여기에 모듈 의존성을 설정한다. 

  - 형식 : angular.module('<ApplicationName>', [<의존성 모듈 열거>]);

// app/bower_components/angular-bootstrap/ui-bootstrap-tpl.js 파일 상단

angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.transition","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.bindHtml","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdownToggle","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);



// app/scripts/app.js 안에 추가 

'use strict';

angular.module('meanstackApp', [

  'ngCookies',

  'ngResource',

  'ngSanitize',

  'ngRoute',

  'ui.bootstrap'

])

  .config(function ($routeProvider) {

    .. 중략 ..

  });



3. Angular UI-Router 설치 

클라이언트 화면 전환을 위한 라우팅 기능은 앵귤러에서 제공하는 기본적인 라우터를 사용하지 않고 AngularUI팀에서 역시 제공하는 ui-router (https://github.com/angular-ui/ui-router)를 사용한다. ui-router는 한 화면에서 멀티 view 설정으로 원하는 부분들의 DOM을 변경처리할 수 있다. bower를 통해 ui-router v0.2.7 버전을 설치한다. ui-router v0.2.8 버전설치에 오류가 존재하기 때문이다. 또한 설치전에 Angular.js 버전을 현재 (2014.1.28기준) 최신버전인 v1.2.10 으로 업데이트한다.

// bower.json 파일에서 angular 관련 버전을 1.2.10 으로 지정한다 

$ vi bower.json

{

  "name": "meanstack",

  "version": "0.0.0",

  "dependencies": {

    "angular": "1.2.10",

    "json3": "~3.2.6",

    "es5-shim": "~2.1.0",

    "jquery": "~1.10.2",

    "bootstrap": "~3.0.3",

    "angular-resource": "1.2.10",

    "angular-cookies": "1.2.10",

    "angular-sanitize": "1.2.10",

    "angular-route": "1.2.10",

    "angular-ui-router": "0.2.7"

  },

  "devDependencies": {

    "angular-mocks": "1.2.10",

    "angular-scenario": "1.2.10"

  }

}


// angular 버전 업데이트

$ bower update 

angular-resource#1.2.10 app/bower_components/angular-resource

└── angular#1.2.10

.. 중략..

angular-scenario#1.2.10 app/bower_components/angular-scenario

└── angular#1.2.10


// ui-router 설치 1번을 선택한다 

$ bower install angular-ui-router#0.2.7 --save

    1) angular-ui-router#0.2.7 which resolved to 0.2.7

    2) angular-ui-router#~0.2.8 which resolved to 0.2.8 and has meanstack as dependants

[?] Answer: 1

angular-ui-router#0.2.7 app/bower_components/angular-ui-router

└── angular#1.2.10


   bower를 통해 ui-router를 설치 후 "grunt build" 또는 "grunt serve" 를 실행하면 index.html에 필요 파일이 자동 추가된다. 자동 추가되는 파일의 정보는 bower_components/angular-bootstrap/bower.json 파일에 지정되어 있다. 

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

    <!-- bower:js -->

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

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

    <script src="bower_components/bootstrap/dist/js/bootstrap.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>

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

    <script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>

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

    <!-- endbower -->

    <!-- endbuild -->


  ui-router 또한 별도의 모듈이므로 app/scripts/app.js 안에 모듈 의존성을 설정한다 

'use strict';


angular.module('meanstackApp', [

  'ngCookies',

  'ngResource',

  'ngSanitize',

  'ngRoute',

  'ui.bootstrap',

  'ui.router'

])

  .config(function ($routeProvider) {

  .. 중략 ..

  });



4. FontAwesome Icon 설치

  다양한 아이콘을 사용하기 위하여 bootstrap과 별도로 fontawesom(http://fortawesome.github.io/Font-Awesome/) 라이브러리를 설치한다. 텍스트 정보를 좀 더 직관적으로 인지할 수 있도록 다양한 아이콘을 적용할 수 있다. 

$ bower install font-awesome --save

bower font-awesome#*            cached git://github.com/FortAwesome/Font-Awesome.git#4.0.3

font-awesome#4.0.3 app/bower_components/font-awesome


  설치 후 app/bower_components/font-awesome/ 폴더 밑으로 bower.json 이 존재하지 않으므로 필요한 파일을 index.html에 직접 기입해야 한다. index.html에 font-awesome.css 파일을 추가하자. 추가시 <!-- bower:css --> 영역에 넣으면 grunt build(grunt serve)시에 직접 넣은 정보는 자동 삭제되므로 <!-- bower:css --> 가 없는 <!-- build:css --> 안에 추가한다. 

     <!-- build:css styles/vendor.css -->

    <!-- bower:css -->

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

    <!-- endbower -->

    <!-- endbuild -->


    <!-- build:css({.tmp,app}) styles/main.css -->

    <link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.css" />

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

    <!-- endbuild -->


  'grunt build'를 하게 되면 font-awesome의 css는 app/styles/main.css 파일로 묶이게 된다. font-awesome.css 소스를 보면 css 상위의 fonts 폴더의 파일을 참조한다. 따라서 font-awesome의 fonts 폴더안의 파일들을 app/fonts 밑으로 복사한다.

Twitter Bootstrap과 Font awesome의 css가 참조하는 파일들 목록

$ cd app/fonts

$ ls 


fontawesome-webfont.woff

fontawesome-webfont.ttf

fontawesome-webfont.svg

fontawesome-webfont.eot

FontAwesome.otf

glyphicons-halflings-regular.woff

glyphicons-halflings-regular.ttf

glyphicons-halflings-regular.svg

glyphicons-halflings-regular.eot


이제 필요한 라이브러리들이 index.html에 추가되었다. 다음장에서는 Twitter Bootstrap을 이용하여 반응형 웹 디자인(Responsive Web Design : RWD) 메뉴와 전체 화면 레이아웃을 만들어 보자.

posted by Peter Note
2014. 1. 26. 07:50 AngularJS/Start MEAN Stack

yeoman은 자바스크립트 기반의 프론트앤드 개발을 도와주는 자동화 툴이다. 자바에서 요즘 많이 사용되는 Maven과 유사하다. 



1. 사용이유

  서비스 개발의 규모가 커지고 자바스크립트의 각 모듈별의 버전 의존성을 관리하려면 어떻게 해야 할까? 그리고 필요한 자바스크립트 모듈을 다운로드하기 위하여 각 사이트를 방문하여 다운로드 해야할까? 기본적인 코드 골격과 코딩 컨벤션은 최초에 어떻게 해야할까? 개발하고 나서 테스트 및 빌드 자동화는 어떻게 해야할까? 이러한 의문에 대한 해답을 주는 것이 Yeoman이다.

  - 스케폴딩 생성 : 기본적인 코드 골격을 만들어 주고 이후 추가되는 앵귤러 코드는 명령을 통해 생성한다 

  - 라이브러리 의존성 관리 : 프론트앤드에서 사용하는 다양한 jquery 모듈과 angular.js 모듈의 설치 및 버전 관리를 한다 

  - 테스트 및 빌드 자동화 : 테스트 코드를 작성하였다면 테스트 수행 및 코드 압축과 문법오류 검사 그리고 배포파일의 생성을 한다 

  - 사전 점검 : 프론트단의 코드를 Node.js기반 수행하여 브라우져에서 개발한 화면을 사전에 테스트 점검 가능하다



2. 설치하기  

  설치는 의외로 간단하다. 사전 준비로 Node.js를 설치 하였다면 NPM (Node Package Manager)을 통해 설치한다. yo, bower, grunt 의 특징은 자바스크립트로 개발하여 node.js기반위애 구동하는 도구이다 

// 1) yo 설치 

$ npm install -g yo


// 2) bower 설치 

$ npm install -g bower


// 3) grunt 설치 

$ npm install -g grunt-cli

  * http://yeoman.io, http://bower.iohttp://gruntjs.com 에서 설치관련 자세한 사항을 참조한다. 



3. 주요 기능 

  yeoman은 크게 3가지 기능으로 구분된다. 각 기능의 구분이 명확하고 사용하는 목적이 틀리기 때문에 잘 알아 두기 바란다. 기본적으로 yo를 yeoman팀에서 자체적으로 만들었고, bower와 grunt는 이미 존재하였던 도구로써 yeoman이라는 이름으로 통합한 것이다. 따라서 bower와 grunt를 별개의 독립적인 도구로 사용할 수도 있다


  yo 

  - 프론트앤드 개발을 위한 기본 구조를 만들어 준다. yeoman에서는 스켈폴딩 코드를 만들어 준다고 말한다 

  - 스케폴딩 파일을 생성하려면 목적에 맞는 제너레이터를 사전에 설치해야 한다.

    1) 제너레이터 명칭 구성 : generator-<UserDefine> 로서 UserDefine 명칭을 정한다. 예) generator-angular

    2) npm 을통하여 글로벌 설치한다 

    3) UserDeinfe 명칭이 angular 라면 yo 명령 다음에 제너레이터의 구분을 위하여 항시 해당 UserDefine 명령이 온다 

        yo angular <Subject>  또는 yo angular:<SubCommand>  <Subject>

    4) yo angular <Subject> 를 통해 초기 프로젝트의 명칭을 정하고 기본 골격을 생성한다 

    5) yo angular:<SubCommand> <Subject> 를 통해 기본골격 밑으로 기능을 추가한다. SubCommand는 제너레이터에 따라 사용자 정의 할 수 있다 

  - 프로젝트에 필요한 골격 코드 생성을 위한 자신만의 제너레이터를 만들 수 있다. 

  - 사용법

    1) angular 제너레이터 설치

    2) 프로젝트 폴더생성

    3) angular 프로젝트 생성을 하고 Sass 미사용, Bootstrap을 사용체크하면 필요한 파일과 모듈을 자동 설치한다

    4) 프로젝트 기본 골격코드 자동 생성하면 app 폴더가 프론트 개발 ROOT 폴더가 된다

// 1) 

$ npm install -g generator-angular


// 2) 

$ mkdir meanstack & cd meanstack


// 3) 

$ yo angular meanstack

     _-----_

    |       |

    |--(o)--|   .--------------------------.

   `---------´  |    Welcome to Yeoman,    |

    ( _´U`_ )   |   ladies and gentlemen!  |

    /___A___\   '__________________________'

     |  ~  |

   __'.___.'__

 ´   `  |° ´ Y `


Out of the box I include Bootstrap and some AngularJS recommended modules.


[?] Would you like to use Sass (with Compass)? No

[?] Would you like to include Twitter Bootstrap? Yes

[?] Which modules would you like to include? (Press <space> to select)

❯⬢ angular-resource.js

 ⬢ angular-cookies.js

 ⬢ angular-sanitize.js

 ⬢ angular-route.js


// 4) 


bower

  - bower는 트위터에서 개발한 프론트앤드 라이브러리 설치 및 버전 의존성 관리 도구이다 

  - yo를 통하여 스케폴딩 파일이 생성하면 bower 사용을 위한 기본 환경파일도 자동 생성된다 

    1) 환경파일은 bower.json 으로 현재 설치된 라이브러리 명칭과 버전을 자동 기록한다 

    2) 라이브러리 설치 위치정보는 .bowerrc 에서 변경한다. 기본값으로 "bower_components" 를 사용한다 

// 1) bower.json 최초 설치 정보 

{

  "name": "meanstack",

  "version": "0.0.0",

  "dependencies": {

    "angular": "1.2.6",

    "json3": "~3.2.6",

    "es5-shim": "~2.1.0",

    "jquery": "~1.10.2",

    "bootstrap": "~3.0.3",

    "angular-resource": "1.2.6",

    "angular-cookies": "1.2.6",

    "angular-sanitize": "1.2.6",

    "angular-route": "1.2.6"

  },

  "devDependencies": {

    "angular-mocks": "1.2.6",

    "angular-scenario": "1.2.6"

  }

}


// 2) .bowerrc 위치정보

{

    "directory": "app/bower_components"

}


  - 사용법 

   1) 검색 : "bower search <명칭>"

   2) 설치 : "bower install <명칭>  --save (또는 --save-dev)"

   3) 보기 : "bower list"

   3) 도움말 : "bower help" 또는 "bower help <명령어>"

$ bower list

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

meanstack#0.0.0 ~/meanstack

├── angular#1.2.6 (latest is 1.2.11-build.2184+sha.319dd1a)

├─┬ angular-cookies#1.2.6 (latest is 1.2.11-build.2184+sha.319dd1a)

│ └── angular#1.2.6 (latest is 1.2.11-build.2184+sha.319dd1a)

├─┬ angular-mocks#1.2.6 (latest is 1.2.11-build.2184+sha.319dd1a)

│ └── angular#1.2.6

├─┬ angular-resource#1.2.6 (latest is 1.2.11-build.2184+sha.319dd1a)

│ └── angular#1.2.6

├─┬ angular-route#1.2.6 (latest is 1.2.11-build.2184+sha.319dd1a)

│ └── angular#1.2.6

├─┬ angular-sanitize#1.2.6 (latest is 1.2.11-build.2184+sha.319dd1a)

│ └── angular#1.2.6

├─┬ angular-scenario#1.2.6 (latest is 1.2.11-build.2184+sha.319dd1a)

│ └── angular#1.2.6

├─┬ bootstrap#3.0.3

│ └── jquery#1.10.2 (2.1.0 available)

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

├── jquery#1.10.2 (latest is 2.1.0)

└── json3#3.2.6 (latest is 3.3.0)

  * angular 개발시점에 정식 릴리즈된 가장 최신버전을 사용할 예정이고 bower를 통하여 업데이트할 것이다. 


Grunt

  - grunt는 자바의 ant와 같은 기능을 수행한다

  - 다양한 플러그인을 통하여 기능을 첨부하여 확장할 수 있다. 

  - yo을 통해 프로젝트 골격 코드생성시 Gruntfile.js 환경파일이 기본 생성된다 

  - 또한 골격 코드 생성시 기본적으로 사용되는 플러그인은 node_moudles 폴더에 자동 설치된다 

// 1) node_modules에 기본 설치된 grunt 플로그인 


// 2) Gruntfile.js 내역 중 명령 일부

module.exports = function (grunt) {

  // Load grunt tasks automatically

  require('load-grunt-tasks')(grunt);


  // Time how long tasks take. Can help when optimizing build times

  require('time-grunt')(grunt);


  // Define the configuration for all the tasks

  grunt.initConfig({


    // Project settings

    yeoman: {

      // configurable paths

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

      dist: 'dist'

    },

    ... 중략...


grunt.registerTask('serve', function (target) {

    if (target === 'dist') {

      return grunt.task.run(['build', 'connect:dist:keepalive']);

    }


    grunt.task.run([

      'clean:server',

      'bower-install',

      'concurrent:server',

      'autoprefixer',

      'connect:livereload',

      'watch'

    ]);

  });


  grunt.registerTask('server', function () {

    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');

    grunt.task.run(['serve']);

  });


  grunt.registerTask('test', [

    'clean:server',

    'concurrent:test',

    'autoprefixer',

    'connect:test',

    'karma'

  ]);


  grunt.registerTask('build', [

    'clean:dist',

    'bower-install',

    'useminPrepare',

    'concurrent:dist',

    'autoprefixer',

    'concat',

    'ngmin',

    'copy:dist',

    'cdnify',

    'cssmin',

    'uglify',

    'rev',

    'usemin',

    'htmlmin'

  ]);


  grunt.registerTask('default', [

    'newer:jshint',

    'test',

    'build'

  ]);

};


  - 사용법

   1) 테스트 : grunt test

   2) 빌드 : grunt build

   3) 프리뷰 : grunt serve

// 1) 

$ grunt test

Running "clean:server" (clean) task


Running "concurrent:test" (concurrent) task


    Running "copy:styles" (copy) task

    Copied 1 files


    Done, without errors.



    Execution Time (2014-01-25 22:42:19 UTC)

    loading tasks   4ms  ▇▇▇▇ 25%

    copy:styles    10ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 63%

    Total 16ms


Running "autoprefixer:dist" (autoprefixer) task

Prefixed file ".tmp/styles/main.css" created.


Running "connect:test" (connect) task

Started connect web server on 127.0.0.1:9001.


Running "karma:unit" (karma) task

INFO [karma]: Karma v0.10.9 server started at http://localhost:8080/

INFO [launcher]: Starting browser Chrome

WARN [watcher]: Pattern "/Users/nulpulum/prototyping/yeomain/meanstack/test/mock/**/*.js" does not match any file.

INFO [Chrome 32.0.1700 (Mac OS X 10.9.1)]: Connected on socket nUaNX14YFrQJ_aeDkWM5

Chrome 32.0.1700 (Mac OS X 10.9.1): Executed 1 of 1 SUCCESS (2.184 secs / 0.033 secs)


Done, without errors.



Execution Time (2014-01-25 22:42:17 UTC)

concurrent:test  2.2s  ▇▇▇▇▇▇▇▇▇▇▇26%

karma:unit       6.2s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 73%

Total 8.4s


// 2) 브라우져가 자동 실행된다 

$ grunt serve 


Yeoman에 대한 이해와 기본적인 설치, 사용법을 익혔다. 프론트 앤드 개발을 하면서 계속 사용하게 되므로 잘 익혀 놓도록 하자 


posted by Peter Note
2014. 1. 17. 13:12 카테고리 없음

운영중이 사이트가 있다면 이제 손쉽게 안드로이드 실행파일을 만들어 보세요. 


http://www.appsgeyser.com/


해당 블로그 사이트를 .apk로 만들어 보았습니다. 1분만에 만들어 지네요. 안드로이드에 다운로드하여 설치하세요. 

이제 모바일 컨버젼스 블로그를 앱으로...^^


MobiconSoft.apk



posted by Peter Note
2014. 1. 2. 13:06 AngularJS/Start MEAN Stack

개인 블로그를 통하여 정보를 공유하는 일에서 한발 더 나아가 함께 글을 올리고 글에 대한 Q&A를 하는 웹앱 서비스를 만들어 보고 싶다. 내가 로그인을 했을 때 처음 보여지는 UI는 SNS 형태이며 내가 올린 질문(Question)에 대한 답변이나 커뮤니티안에서 활동하는 재능 기부자를 팔로잉 하면 해당 글들이 먼저 보여졌으면 좋겠다. 즉, 글에 대한 개인화를 원하며 재미있는 재능 기부 놀이를 해보고 싶다. 




1. 기존 커뮤니티 서비스

  - 특정 재능 기부자가 글을 연재하여 올린다

  - 여러 Q&A 게시판 형태를 취한다  

  - 내가 원하는 것을 Search 하여 찾아 보아야 한다 

  - PC에 최적화 되어있다. 어떤 것은 모바일에서 볼 수도 있다

   글올렸으니 알아서 찾아 읽어봐 ^^;



2. MEAN Stack 커뮤니티 서비스 

  - SNS 형태를 취하여 모바일기기(Tab, Phone) 중심으로 만들어진다 

  - MEAN Stack에 대한 글을 연재하는 재능 기부 개발자들이 있고, 이중 몇명을 following 할 수 있다.

  - 내가 연재글이나 별도의 란에 질문(Question)을 달면 MEAN Stack 재능 기부 개발자들에게 자동 Notify가 된다. 

    (단, 별도의 질문은 지정된 분류만을 할 수 있다. Concern Area)

  - 답변을 준 재능 기부자에게 점수를 통해 고마움을 표현하면 해당 점수는 재능 기부자의 인기도 순위에 영향을 준다.

  - 또한 연재 글에 대해서도 모든 사람은 한번 점수를 줄 수 있다.

  - 인기도는 천민, 일반, 귀족, 왕족, 신(구루) 등의 구분으로 명예의 전당에 공개되어 구루로 추앙된다. 게임처럼 놀고 싶다. 

  - 해당 인기도는 한달에 한번 reset되고 이달의 구루에겐 모종의 선물을 수여한다.(뭘 주지? ^^)

  - 즉, 답변이나 글연재를 많이 한다고 구루가 되지 않고 해당 답변, 글에 메겨진 점수를 통해 인기도 순위와 등급은 결정된다 

  - 가입한 누구나 특정 분류의 재능 기부자가 될 수 있다 

   재능 기부하면서 함께 재미 있게 놀고 싶다. 



3. Mockup

  - 서비스의 명칭 

    + Community Planet : 새로운 혹성의 커뮤니티. 점수통해 자신만의 영역을 만들어 간다. 우리는 디지털 이주민.

      

  - 메인 화면 

    + 좌측은 모바일의 Sliding 메뉴로 구성된다 

    + 로그인을 하면 우측 상단에 Push Message 건수와 로그인/로그아웃 링크가 나온다 

    + 중앙에는 내가 질문한 것에 대한 답변과 다른 질문/답변들 그리고 Following 한 재능 기부자들의 글목록이 나온다

    + 글 목록은 핀터레스트 스타일 이다 


  - Question & Answer

    + 내가 질문한 것과 답변 받은 것을 확인 할 수 있다 

    + 질문내역을 클릭 하면 질문에 대한 답변을 볼 수 있다. 

    + 답변에 대한 점수를 줄 수 있고, 답변이 틀리면 마이너스 점수도 줄 수 있다 


  - Following 재능 기부자들의 글 보기

    + 팔로어한 사람별의 최근 글을 볼 수 있다. 내가 관심갖는 글만 보기 

    + 보는 방식은 Q&A화면과 유사하다 

    + 연재글에 역시 질문과 답을 달 수 있다 

 

  - Concern Area

    + 관심영역은 현재 커뮤니티 서비스 관리화면에서 목록을 만들 수 있다. (마치 워드프레스의 관리화면처럼-향후 개발)

    + 특정 관심 영역을 들어가면 해당 영역에서 활동하는 모든 글들을 볼 수 있다.

    + 검색하여 글을 찾을 수 있다 


  - 구루구루

    + 이곳은 명예의 전당이다 

    + 가장 점수가 높은 재능 기부자를 상위에 놓고 점수가 높은 순으로 글목록이 나열 된다 

    + 그리고 이번달의 글과 질문 답변에 대해 볼 수 있다 



다음의 비디오를 참조 했으면 좋겠다. Community Planet은 바로 이 정신이다. 


posted by Peter Note
2013. 12. 15. 13:38 AngularJS/Start MEAN Stack

개발자들이 가장 힘들어 하는 부분은 UI 개발이라 생각한다. 더 나아가 UX(User eXperience)를 어떻게 UI속에 녹여내어 편의성과 제품의 가치에 맞는 감성을 넣을 수 있을지도 고민해 보아야 한다. 막상 클라이언트 개발을 시작하였지만 마땅히 디자이너의 도움 없이도 완성도 있는 서비스를 만들고 싶다면 CSS Framework을 사용해 보자. 


요즘 추세는 크게 두가지 측면으로 움직이는 것 같다. 하나는 웹의 모양과 색감등 보여지는 부분을 담당하는 CSS를 프레임워크화하여 일관된 UX를 전달해주고 있고 이를 공개하고 있다. 이중 가장 유명한 것이 트위터 부트스트랩이다. 버전 3.0 에서는 반응형 웹 디자인(RWD, Responsive Web Design)이 기본으로 적용되어 모바일 스마트기기의 해상도에 따라 화면이 최적화되어 표현될 수 있도록 하는 기능을 제공한다. 둘째는 플랫디자인이다. 구글이나 애플에서 주도를 하고 있고 간결하면서 사용성을 늘린 디자인으로 보인다. 기본 트위터 부트스트랩을 확장하여 플랫디자인 CSS를 적용한 다양한 CSS 프레임워크를 제공하고 있다. 


따라서 트위터 부트스트랩을 기본으로 하여 확장한 CSS 프레임워크를 사용하게 되면 '반응형 웹 디자인' 과 '플랫디자인'을 동시에 충족하여 적용할 수 있게 된다. 두가지 요소에 대해서는 이곳의 주제와 약간 벗어나므로 좀 더 전문적인 서적을 통해 개념과 상세 기능을 익히기 바란다. 여기서는 선택한 CSS 프레임워크를 어떻게 적용하고 활용하는지 중점적으로 보도록 하겠다. 



1. 반응형 웹 디자인 고려

  - Fluid Layout : 화면 사이즈에 따라 배치된 요소들의 정렬을 재 배치하는 것이다. 화면이 작아진다고 하여 좌우 스크롤이 생기는 것이 아니라 요소가 밑으로 흘러들어 간다. 이를 위하여 부트 스트랩은 Grid 12 컬럼으로 요소 배치 및 플루이드 정렬을 할 수있다 

  - Responsive Menu : 부트 스트랩은 상단에 메뉴를 놓고 해상도에 따라 메뉴의 형태를 자동 변경해 준다 

서비스 개발이 위의 두가지를 먼저 유념하여 사용하도록 한다. 기본적인 사용법은 트위터 부트스랩의 홈페이지를 참조하자 



2. 플랫 디자인에 대한 고려

애플은 iOS7에서 플랫디자인을 선보이고, 구글은 전사 서비스를 플랫디자인으로 도배하고 있습니다. 프러덕트 디자이너가 Visual 단계에서 바라본 플랫디자인의 요소 5가지를(참조) 유념하여 개발자가 UI와 UX를 플랫 디자인으로 진행할 때 고려해야할 사항에 대해 미리 알아두면 좋을 것 같습니다.

     + Use of simple elements 

        심플한 아이콘 : 사각, 원형의 단순 모형에 배치, 컬러, 형태로 표현 -> 사용자가 UI 를 쉽게 인식하고 사용토록 만든다 

     + Absence of depth 

        배제된 효과 : 그림자, 입체감, 그라데이션등의 현실감있게 하는 3D적 효과를 배제하여 직관적으로 만든다  

     + Typography

        타이포그래피 : 화려한 폰트를 배제하고 간결한 폰트를 사용한다. 예) 산세리프 

     + Color

        단순한 칼라 : 2~3가지 또는 5~6가지의 색상만을 사용하여 간결하고 과감하고 표현한다. 핑크그린블루가 대세  

     + Minimalism

        미니멀리즘 : 미니멀리즘과 부합 - 간단하고 심플하게 표현으로 다양한 웹, 모바일에 적응이 쉽다  



3. 서비스 디자인으로 나아가야 한다 

  서비스을 만들 때 개발자는 어떻게 접근을 하고 있을까요? 아마도 대부분 디자이나 웹 퍼블리셔가 만들어준 화면에 코딩을 하는 정도를 경험했으리라 봅니다. 하지만 자신의 웹앱 서비스를 만들기 시작한다면 단지 디자인이 문제가 아니라 UX라는 사용자 경험을 고려하여 사람과 컴퓨터의 상호작용을 생각해 보아야 하고, 우리가 만들려는 프로덕트의 가치를 서비스에 어떻게 녹여 내야 하는지 연구하고 새롭게 창조하는 과정을 거쳐향 합니다. 이를 줄이면 서비스 디자인이라고 말하며 서비스 개발전 우리가 사용하는 도구와 프레임워크들이 가치를 창출하기 위하여 어떻게 쓰여야하는지 큰 뷰에서 설명을 해주고 있습니다. 인식의 전환을 통하여 중요한 가치를 찾고 반응형 웹 디자인이나 플랫 디자인이 왜 나왔는지 이해하여 보는 것도 재미날 것같습니다. 한국디자인진흥원의 윤성원님이 이야기하는 서비스 디자인 개념 자료를 한번 보시면 쉽게 이해 되리라 생각합니다. 


  - 서비스 디자인 이란 무엇인가? - 개념 이해


posted by Peter Note
2013. 12. 5. 13:34 AngularJS/Start MEAN Stack

이전 블로그에서 MEAN Stack에 대한 의미를 알아보았습니다. Angular.js 의 Why, How, What을 살펴보도록 하겠습니다.


1. Angular.js 가 주고자 하는 가치는 무엇인가?

  앵귤러는 모던 애플리케이션을 개발하기 위한 프레임워크입니다. 복잡하고 단순 반복적인 작업을 대폭 줄여줌으로써 신속한 개발을 가능하게 해줍니다. 이를 통해 보다 빨리 고객의 피드백을 받고 개선할 수 있는 에자일한 개발 진행이 가능해 집니다. 보다 적은 작업을 통해 보다 더 많은 행복을 주는 프레임워크입니다. 


  1989년 HTML을 시작으로 2005년 Ajax가 나오고 이후 jQuery를 통하여 DOM 을 조작을 통한 웹서비스 개발 시대를 거쳐 지속적인 브라우져 기술의 성숙과 JavaScript 해석기의 성능향상으로 이제는 자바스크립트를 통해 클라이언트에서도 서버와 같은 MVC 패턴 방식의 애플리케이션 개발이 가능해 졌습니다. 만일 jQuery만을 통해 웹앱을 개발한다고 생각하면 HTML 페이지마다 들어가는 서버코드와 자바스크립트들의 복잡한 코드에 머리를 쥐어 짤지도 모릅니다. 

  저는 13년을 넘게 자바언어로 서버만을 개발했었습니다. 가끔 클라이언트단의 jQuery 코드를 볼 때마다 저 영역으로는 절대 들어가지 말자라고 생각했습니다. 왜일까요? 그것은 서버처럼 잘 정비된 애플리케이션 프레임워크도 없으며, 서버코드와 자바스크립트를 HTML 사이사이에 끼워 넣으며 스파게티같은 코드를 짜야 하기 때문이었습니다. 

  그러나 2010년 이후부터 상황은 바뀌었습니다. Backbone.js가 이러한 복잡함을 해결하고자 초기 프레임워크로 나왔고, 이후 Ember.js 그리고 Angular.js 에서 Meteor.js 까지 모던 웹앱을 개발할 수 있는 프레임워크가 나왔습니다. 앵귤러는 어떻길래 이들 프레임워크 중 단연 선풍적인 인기를 누리고 있는 걸까요?


  - .js F/W  트랜드 



- .js 에서 dot(점)을 빼고 Backbonejs 와 Angularjs 트랜드 


  Backbonejs 와 Angularjs(노란색) 의 트랜드 변화는 급상승 중



2. Angular.js 가 어떻길래 개발자들이 열광하는가?

  앵귤러의 아버지인 미스코님(Misko Hevery)의 소개 동영상을 잠시 감상해 봅시다 


  요즘 SI시에 서버개발을 위하여 Spring Framework와 iBatis(myBatis)를 사용하는 것이 기본적인 관례처럼 되었는데요. 이들의 기능을 잠깐 생각해 볼까요. 

  - DI (Dependency Injection) 을 통하여 코드간의 결합도를 줄여주고, 테스트 코드의 작성을 쉽게 해줍니다. 

  - MVC 패턴를 통하여 기본적인 서버 개발의 틀을 가이드 해줍니다.

  - 다양한 라이브러리와 툴의 결합으로 개발 생산성을 높여줍니다.


 이를 앵귤러 입장에서 생각해 보면 정확히 위와 같은 요구사항을 충족해 주고 있습니다. 

  - DI를 지원합니다. 모듈단위의 개발로 코드를 간결하게 유지하고 테스트를 쉽게 해줍니다.

  - MV* 패턴을 통하여 역할을 나누고 아키텍쳐 Layered 개발을 가능하게 해줍니다. 당연히 유지 보수가 쉬워지겠죠

  - jQuery의 Plugins 포함한 기존의 다양한 라이브러리를 재 사용할 수 있습니다. 

  - 서버의 Maven, Ant 와 같은 관련된 라이브러리 의존성 관리 및 빌드 자동화 툴과 결합하여 개발 생산성을 높일 수 있습니다. 


  하지만 앵귤러를 시작하기 전에 하나의 선입견을 버리고 새로운 인식으로 접근을 해야 합니다. 자바스크립트는 이제 브라우져에서 화면의 단순 조작을 통한 효과에 쓰이는 언어가 아닌 진정한 엔터프라이즈급 애플리케이션부터 모바일 웹앱까지 모던한 애플리케이션을 만드는데 가장 많이 쓰이고 있는 개발언어가 되었다는 것입니다. 구글은 크롬앱을 코르도바(폰갭)를 통하여 안드로이드 및 iOS에서 구동하는 툴킷을 2014년 초에 출시할 계획입니다. 크롬앱은 자바스크립트로 개발합니다. 이는 본격적으로 네이티브 모바일 앱과 자바스크립트 모바일 웹앱이 함께 공존할 수 있는 시기가 왔다는 것을 암시합니다. 


 앵귤러(Angular.js)는 화면을 조작하는 라이브러리나 화면을 조작하는 프레임워크가 아니 모던 애플리케이션을 개발하는 프론트엔드 프레임워크입니다. 최근에는 이를 SPA(Single Page Application) 개발이라 부릅니다. Adobe의 Flex 기술을 통하여 클라이언트단에 엔터프라이즈 애플리케이션을 개발하는 RIA(Rich Internet Application) 가 선풍적인 인기를 누렸음을 잘 알것입니다. 인터넷이 되는 스마트 기기 에서 Adobe Flash의 공식 미지원 발표에 있은 후 이제 RIA의 용어는 점점 잊혀져 가고 있습니다. 하지만 사람들의 요구는 UX에 점점 더 목말라하고 있습니다. 단순한 웹서비스로는 이에 대응하기 힘들며 PC시대의 RIA를 표방한 Adobe의 Flex 프레임워크처럼 현재의 PC와 Mobile을 통합하는 진정한 RIA와 같은 자바스크립트 진영의 개발 프레임워크가 바로 앵귤러(Angular.js) 입니다. 무엇을 제공하 길래 앵귤러는 SPA 프레임워크의 큰형님이 되고 있는 걸까요?


 SPA vs RIA 비교 



3. Angular.js에는 무엇이 있는가?

  앵귤러는 클라이언트의 애플리케이션을 견고하게 만들 수 있는 방법을 제시하고 있습니다. 클라이언트의 생명은 바로 UX에 있다고 생각합니다. 훌륭한 사용자 경험을 주기 위하여 앵귤러 사용한다면 빠르게 개발하고 사용자의 피드백을 받아 개선해 갈 수 있습니다.


  견고한 싱글 페이지 웹 애플리케이션(SPA)을 만들기 위하여 앵귤러는 다음과 같은 기능을 제공합니다. 

  - 모듈 단위 개발을 통하여 글로벌 영역을 오염시키지 않고 모듈 단위 개발을 가능하게 해줍니다. 따라서 대규모의 애플리케이션 확장이 가능해 집니다. 


  - 양방향 데이터 바인딩을 통하여 View(HTML) 와 Controller(자바스크립트) 사이에 데이터에 대한 양방향 동기화를 자동으로 해줍니다. 즉, View 에서 데이터를 입력하면 Controller 단의 데이터를 업데이트 해주고, 그 반대도 가능해 집니다. 기존 jQuery에서는 데이터의 동기화를 위하여 Event와 Listener를 등록하는 코드를 모두 개발해야 했다면 앵귤러에서는 이런 코드를 찾아 볼 수 없습니다. Flex의 [Bindable]을 생각하면 됩니다. 


  - 앵귤러를 보통 MVW 프레임워크라 부릅니다. W는 Whatever의 의미로 Controller, Service등 무엇이든 올 수 있다는 것입니다. 역시 가장 중요한 것은 Model과 View의 연동을 양방향으로 함으로써 UX를 보다 쉽고 빠르게 개발토록 하는데 있습니다. 


  - Controller는 View HTML에서 발생한 이벤트에 따라 Model 변경을 제어하고, Service는 백앤드 서비스를 데이터 I/O 통신을 담당합니다. 각 필요한 기능은 DI(Dependency Injection)을 통하여 펑션의 파라미터 주입방식으로 인스턴스를 받아서 사용합니다. 


  - SPA 의 Single Page라는 의미는 최초에 index.html 을 서버로부터 받은 후 부분적인 화면의 변경을 위하여 부분 HTML(Partial HTML)만을 받아서 index.html의 특정 영역의 DOM 객체 변경을 통해 View의 일부분을 바꿔줍니다. 이후 서버로 부터 받는 것은 View의 컨텐츠 데이터인 JSON 이나 XML 입니다. 이렇게 화면의 부분 변경을 위하여 Routing 기능을 제공합니다. 


  - jQuery + PHP, jQuery + JSP 코드가 들어간 HTML 이 기존의 코딩 방식이라면 Javascript + HTML 만 존재하는 것이 앵귤러의 코딩 방식이다. 또한 반복해서 사용하는 HTML 또는 자바스크립트 코드조각이나 위젯/플러그인을 앵귤러만의 컴포넌트로 만들어서 HTML tag, attribute, class, comment 등의 방식으로 HTML에 포함시킬 수 있는 Directives(지시자) 기능을 제공합니다. Flex의 MXML에 차트관련 컴포넌트를 <Line> 태그로 표현하듯이, Angular.js의 Directive(컴포넌트)를 만들면 HTML안에 <Line> 과 같은 태그를 포함시킬 수 있습니다. 즉, Directive는 HTML 태그로 표기할 수 있는 컴포넌트 모듈을 만들 수 있고 Callback 펑션 기능을 내부로 숨기고 재사용할 수 있는 것이다. 


  - 백앤드로 Node.js를 사용한다면 Flex의 Messaging 기술과 같은 Push 방식의 구현을 Socket.io 모듈을 통하여 보다 더 쉽게 구현 할 수 있다. 


이제 자바스크립트 코딩이 가능하다면 싱글 페이지 애플리케이션(SPA) 에 도전해 봅시다.



<참조>

  - 스타트업을 위한 서비스 개발 기술 - MEAN Stack

  - 웹 애플리케이션 견고하게 만들기

  - 프론트 엔드 웹앱 프레임워크 (SlideShare)

posted by Peter Note
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 Peter Note
2013. 11. 30. 11:58 HTML5, CSS3/jQuery

페이스북 같은 앱에서 사용하는 좌/우측 스라이딩 사이드 메뉴를 Angular.js 기반으로 만들어진 컴포넌트에 대해 알아보자. 



Angular-Snap.js

    

  - http://jtrussell.github.io/angular-snap.js/

  - 드래그하여 메뉴를 열수 있다 

  - 버튼 클릭으로 메뉴를 열수 있다 

  - IE8 지원하나 snap.js 가 IE8 미지원이므로 미지원



Angular-SlideNav

  

  - https://github.com/sthomp/angular-slidenav.js

  - 버튼 클릭으로 메뉴를 열수 있다 

  - IE8 미지원



jPanelMenu

  

  - http://jpanelmenu.com/

  - jQuery 기반 메뉴

  - IE8 ?



Sidr

  

   - http://www.berriart.com/sidr/

   - left, right 슬라이딩 메뉴 

   - IE8 지원



Nexus Style Side

  

  - http://www.jqueryscript.net/menu/Google-Nexus-Page-Like-Sidebar-Menu-with-CSS3-javascript.html

  - 작은 아이콘에서 큰 아이콘과 텍스트가 펼치는 효과 (강추)

  - IE8 미지원



Multi-Level Side Menu

  

  - http://www.jqueryscript.net/menu/Multi-Level-Slide-Push-Menu-with-CSS3-Javascript-MultiLevelPushMenu.html

  - 또 다른 멀티 레벨 메뉴 (강추) : 메뉴로 사용하기 괜찮음

  - IE8 미지원

 


Blueprint Side

  

  - http://tympanus.net/Blueprints/SlidePushMenus/

  - 왼쪽, 오른쪽, 위, 아래로 메뉴가 펼쳐진다

  - 스타일은 푸른색  

  - IE8 지원 



MMenu

  

  - http://mmenu.frebsite.nl/

  - 또 다른 jQuery MMenu (강추)

  - 모바일에 잘 맞는 슬라이딩 메뉴

  - 다른 컴포넌트도 있음 

  - IE8 미지원



Tegansnyder

  

  - https://github.com/tegansnyder/JQuery-Mobile-Slide-Menu

  - jQuery Mobile Slide Menu : 페이스북 형태 

  - 페이스북과 유사한 Slide Menu 만들기 블로그 : 이것이 좀 더 괜찮음

  - IE8 지원



Snap.js

  

  - https://github.com/jakiestfu/Snap.js

  - left, right 메뉴 제공

  - 드래그 제공

  - 페이스북 스타일 제공

  - IE8, 9 미지원 



Meny

  - http://lab.hakim.se/meny/

  - IE8 지원



<참조> 

  - 원문 : Slide Out Sidebar Navigations

  - 원문 : jQuery Side Menu 목록

  - Angular Snap.js

  - Angular SlideNav

  - Angular Datatable

  - 15개의 RWD Menu 

posted by Peter Note
2013. 11. 28. 13:19 AngularJS/Concept

하기 영상을 보면 JSON Object일 때와 String(Primitive type) 일때의 처리가 틀리다 왜그럴까 고민을 해보자 



1. Angular.js scope 상속 방법

  - childe scope는 기본적으로 parent scope를 상속받는다. 

  - 단, directive에서 scope : {...} 설정에서 scope isolate을 정하게 되어있다 

  - 원칙

1) define objects in the parent for your model, then reference a property of that object in the child: parentObj.someProp

    부모 scope에 object를 정의하면 child scope로 상속 된다. 그러나 primitive type(string, number, boolean)은 상속되지 않는다 

2) use $parent.parentScopeProperty (not always possible, but easier than 1. where possible)

    $parent 의 프로퍼티를 사용한다. 항상 가능하진 않으나 1)번 보다는 쉽다

3) define a function on the parent scope, and call it from the child (not always possible)

    부모 scope에 function을 정의하면 child scope로 상속 된다



2. JavaScript Prototypal 상속에 대하여

  - scope chaing에 따른 상속 : childScope는 primitive, object 전부 호출 가능 




3. Angular.js Scope 상속에 대하여 

  - 상속유형 

1) The following create new scopes, and inherit prototypically: ng-repeat, ng-include, ng-switch, ng-view, ng-controller, directive with scope: true, directive with transclude: true.

    ng-repeat, ng-include, ng-switch는 새로운 scope를 만들고 prototypically를 상속을 한다. 

    즉, scope:true, transclude:true인 directive이다 (새로운 scope를 만들지 않는 것은 scope: false 이다)

2) The following creates a new scope which does not inherit prototypically: directive with scope: { ... }. This creates an "isolate" scope instead.

     prototypically사용을 하지 않고 새로운 scope를 만들려면 scope: {...} 를 정의하여 "isolate" scope를 만들어야 한다


  - ng-include의 예

// controller 에서 

$scope.myPrimitive = 50;

$scope.myObject    = {aNumber: 11}; 


// html 에서 

<script type="text/ng-template" id="/tpl1.html">

    <input ng-model="myPrimitive">

</script>

<div ng-include src="'/tpl1.html'"></div>


<script type="text/ng-template" id="/tpl2.html">

    <input ng-model="myObject.aNumber">

</script>

<div ng-include src="'/tpl2.html'"></div>

  - 최초 설정 상태 : ng-include는 새로운 ChildScope1, 2를 생성한다. ParentScope는 ng-include를 감싸는 상위 Controller scope 가정 


  - 파란색 input box에 값을 입력 할 때 : primitive는 child scope에 새로운 property가 생성됨



  - 분홍색 input box에 값을 입력 할 때 : object는 parentScope의 값이 변경됨 


  - 파란색을 분홍색처럼 영향을 미치고 싶다면 하기와 같이 사용하면 parent scope의 primitive 값을 child scope에서 제어가능

     * 펑션도 Object와 동일하게 Child에 상속된다 

<input ng-model="$parent.myPrimitive">

  - ng-switch 도 ng-include와 유사하게 동작한다 



4. ng-repeat 상속의 경우 

  - ng-include나 ng-switch와 약간 틀리다 

// controller 에서 

$scope.myArrayOfPrimitives = [ 11, 22 ];

$scope.myArrayOfObjects    = [{num: 101}, {num: 202}]


// html 에서 

<ul><li ng-repeat="num in myArrayOfPrimitives">

       <input ng-model="num">

    </li>

<ul>


<ul><li ng-repeat="obj in myArrayOfObjects">

       <input ng-model="obj.num">

    </li>

<ul> 

  - 최초 ng-repeat이 새로운 scope를 하나 만들고 iteration 하면서 아이템의 값을 새로운 scope를 또 만들어 새로운 property 에 할당함

    여기서, 새로운 proeperty란 loop 변수 - 위의 예에서 num 또는 obj - 이다. 즉, 하기와 같은 작업이 일어나는 것이다. 

childScope = scope.$new(); // child scope prototypically inherits from parent scope ...     

childScope[valueIdent] = value; // creates a new childScope property

  - 파란색 Primitive 배열 경우 : 새로운 scope가 생기고 loop 변수 num이 Childe scope에 생기고 primitive 값의 복사가 이루어진다. 

    즉, referencing 되지 않아 Parent Scope와 관련성이 없어진다


  - 분홍색 Object 배열 경우 : Child Scope가 생성되고 loop 변수 obj는 parent scope의 배열 요소를 referencing 한다. 

    즉, child에서 변경을 하면 parent scope 값도 변경되는 것이다 


  - ng-controller

    + 일반적인 prototypal 상속을 따른다 

    + controller 끼리 $scope를 통해 정보를 공유하는 것은 좋지 않다. 공유할려면 service를 이용한다 (참조)

  - ng-view

    + ng-include와 동일하다 



5. 사용자 정의 Directive의 Scope 경우

  - directives를 만들 때

    + scope: false 는 기본값이다. directive를 만들면 scope를 새로 생성하지 않는다 

    + scope: true 를 정의하면 일반적인 prototypal 상속을 따른다

    + scope: { ... } 경우는 isolate/isolated scope를 새롭게 생성한다.

  - scope: {...} 경우 상세 고찰 

1) prototypically 상속을 하지 않는다 

2) 재사용 가능한 컴포넌트를 만들 때 사용한다. 즉 컴포넌트가 parent scope의 값을 read/write 못하게 한다

3) 그러나 간혹 parent scope에 접근(access) 하고 싶을 경우 object hash를 사용한다 

    '=' : two way binding (isolate scope <-> parent scope) 

    '@' : one way binding (isolate scope <- parent scope)

    '&' : parent scope expressions 에 바인딩 된다 

4) objec hash는 자신의 directive의 attributes 가 바인딩시에 이용됨을 주의한다 

5) object hash 는 parent scope의 property를 지정 할 수 없다 

    즉, 'parentProp' 가 있을 때 <div my-directives>의 scope: {localProp: '@parentProp'} 지정로 하지 않고, 대신 접근하고 싶다면

    <div my-directives the-Parent-Prop=parentProp>  의 scope: {localProp: '@theParentProp'}로 parent property를 명시해야 한다 (참조 예제)

6) isolate scope 의 attribute 명칭과 parent scope의 property 명칭이 같다면 scope: { attributeName: '=' } 이런식으로 설정함

    틀리면 scope: { keyName1: '@isolateAttributeName', keyName2: '=isolateAttributeName'} 으로 정의함

  - isolate scope 개념도

    + isolate scope 가 생성되면 __proto__ 는 Scope를 레퍼런스 한다 (하기 그림의 오랜지색 박스 'Object')

    + isolate scope의 $parent는 parent scope를 레퍼런스 한다. 그렇다고 prototypically 상속을 parent scope로 부터 하진 않는다 

    + 하기와 같이 정의 하였을 경우 

// html 에서 : attribute 에서 parent scope의 property를 접근한다 

<my-directive interpolated="{{parentProp1}}" twowayBinding="parentProp2">


// directive 에서 : =, @, & 등을 사용하여 parent scope property의 접근 방법을 명시한다 

scope: { interpolatedProp: '@interpolated', twowayBindingProp: '=twowayBinding' } 


// directive의 linking function 에서 

scope.someIsolateProp = "I'm isolated" 

    + 위 코드이 개념도 


 - @ 을 사용하면 linking function안에 하기 코드를 사용한다. 

    즉, attrs.$observe('interpolated', function(value) { ... } 의 value에 11을 설정한다  

attrs.$observe('attr_name', function(value) { ... }

  - 따라서 scope.interpolatedProp는 linking function 안에 정의 되지 않으나, 

     scope.twowayBindingProp는 inking function 안의 정의 된다. 이에 대해 해당 링크를 참조하자 (예제)

 


6. Transclude 에 대하여

  - transclude :true 하면 새로운 "transcluded" child scope를 생성한다. 이는 일반적인 prototypically 상속을 한다 

  - transaclude content가 two way binding을 원한다면 $parent를 이용한다

  - isolate scope 객체와 transclude scope 객체는 형제지간으로 $parent는 같은 부모 object를 레퍼런스 한다 (sibling)

  - isolate scope 의 $$nextSibling은 transclude scope 객체를 레퍼런스 한다 



  - 링크 사이트를 참조한다


즉, Isolate Scope를 가지게 되면 일반적인 Prototypal 상속을 따르지 않는다. 그러나 Isolate Scope(== Child Scope)가 간혹 parent scope를 접근하고 싶을 때는 @, =, & 와 같은 object hash를 Directive의 Attribute 앞에 붙임으로써 접근 방법을 지정할 수 있다. 


* 주의) 버전 1.1.* 와 1.2.* 버전사이의 동작이 틀리다. 1.1.* 에서는 정상 작동하지만 1.2.* 에서 Isolate Scope방식이 정상 동작하지 않는다! (이유를 찾아봐야 함)

  예제에서 왼쪽 메뉴의 "External Resource"를 바꾸어 가며 테스트해 보라

  "http://code.angularjs.org/1.1.5/angular.js" <->  "http://code.angularjs.org/1.2.0/angular.js"



<참조>

  - 원문 : Angular.js Scope에 대한 이해 개념도

  - Angular.js Scope 상속 영역 

  - Angular.js Architecture 고려 사항 (필독)

  - Angular.js Isolated Scope에 대한 이해 (필독)

  - Isolated Scope @, =, & 예제

  - Transclude의 two-way binding 실현

  - 본문 다이어그램 소스보기 프로그램

posted by Peter Note
2013. 11. 28. 09:32 HTML5, CSS3/CSS

Bootstrap은 CSS Framework으로 프리젠테이션을 담당하고 jQuery UI 와 Angular.js는 자체 컨트롤도 가지고 있지만 DOM 핸들링을 한다. 만일 서비스를 만드는데 Bootstrap + jQuery UI 조합일 때 또는 Bootstrap + Angular.js 조합일 때 그리고 Bootstrap + jQuery UI + Angular.js 조합 일때 프리젠테이션을 통합하는 방법은 어떤 것들이 있을까? 각 조합에 대해 어떻게 사용할지 알아보자.



1. Twitter Bootstrap 만 사용할 경우 

  - 부트스트랩만 놓고 보면 CSS Framework으로써 기본적인 Fluid Layout 형태와 기본 HTML 컨트롤의 모양을 정의하고 있다

    또한 JavaScript 기반 다양한 component들도 포함한다 

  - 부트스트랩의 CSS 부분을 향상시키고 extra component를 추가한 부분 변경모델은 Bootflat, Bootplus, FlatStrap, LeapStrap

  - 또는 다양한 완성체 템플릿을 startBootstrap같은 곳에서 만들어 제공도 한다 

    



2. Twitter Bootstrap + jQuery UI  함께 사용할 경우 (이제 없어졌다)

  - 문제는 여기서 부터 시작한다 

  - Bootstrap의 CSS 스타일이 존재하고 jQuery UI 도 자체 theme을 CSS로 정의하고 있다

  - Boottrap의 CSS를 jQuery UI에 접목하여 사용할 경우는 jQuery UI Widgets 에 대해서 Bootstrap theme을 입힌 것을 사용한다

    http://addyosmani.github.io/jquery-ui-bootstrap/

    



3. Twitter Bootstrap + Angular.js 함께 사용할 경우

  - Angular에 JavaScript component를 포팅하기 위하여 Directive로 감싸야하는 문제가 있다

  - jQuery UI와 Bootstrap theme을 결합하였다면 Angular.js 와 Bootstrap theme 을 결합하고 있다

  - 하지만 이것은 Bootstrap v2.3.* 버전의 component를 Angular.js Directive로 구현하는 것이다

    http://angular-ui.github.io/bootstrap/

    



4. Twitter Bootstrap + jQuery UI + Angular.js 함께 사용할 경우 

  - 해당 형태는 3번의 연장선으로 보면 된다 

  - Twitter Bootstrap의 javascript component를 Angular.js Directive화 하였듯, jQuery UI component들도 Directive화 한것이다 

    http://angular-ui.github.io/

     

   - 소스 : https://github.com/angular-ui/


기존의 JavaScript Component를 처리하려면 Directive화 과정을 거쳐야 하는데 이런 번거로운 과정을 해소하고자 Bootstrap의 JavaScript Component를 Directive로 만들어 놓은 Angular UI Bootstrap이 나왔다. jQuery UI를 지원하기 위하여 Angular UI 에 상당부분 즐겨 사용하는 JavaScript Component를 포함시켰으나 이 또한 jQuery UI의 모든 것을 커버하고 있지는 못하다. 


이래저래 문제되는 것은 Angular.js를 기반으로 개발할 때 UI를 담당하는 JavaScript Component를 Angular.js의 Directive화를 잘 해야 한다는 것이다. 



<참조>

  - Bootstrap ToolBox : 다양한 UI 컴포넌트들 

posted by Peter Note
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 Peter Note
2013. 11. 27. 14:37 HTML5, CSS3/CSS

개발자들이 서비스를 만들면서 어렵게 여기는 부분이 디자인이다. 특히 웹 서비스를 만들라 치면 어디서 부터 시작해야 할지 난감할 때가 있는데 이를 해소하고자 CSS Framework를 오픈소스로 제공하는 업체들이 늘어나고 있다. 가장 유명한 Twitter Bootstrap 부터 Pure까지 그 종류도 많은데 어떤 것들이 있는지 정리해 본다. 정리하면서 느끼는 것은 Bootstrap을 확장한 framework인 Bootflat, Maxmertkit같은 것이 좀 더 다양한 느낌의 모듈을 제공하고, Metro UI스타일의 Flat 디자인을 언젠가 꼭 써보고 싶다. 그리고 GroundWork, Foundation, Pure 도 테스트해 볼 필요가 있겠다. 그외의 웹 모듈은 선택한 CSS Framework에 Add할 수 있는지 확인 후 쓰면 되겠다 



Twitter Bootstrap

  - http://getbootstrap.com/

  - CSS 화면 레이아웃과 스타일 제공

  - 화면 구성을 위한 jQuery 접목된 JavaScript 컴포넌트 제공 

  - RWD (반응형 웹디자인) 제공 : RWD 25개 도구들

  - 다양한 곳에서 많이 사용하나 개인적으로 색감이 이젠 식상할 때가 된 듯함

  - Bootstrap 확장 툴박스들 소개  그외 정리한 툴들 (강추)

  - OpenTutorial 동영상으로 배우기

  - Bootstrap v3 으로 화면 만들기

  - 다양한 도구와 컴포넌트 목록 *강추

  - BootStrap Theme 바꾸는 방법




Semantic UI

  - http://semantic-ui.com/

  - Bootstrap같은 기계적인 용어보단 인간친화적인 CSS Class 명칭

  - 좀 더 상업적인 UI 제작에 필요한 다양한 웹 모듈 제공

  - 개인적으로 인간친화적인 따뜻한 색감을 좋아함




Zurb Foundation 

  - http://foundation.zurb.com/

  - 다양한 화면 레이아웃 템플릿 제공으로 빠른 포로토타입핑 가능

  - RWD 제공

  - 웹서비스 페이지 만들기 적합




BootSnipp

  - http://bootsnipp.com/

  - Bootstrap에서 사용하는 코드 템플릿 제공

  - 다른 Template 리소스 사이트들



Start Bootstrap Template

  - http://startbootstrap.com/

  - template을 가지고 시작해 보자 

  - BootPlus에서도 제공함 

  - CodeStrap 또 다른 변형

  - http://bootswatch.com/ 에서는 다양한 Theme 색감의 bootstrap 선택가능



Bootflat

  - http://www.flathemes.com/

  - Bootstrap v3.0 확장판이다 

  - Bootstrap css 파일과 확장된 css를 링크하므로 Bootstrap에서 모자란 부분을 보완할 수 있다

  - https://wrapbootstrap.com/ 의 테마들처럼 Bootstrap 확장 느낌이 난다 



BootPlus

  - http://aozora.github.io/bootplus/

  - Bootflat과 유사한 기능을 제공

  - CSS의 느낌을 좀 더 간결하게 표현함 



FlatStrap

   

  - http://flatstrap.org/index.html

  - Bootflat과 유사함 



LeapStrap

  - http://wilkesalex.github.io/leapstrap/

  - touch smart device 고려 



FuelUX

  - http://exacttarget.github.io/fuelux/

  - Bootstrap 스타일의 다양한 컨트롤러 



PixelKit 

  

  - http://pixelkit.com/

  - 프리미엄 UI Kits 을 Theme별로 제공을 한다 

  - 예제 Sweet Candy

  - 소스 : https://github.com/Pixelkit/PixelKit-Bootstrap-UI-Kits

  - 상용이다 



Maxmertkit

  - http://www.maxmert.com/

  - Bootstrap 과 유사한 느낌을 전달 CSS class가 항시 "-" 또는 "_"로 시작하여 다른 CSS Framework와 충돌방지 

  - Bootflat에서 제공하지 않는 3D 적인 디자인과 효과 좋음 

  - Bootstrap css 파일과 혼용하여 사용할 수 있는지 체크 필요



Metro UI CSS

  - http://metroui.org.ua/

  - MS의 Metro Flat 디자인 제공

  - Bootstrap에서 느끼지 못하는 깔끔함이 있음 

  - RWD 제공




GroundWork

  - http://groundwork.sidereel.com/

  - 웹페이지, 이미지 겔러리, 커머스사이트 레이아웃 예제 제공

  - 다양한 화면사이즈에 대한 테스트 제공 (RWD)

  - 개인적으로 좋아하는 약간 따뜻한 색감 

  - 웹 모듈은 많지 않고 다른 UI 툴과 연계해서 사용하면 좋을 듯함



Pure

  - http://purecss.io/

  - Yahoo 에서 만듦

  - 사이즈 작고 flat한 디자인으로 간결한 맛이 있음 



Ink v.2

  - http://ink.sapo.pt/

  - 빠르게 웹 인터페이스를 개발할 수 있는 툴이다 

  - RWD 제공

  - Bootstrap과 거의 유사

  - Drap 컴포넌트 제공 



UIKit

  - http://getuikit.com/

  - 30가지의 UI 모듈을 제공

  - 다른 CSS Framework엔 없는 효과를 준 모듈 존재



Amazing Web Library

  - http://a-web.me/

  - UIKit과 유사하지만 차트와 그리드에 대한 부분이 강력함 




Brick

  - http://mozilla.github.io/brick/

  - 모질라에서 제공

  - 모바일에 특화된 UX적인 효과 제공

  - 모바일 예제 데모를 충실히 보여줌 



MicroJS

  - http://microjs.com/ (강추)

  - 다양한 웹 컴포넌트 검색 

  - 파일 크기 명시



<참조>

  - 원문 : 10 Free UI Kit

  - 원문 : 18개의 CSS Framework

  - 50개의 Web Developer를 위한 리소스와 툴들

  - Twitter Bootstrap v3 이해하기

  - Yahoo Pure에 대하여

  - HTML5 free Layout Template을 얻고 싶다면

  - UI Kit Maker

  - 유용한 CSS 도구들 (필독)

posted by Peter Note