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

Publication

Category

Recent Post

2015. 10. 22. 10:41 AngularJS



본 서적은 자바스크립트와 앵귤러 프레임워크를 프로젝트에 도입해 보려는 개발자 또는 프로토타입을 해보고 싶은 개발자 또는 사용해 본 경험이 있는 개발자를 대상으로 한다. 앵귤러에 대해 처음 공부하는 분에게는 다소 어려울 수 있으므로 기초서적을 권장드린다. 책 제목처럼 3장이후 부터 실전 프로젝트 실습을 위한 상세 소스를 GitHub에서 제공한다. 1~2장은 개념 정리이므로 읽고 나중에 참조하는 장으로 사용한다. 참고로 2장은 실습이 없이 개념을 설명하는 장이기에 소스를 제공하지 않고 있다. (소스의 Git Branch는 장이 끝나는 시점에 언급하고 있다. 먼저 전체 소스를 보고 싶다면 GitHub 내용을 참조하자)








출판의 여정 


8개월의 원고작성, 3개월의 편집기간을 거쳐 드디어 책이 출간되었다. 책을 통해 개발자들과 공유하자는 의기투합은 2013년 겨울로 거슬러 올라간다. 이강훈님을 통해 알게된 이규원님, 김충섭님, 김대권님, 박유진님, 구교준님과 함께 MEAN 스택을 기획했었다. 그 당시 미국에 있었던 지라 구글 행아웃을 통해 주말마다 화상미팅을 하며 깃헙과 페이스북을 통해 서로의 의견과 결과물을 공유했다. 2014년 봄 귀국후 함께 오프라인에서 헤커톤도 하면서 책의 샘플을 만들며 진행했지만 각자의 일정으로 인해 MEAN 스택 집필을 흐지부지 되어갔다. 2014년 7월쯤 결단을 내리고 이렇게 진행하면 끝나지 않겠다 싶어서 다음 기회를 기약하였고, 홀로 책을 쓰기로 결심했다. 


2014년 8월 이강훈님 회사인 스터디지피에서(https://bagle.io)의 서비스를 개발하면서 미국 플젝에서 얻은 AngularJS 경험과 개념정리가 덜 된 부분을 하나하나 집어가며 집필을 시작했다. 처음에는 한 페이지를 나가는데 하루의 시간이 걸리더니 조금씩 속도가 붙으면서 한달만에 100p 가량을 썼다. 하지만 다시 가을 미국 프로젝트로 2달간 집필 중단 후 한국에 돌아와 마음을 다잡고 12년전 내 책을 출판해 보자는 꿈을 다시한번 상기했다. 위키북스의 박찬규 대표님도 은근히 압력을 넣어주시며 언제쯤 나올까요 물어볼때마다 곧 나올겁니다 대답하곤 하루 1~2시간씩 짬을 내어 집필을 계속했다. 2015년 3월 집필을 완료하고 출판사에 전달했지만 피드백으로 날아온 것은 맞춤법과 문장 난해함에 대한 엄청난 지적질의 빨간줄이었다. 


한달간 주말마다 빨간 선생님과 싸움하며 "그래 글쓰는 것도 배우는 거다"라고 생각하고 나의 국어실력을 한탄하며 글을 풀어서 이야기식으로 고쳐나갔다. 사실 블로그를 쓰다보니 블릿(점, 점으로 요약한 내용)으로 내용을 요약해 표현하다 보니 책도 그렇게 썼드랬다. 블릿 내용을 다 풀어쓰는 것만도 한달. 다시 피드백받아 또 고치고 피드백받아 또 고치고, "아 무한루프의 피드백". 점점 스트레스와 화가 살짝 났다. 인생 목표 10가지중 1가지가 1년에 책한권씩 내자였던 것이 여지없이 깨지는 순간이었다. 이렇게 하다가는 2년에 한권내는 것도 힘들겠다 싶었다. 하지만 일에는 끝이 있는 법. 5월초 거의 마무리 피드백을 보내고, 책 추천사를 받고 인덱스를 보내고, 몇가지 피드백을 마무리하며 3개월의 수정작업은 6월 5일 마무리되었고, 예약 판매가 모든 온라인 서점에서 시작되었다. 그렇게 나온 것이 "실전 프로젝트로 배우는 AngularJS" 이다. 


본 서적은 AngularJS가 하도 유행이라 하여 한번쯤은 기초서적을 구매하거나 빌려서 본적이 있고, 온라인 튜토리얼을 통해 프로토타입핑을 해보았지만 당췌 AngularJS로 서비스를 어떻게 만들어야 할지 모르는 개발자를 위한 책이다. "나도 내가 원하는 서비스 뚝딱 만들어 보고 싶어 근데 어떻게 시작해야는 거야?"라고 생각하는 모든 개발자를 위한 책이다. AngularJS는 생태계를 가지고 있고 인식의 전환을 해야하는 새로운 용어들이 존재한다. 


처음 1장에서는 간단한 ToDo 서비스를 만들면서 주변의 생태계 도구와 개념을 이해하고 2장에서는 AngularJS의 장점들과 그중에 지시자(Directive)에 대해 좀 더 심도있게 설명을 했다. 3장, 4장에서 서비스를 바로 만들 줄 알았다면 오산이다. 이제 서비스를 만들 준비운동단계이다. 먼저 요건정의를 하고 이에 맞는 코딩 컨벤션 부터 AngularJS를 이용한 SPA 개발시 준비할 것들을 프론트앤드 아키텍처로 접근해 보았다. 5장에서 이제 좀 개발들어가려 했더니 그래도 요즘 많이 사용하는 NodeJS, MongoDB는 알고가자는 취지에서 개념과 실습코드를 넣었다. 1장~5장이 준비운동과 스타트 단계였다면 6장, 7장은 엄청난 스피도로 골인점을 향해가는 단계이다. 올초 2개월간 TossLab의 JANDI(http://www.jandi.com)에서 AngularJS에 대한 컨설팅 경험을 적용한 장이다. 





목차


▣ 01장: 단일 페이지 애플리케이션 개발 준비
1-1. 개발 도구 설치 
   - 깃 설치 
   - 노드 설치 
   - 요맨 설치 
   - 서브라임 텍스트 편집기 설치 

1-2. 단일 페이지 애플리케이션 생성 
   - yo generator 선택과 설치 
   - Yo를 이용한 ToDo 애플리케이션 생성 
1-3. 애플리케이션 컴포넌트 생성 
   - 앵귤러를 위한 index.html 설정 이해하기 
   - yo를 이용한 앵귤러 컨트롤러 추가 
   - bower를 이용한 앵귤러 지시자 추가 
1-4. 애플리케이션 테스트 및 빌드 
   - grunt를 이용한 테스트 
   - grunt를 이용한 배포 
정리 

▣ 02장: AngularJS 프레임워크 이해
2-1. MV* 프레임워크 
2-2. 양방향 데이터 바인딩 
   - 스코프 내부와 상속 관계  
   - MyToDo 애플리케이션에서 양방향 데이터 바인딩 
   - 스코프 생명 주기(Life Cycle) 
   - 그 외 $scope 객체 메서드 
2-3. 의존성 주입(DI, Dependency Injection) 
2-4. 클라이언트 템플릿 
2-5. 지시자(Directive) 
   - 지시자가 DOM에 적용되는 순서 
   - 지시자 정의 
   - 지시자의 스코프 객체의 범위 종류 
   - Template, TemplateUrl, TemplateCache, replace와 ng-template 사용 
   - compile, link의 $watch 등록을 이용한 양방향 데이터 바인딩 
   - controller, require와 link 네 번째 파라미터와의 관계 
   - transclude, ng-transclude 사용 
2-6. 테스트 프레임워크(단위, E2E) 
   - 카르마 기반 단위 테스트 
   - 프로트랙터 기반 E2E 테스트 
정리 

▣ 03장: 싱글 페이지 애플리케이션 기획및 생성
3-1. 애플리케이션 기획
   - 메인 페이지 
   - 그룹 정보 페이지 
   - 그룹 활동 페이지 
   - 설문 생성 페이지 
3-2. 애플리케이션 제너레이터 설계 
   - 애플리케이션의 폴더 구조 전략 
   - 애플리케이션 제너레이터 선정 
   - 앵귤러 코드 스타일 전략 
   - 스타일 가이드에 따른 제너레이터 템플릿 수정 방법 
   - IE8 지원을 위한 index.html 설정 
3-3. SPA 생성 
   - 애플리케이션의 모듈 구성 
   - 라우팅 설정 방식 
3-4. 단위 업무를 위한 앵귤러 컴포넌트 조합 
   - $resource를 통한 REST 모델 사용 
   - promise와 $q Async 호출에 대한 이해 
정리

▣ 04장: 애플리케이션을 위한 공통 프레임워크 개발
4-1. 공통 프레임워크 모듈 개발 
   - 다국어 처리 
   - 메시지 처리 
   - 팝업 메시지창 지시자 
   - HTTP 에러 처리 
   - 사용자 정의 Bower 컴포넌트 등록 
   - 로컬 저장소 서비스 
   - 유틸리티 지시자 
4-2. 로그인 화면 개발 
   - 트위터 부트스트랩 기반의 화면 디자인 및 폰트 사용 
   - 폼 유효성(Form Validation) 검사 
   - 인증을 위한 토큰과 쿠키 
4-3. OAuth를 이용한 인증 처리 
   - 백엔드에서 Passport 모듈을 이용한 인증 처리 
   - 페이스북 인증 처리 
   - 크롬 브라우저 개발자 도구를 이용한 클라이언트 디버깅 
   - 노드 인스팩터를 이용한 서버 디버깅 
정리

▣ 05장: 메인 페이지 개발
5-1. 백엔드 API 개발 
   - REST API 별 서버 모듈 조합 
   - 노드 모듈의 exports 이해 
   - 몽고디비와 몽구스 이해 
   - 서버 모델 개발 
   - 그룹 REST API 개발 
   - 포스트맨을 이용한 REST API 검증 
   - 백엔드 단위 테스트 수행 
5-2. 메인 화면 개발 
   - 공통 컴포넌트 재구성 
   - 메인 화면 레이아웃 개발 
   - 그룹 생성 
5-3. 그룹 목록 및 정보 표현 
정리 

▣ 06장: 그룹 페이지 개발
6-1. 그룹 정보 페이지 
   - 그룹 상세 정보 조회 
   - 그룹 프로필 이미지 변경 
   - 그룹 가입, 탈퇴 
6-2. 그룹 활동 페이지 
   - 그룹 활동 화면 레이아웃 개발 
   - 그룹 멤버 목록 표현 
6-3. 설문 카드 생성 
   - 설문 카드 생성 
   - 카드 지시자 개발 
6-4. 설문 종류별 카드 표현 
6-5. 설문 응답 및 결과 표현 
정리

▣ 07장: 실시간 반응 개발
7-1. Socket.IO 기반 실시간 연동 
   - 노드 기반 백엔드 Socket.IO 
   - AngularJS 기반 프런트엔드 Socket.IO 
   - 상단 알림 메뉴 추가 
7-2. 카드 목록 UX 개선 
   - 카드에 동영상 추가 
   - 무한 스크롤 적용 
   - 애니메이션 효과 적용 
7-3. AngularJS 성능 옵션 
   - 일회 바인딩 
   - ngModelOptions 지시자 
   - 디버깅 정보 비활성화 
   - $applyAsync 적용 

정리




구매


예스24 온라인 서점


알라딘 온라인 서점


인터파크 온라인 서점


교보문고 온라인 서점


G마켓 온라인 서점


현재는 예약 판매로 10% 싸게 구매 할 수 있다. 6월 17~19일간 배송을 시작한다. 당초 높은 가격이 책정되었지만 위키북스 대표님에게 가격이 높아 개발자에게 부담이 될 것 같다하였더니 파격적인(?) 가격에 판매를 시작해 주셨다. 지금까지 독자로 책을 사보면서 철자하나 틀리면 왠지 기분이 나쁘고 지적을 해주고 싶었는데 이제부터는 너그러이 용서해 줄것 같다. 정말 많은 공력과 시간이 투여되는 시간이었다. 하지만 새로운 경험을 하게 되었고 이렇게 해서 책 한권 한권이 출판된다는 생각을 하니 한권의 책을 사도 감사히 볼 마음가짐이 생긴다.  


이번달 중으로 출판사의 양해를 얻어 1장과 2장은 블로그에 공개하려 한다. 2장은 AngularJS v1.* 이 계속 사용되는 한 유용한 레퍼런스가 되리라 생각한다. 이렇게 생애 첫 책 출판을 자축하며... 내년에는 "Data Visualization using D3.js" 이고, 여기에 ReactJS 또는 AngularJS v2.* 와 Meteor를 접목한 경험을 출판할 계획이다. 글쓰기도 가끔은 중독이다. 자전거 처럼 잘 타지는 못하지만 타고 있는 동안은 자유다. 나를 표현하고 느낄 수 있는 시간이랄까~~~




소스 


책의 소스는 모두 깃헙에 있습니다. 책 챕터마도 브랜치에 대한 정보가 담겨 있습니다. 


책 소스를 위한 깃헙 그룹 : https://github.com/AngularJS-SPA-Development



posted by 윤영식
2015. 1. 7. 11:29 My Projects/Jandi

Tosslab의 잔디는 팀 그룹간에 원활한 커뮤니케이션을 위한 도구이다. 채팅 및 파일공유를 할 수 있어서 업무 커뮤케이션 툴로 사용하기에 적당하고 모바일, 웹에서 접근할 수 있다. 



잔디의 기술


  잔디는 MEAN Stack 기술을 사용하고 모바일은 네이티브로 개발한다. 업무적인 커뮤니케이션 도구는 사무실에서 웹을 통해 사용할 경우가 많아서 특히 웹에 대한 사용성이 좋아야 한다. 블로그에서는 프론트앤드 영역만을 다루도록 한다. 기존에는 AngularJS v1.2.* 기반으로 되어 있고 개발을 진행함에 따라 웹 애플리케이션의 복잡도가 증가하여서 애플리케이션을 재구성할 필요가 발생하였다. 




모듈단위 애플리케이션 구성


 애플리케이션 복잡도를 단순화 시키고 애플리케이션을 구조화하는 방안이 필요하다. 애플리케이션을 구조화 한다는 것은 거시적인 부분과 미시적인 부분의 조합으로 이루어진다. 먼저 거시적인 부분은 아키텍쳐레벨로 AngularJS의 모듈을 구성하는 것이다. 


 

  AngularJS 모듈은 컴포넌트의 그룹이다. 모듈안에는 다양한 컴포넌트를 가지고 있고 컴포넌트 성격별로 구분해 놓을 필요가 있다. 위의 예에서 가장 기본으로 있어야할 AngularJS에서 제공하는 모듈 묶음과 어느 프로젝트에서나 쓰일만한 모듈을 묶어놓은 공통 모듈이 있을 것이다. 그리고 업무적으로 추상화할 필요가 있는 업무 공통 모듈이 존재하고 업무를 처리하는 업무 모듈식으로 나눌 수 있다. 하위 모듈은 상위 모듈을 참조할 수 없고 상위 모듈만이 하위모듈안의 컴포넌트를 DI(Dependency Injection) 방식으로 사용을 할 수 있다. 




제너레이터를 통한 애플리케이션 구성


 기존의 애플리케이션을 보기하면서 다시 재구성하기 위해 무엇부터 시작을 해야할까? 


  - Yeoman Generator를 선정한다. generator-angular-fullstack을 사용할 예정이다. 

  - 자바스크립트 코딩 스타일은 구글의 자바스크립트 스타일 가이드를 따른다. 

  - AngularJS의 코딩 스타일은 존파파의 AngularJS 스타일 가이드를 따른다.


  generator-angular-fullstack의 템플릿을 존파파의 가이드에 따라 변경한 프로젝트를 만들어 본다. 불필요한 예제는 제거하고 몇가지 영역의 템플릿만을 취할 것이다. 먼저 generator-angular-fullstackgenerator-ng-component를 각각 git clone 하고 명칭을 generator-jdi, generator-jdi-component라고 바꾸고 내부의 .git폴더를 제거한 후 각 템플릿의 내용을 파파죤스 가이드에 따라서 수정을 한다. 

$ git clone https://github.com/DaftMonk/generator-angular-fullstack.git

$ git clone https://github.com/DaftMonk/generator-ng-component.git


$ mv generator-angular-fullstack generator-jdi

$ mv generator-ng-component generator-jdi-component


$ cd generator-jdi && rm -rf .git

$ cd generator-jdi-component && rm -rf .git


 파파죤스 가이드에 따라 generator-jdi-component/templates 폴더 밑의 모든 앵규러 컴포넌트를 다음과 같은 형식으로 수정한다. 

   + IIFE : Immediately Invoke Function Expression

   + 앵귤러 모듈 변수를 만들지 않음 

   + 컴포넌트의 펑션을 밖으로 뺌

   + DI 하는 부분은 ng-annotation을 이용함으로 주입받는 펑션위에 /* @ngInject */ 를 둚

(function() { 

  'use strict';


  angular

    .module('<%= scriptAppName %>')

    .controller('<%= classedName %>Ctrl', <%= classedName %>Ctrl);


  /* @ngInject */

  function <%= classedName %>Ctrl($scope) {

         

  }

})();


 수정한 내역에 대해 테스트는 다음과 같이 진행을 하면 된다.  먼저 테스트용 test 폴더를 하나 만든다. 테스트 폴더 밑에 node_modules 폴더를 만들고 밑으로 generator-jdi와 generator-jdi-component 에 대해 심볼릭 링크를 건다. 각 generator-* 폴더로 들어가 npm install을 해준다. 이제는 test밑에서 "yo jdi Jandi" 명령을 치면 애플리케이션 생성 프롬프트를 묻게 된다. 애플리케이션 템플릿은 generator-jdi/app/templates 밑에 존재하고 여기에 있는 파일도 파파죤스 스타일 가이드에 따라 수정을 해준다. 

$ mkdir test && cd test

$ mkdir node_modules && cd node_modules 


$ ln -s <fullPath>/generator-jdi  generator-jdi

$ ln -s <fullPath>/generator-jdi-component generator-jdi-component


// 둘은 같은 디렉토리에 있어야 함 

$ cd <fullPath>/generator-jdi-component && npm install

$ cd <fullPath>/generator-jdi && npm install 


$ cd test 

$ yo jdi Jandi


  각 수정한 generator-jdi, generator-jdi-component는 링크를 참조한다. 이제는 누구나 사용할 수 있도록 npm repository에 등록을 한다. npm publish 로 package.json 파일에 등록된 내역을 기반으로 배포가 된다. 따라서 clone한 generator의 버전을 따르지 않고 최초 0.0.1 버전으로 수정해 배포한다. 

$ cd generator-jdi-component 

$ npm publish

+ generator-jdi-component@0.0.1


$ cd generator-jdi

$ npm publish

+ generator-jdi@0.0.1


 이제 글로벌로 generator-jdi만을 설치해서 어디서나 "yo jdi <App명칭>" 명령을 수행해 최초 애플리케이션 골격을 만들 수 있다. 

$ npm install -g generator-jdi

$ mkdir web_tiger && cd web_tiger

$ yo jdi Jandi



'My Projects > Jandi' 카테고리의 다른 글

[Jandi] 애플리케이션 모듈 리팩토링  (0) 2015.01.21
posted by 윤영식
2015. 1. 5. 12:32 My Projects/Bagle

베이글은 그룹 컨텐츠 공유 서비스이다. 그룹 관리자는 멀티미디어 컨텐츠 및 설문 조사를 위한 다양한 내용을 개제할 수 있고 멤버는 이에 대한 답변을 하거나 댓글은 단다. 페이스북과 유사한 UI이지만 퍼블릭 SNS가 아닌 버티컬 SNS를 지향한다. 





베이글의 탄생


  이전 StudyGPS의 서비스 v1.5를 v2.0으로 개편하면서 서비스이름을 새로 공모했고 최종적으로 베이글을 선택했다. 미국에서 프로젝트를 하면서 금요일 아침마다 먹었던 베이글이 생각나서 개발팀 이름도 MondayBread로 한 마당에 베이글이 어떠냐는 의견을 내놓았는데 잘 매칭되어 채택이 되었다. 베이글은 교육을 타케팅하는 서비스이다. 주로 선생님이 그룹을 만들고 학생이 답변을 다는 형태이다. 선생님은 학생들에게 학업과 관련한 컨텐츠를 제공하면서 이에대한 설문 조사를 수행할 수 있다. 컨텐츠는 파일 첨부, 이미지, 동영상등을 게재할 수 있고 설문형식은 주관식, 객관식등의 조사와 댓글을 통해 의견을 들을 수 있다. 


  보수적인 교육시스템에서는 자유로운 퍼블릭 SNS로 쌍방향 소통 보다는 단방향의 문제를 내는 사람과 문제를 푸는 사람을 구분하여 관심분야의 그룹을 운영할 수 있도록 하는데 목적이 있다. 




베이글의 기술


  스타트업에서 인기를 누리고 있는 MEAN (MongoDB ExpressJS AngularJS Node.js) 스택을 사용한다. 모바일은 하이브리드 앱방식으로 개발을 진행하고 이를 위한 프레임워크로 Ionic을 사용한다. 그외 Sass트위터 부트스트랩도 함께 사용한다. 하이브리드 앱 개발에서 중요한 체크 포인트는 다음과 같다. 


  1) 네이티브 UX에 근접한 경험을 줄 수 있는가?

  2) 네이티브 UI 컴포넌트와 같이 잘 정의된 레이아웃이나 컴포넌트가 있는가?

  3) HTML과 Javascript의 분리해서 네이티브처럼 개발을 진행할 수 있는가?


  1)번을 위한 약간 2주간 AppGyver를 사용해 보았으나 제약 사항이 많고 특히 MPA(Multi-Page Application)으로 WebView를 정말 Multi개만큼 생성해서 운영해야 제대로된 네이티브 기능을 사용할 수 있다. 이것의 단점은 모든 뷰가 하나의 AngularJS SPA가 되버린다는 것이다.  즉, 화면별로 WebView가 개별적으로 운영되면서 나타나는 현상이다. 하지만 steroids와 scanner 앱을 통해 편한 개발환경을 제공하고 있다. 따라서 아주 간단한 App을 만든다면 AppGyver도 쓸만한 프레임워크이자 플랫폼라 생각한다. AppGyver의 supersonic 하이브리드 프레임워크도 결국 Ionic을 내부적으로 사용하고 있고 SPA 방식으로 개발하면서 네이티브 수준의 UI와 UX를 선사하는 Ionic 프레임워크를 최종 선택하게 되었다. 올해(2015)초에는 정식 v1.0이 릴리즈 될 예정이고 ionic 내부에 angular, angular-sanitize, angular-animation, ui-router를 의존관계로 가지고 있고 AngluarJS v1.3.6 최신버전을 사용하고 있다. 


 2)번은 Sass로 일정부분 디자인을 하고 트위터 부트스트랩과 같은 UI 프레임워크를 사용하면 되고 3)번은 reactive programming을 할 수 있는 AngularJS의 양방향 데이터 바인딩을 사용하면 HTML과 JS파일을 분리해 개발을 할 수 있다.  


앞으로 개발하면서 느꼈던 점과 기술적인 이슈등을 회고식으로 적어볼 예정이다. 


  

posted by 윤영식
2014. 6. 2. 10:09 AngularJS/Start MEAN Stack

프로트앤드 개발을 하면서 테스트는 어떻게 할 수 있는지 알아본다. 앵귤러에서는 테스트 관련 내용까지도 포함하고 있어서 개발 테스트의 편의성을 함께 제공하고 있다. 


사전 준비

  generator-angular를 설치하게 되면 앵귤러 프레임워크에 카르마(Karma) 테스트 프레임워크와 Mock 객체등이 포함되어 있다. 과거에는 Karma를 통해서 단위(Unit) 테스트와 E2E (End-To-End) 테스트 두가지를 하였으나, 최근에는 Karma로는 단위 테스트만을 수행하고 Protractor라는 새로운 테스트 프레임워크를 통해서 E2E 테스트를 수행한다 


  - karma.conf.js : Unit Test 환경파일. 내부적으로 Jasmine (http://pivotal.github.io/jasmine/) 프레임워크를 사용한다

  - karma-e2e.conf.js : E2E Test 환경파일 (

  - "grunt test" 를 통해 수행


Karma가 Jasmine 테스트 프레임워크를 사용하고, 필요한 files 목록을 정의하며 Chrome 브라우져에서 8080 포트를 사용하여 테스트함. angular.js 와 angular-mocks.js 파일은 필 첨부 (angular-mocks.js 안에 module, inject 펑션 존재)

// karma.conf.js

// Karma configuration

// http://karma-runner.github.io/0.10/config/configuration-file.html


module.exports = function(config) {

  config.set({

    // base path, that will be used to resolve files and exclude

    basePath: '',


    // testing framework to use (jasmine/mocha/qunit/...)

    frameworks: ['jasmine'],


    // list of files / patterns to load in the browser

    files: [

      'app/bower_components/angular/angular.js',

      'app/bower_components/angular-mocks/angular-mocks.js',

      'app/bower_components/angular-resource/angular-resource.js',

      'app/bower_components/angular-cookies/angular-cookies.js',

      'app/bower_components/angular-sanitize/angular-sanitize.js',

      'app/bower_components/angular-route/angular-route.js',

      'app/scripts/*.js',

      'app/scripts/**/*.js',

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

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

    ],


    // list of files / patterns to exclude

    exclude: [],


    // web server port

    port: 8080,


    // level of logging

    // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG

    logLevel: config.LOG_INFO,


    // enable / disable watching file and executing tests whenever any file changes

    autoWatch: false,

    // Start these browsers, currently available:

    // - Chrome

    // - ChromeCanary

    // - Firefox

    // - Opera

    // - Safari (only Mac)

    // - PhantomJS

    // - IE (only Windows)

    browsers: ['Chrome'],


    // Continuous Integration mode

    // if true, it capture browsers, run tests and exit

    singleRun: false

  });

};


  Gruntfile.js 의 test Task 내역 : grunt test 

// Test settings

karma: {

   unit: {

      configFile: 'karma.conf.js',

      singleRun: true

  }

}


... 중략 ..

grunt.registerTask('test', [

    'clean:server',

    'concurrent:test',

    'autoprefixer',

    'connect:test',

    'karma'

  ]);



1. Jasmine 테스트 프레임워크 

  describe ~ it 으로 BDD를 표현한다. BDD는 TDD의 2세대 버전 정도로 이해하자. 보다 자세한 사항은 위키 참조 (http://en.wikipedia.org/wiki/Behavior-driven_development

 describe("A suite", function() {

  it("contains spec with an expectation", function() {

    expect(true).toBe(true);

  });

});


 describe로 시작하는 것을 Suite (슈트)라고 부르고, it 으로 시작하는 것을 spec (스펙)이라고 부른다. 슈트안에 여러개의 스펙이 있을 수 있다. beforeEach(), afterEach()를 통해서 스펙 수행시 마다 환경 초기값 셋업(Setup)이나 마무리 작업(TearDown)을 수행할 수 있다. 스펙 안에는 expect 기대값에 대한 다양한 메서드를 제공한다.

toEqual()

toBe(), not.toBe()

toBeTruthy(), toBeFalsy()

toContain(), not.toContain()

toBeDefined(), toBeUndefined()

toBeNull()

toBeNaN()

toBeGreaterThan()

toBeLessThan()

toBeCloseTo() : 소수점

toMatch() : 정규표현식

toThrow()

 ...


  보다 자세한 사항은 Jasmine 문서를 참조한다 (http://jasmine.github.io/2.0/introduction.html)



2. 앵귤러 Service 테스트 준비

  yo를 통하여 앵귤러 코드를 완성하게 되면 자동으로 테스트 코드까지 생성된다. SessionService에 대한 테스트 코드는 /test/spec/services 폴더 밑에 동일한 session-service.js 파일이 존재한다. module 호출 후에 inject 가 올 수 있으나 inject 후에 module 호출은 올 수 없다. _ (underscore)를 양쪽에 붙이는 것은 angular프레임워크에 테스트를 위하여 해당 서비스를 사용함을 알려준다. 

describe('Service: SessionService', function () {

  // load the service's module

  beforeEach(module('meanstackApp'));


  // instantiate service

  var SessionService;

  beforeEach(inject(function (_SessionService_) {

    SessionService = _SessionService_;

  }));


  it('should do something', function () {

    expect(!!SessionService).toBe(true);

  });


});

 

 grunt test 명령을 수행하면 에러가 발생할 것이다. 그것은 index.html에 필요에 의해 추가한 .js파일을 Karma 환경파일에 추가하지 않았기 때문이다. controller 쪽에서도 에러가 발생하는데 기본 테스트 코드에서 발생하므로 불필요한 코드를 삭제한다. 

 // karma.conf.js 

  files: [

      'app/bower_components/jquery/jquery.js',

      'app/bower_components/bootstrap/dist/js/bootstrap.js',

      'app/bower_components/angular/angular.js',

      'app/bower_components/angular-mocks/angular-mocks.js',

      'app/bower_components/angular-resource/angular-resource.js',

      'app/bower_components/angular-cookies/angular-cookies.js',

      'app/bower_components/angular-sanitize/angular-sanitize.js',

      'app/bower_components/angular-route/angular-route.js',

      'app/bower_components/angular-bootstrap/ui-bootstrap-tpls.js',

      'app/bower_components/angular-ui-router/release/angular-ui-router.js',

      'app/scripts/*.js',

      'app/scripts/**/*.js',

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

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

    ],


  singleRun: true


// Controller 테스트 파일에서 불필요한 하기 코드 삭제 

  it('should attach a list of awesomeThings to the scope', function () {

    expect(scope.awesomeThings.length).toBe(3);

  });



3. 앵귤러 Service 테스트를 위한 Mock 사용

  SessionService 테스트 수행전에 공통적으로 사용하고 있는 Factory나 Service에 대한 테스트 코드를 작성한 후 SessionService를 최종 테스트 한다  


  - msRequestFactory : 원하는 JSON 객체가 생성되는지 테스트 

  - msRestfulApi : $httpBackend 이용하여 요청 처리 테스트

  - SessionService : $httpBackend 이용하여 요청 처리하고 SessionInfo의 정보 유무 테스트 


msRequestFactory 통하여 만들어진 JSON 객체가 원하는 key:value 값을 가지고 있는지 확인한다. expect().toEqual()을 사용한다 

 'use strict';


describe('Service: msRequestFactory', function () {


  // load the service's module

  beforeEach(module('meanstackApp'));


  // instantiate service

  var msRequestFactory;

  beforeEach(inject(function ($injector) {

    msRequestFactory = $injector.get('msRequestFactory');

  }));


  it('should exist', function () {

    expect(!!msRequestFactory).toBe(true);

  });


  it('should make JSON', function () {

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

    expect(request).toEqual({

      'area' : 'user',

      'resource' : 'login',

      'id' : '',

      'request' : {}

    });

  });


});

  

msRestfulApi는 $resource를 통해 서버에 요청을 보낸다. 서버 요청에 대한 Mock을 만들어서 응답을 줄 수 있는 기능으로 앵귤러는 $httpBackend를 제공한다. 즉, 실서버가 없을 경우 XHR 또는 JSONP 요청없이도 실서버에 요청한 것과 같은 응답을 줄 수 있다. $httpBackend의 유형은 두가지 이다


  - request expectation : 요청 JSON 데이터의 검증 원하는 결과가 왔는지 설정. expect().respond()

  - backend definition : 가상의 응답정보를  설정. when().respond()


서버의 요청은 모두 async 처리가 되므로 테스트코드 작성하기가 애매하다. 따라서 $httpBackend.flush()를 호출하는 시점에 request가 전달되므로, 테스트 코드 작성시 적절한 위치에 놓아야 한다. API 참조 (http://docs.angularjs.org/api/ngMock.$httpBackend)

// 로그인 서비스 테스트에서 계속 에러가 발생하여 Re-Thinking 중이다. 

// 에러 코드 

'use strict';


describe('Service: msRestfulApi', function () {


  // load the service's module

  beforeEach(module('meanstackApp'));


  // instantiate service

  var msRestfulApi, httpBackend, scope, resource;

  beforeEach(inject(function ($injector) {

    msRestfulApi = $injector.get('msRestfulApi');

    httpBackend = $injector.get('$httpBackend');

    scope = $injector.get('$rootScope');

    resource = $injector.get('$resource');


    httpBackend.when('POST', '/api/v1/user/login').respond(200, {'id': 1});

    

  }));


  it('should call test', function() {

    var request = {

      'area' : 'user',

      'resource' : 'login',

      'id' : '',

      'request' : {}

    };


    msRestfulApi.login(request, function(response){

      expect(response.id).toBe(1);

      console.log(response);

    }, function(error){

      console.log(error);

    });


    httpBackend.flush();

    expect(r.id).toBe(1);


  });


});

  


<참조>

  - Jasmine 문법

  - Jasmine 소개

  - Karma 테스트 하기

  - Protractor Seed 소개 : Protractor WebDriver 업데이트 

  - WebDriverJS 소개 

posted by 윤영식
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 윤영식
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 윤영식
2013. 11. 25. 09:36 AngularJS/Start MEAN Stack

MEAN Stack을 가지고 서비스를 만드는데 사용을 하려면 어떤 순서로 익혀야 할까? 각각의 기술을 따로 배우는 것은 어차피 전문서적들이 있기 때문에 여기서 다시 상세히 설명하는 것은 의미가 없을 것 같고, 서비스를 기획, 개발, 런칭까지의 과정을 알아보자. 모든 과정의 내용은 웹 서비스쪽에 공개되어 공유될 것이다 




Sprint-1) Full Stack 개발자 되기

  - MEAN 개념과 개요

    + AngularJS

    + NodeJS + ExpressJS

    + MongoDB + Redis(Advanced)

  - MEAN 이외에 알아야 할 것들

    + JavaScript 

    + Bootstrap (or Zurb Foundation

    + NodeJS Modules

    + Mongoose



Sprint-1) 개발 준비 단계

  - 로컬 개발 환경 만들기 

    + Vagrant + Linux 환경 구성하기

    + 개발환경에 Node.js, MongoDB 설치하기 

    + Trello 에서 Scrum 개발하기 

  - MEAN Stack 개발도구 개발환경에 설치하고 익히기 

    + Yeoman : 클라이언트 라이브러리관리 및 배포 Grunt, Bower, Yo 설치하기 

    + NPM : 서버 라이브러리 관리

  - Git & GitHub 저장소 만들기 

    + Git Branch 전략 

    + GitHub 사용방법

  - 클라우드 환경에 배포하여 테스트 하기

    + Cloud Development(Nitrous.io) 를 통한 협업개발 및 테스트하기

    + Heroku같은 PaaS에 배포하여 테스트하기 

 


Sprint-1) MEANK Stack 개발 환경 구성하기

  - SRS 작성하기 

    + Software Request Requirement란 무엇인가?

    + Trello 에 소프트웨어 요구사항 정의서 작성하기 

    + Balsamiq 도구 사용하여 Wireframe & Mockup 그리기 화면 그리기

  - AngularJS Scaffolding 코드 사용하기 

    + angular-generator 알아보기 

    + angular 기본골격 만들기

    + NodeJS로 테스트 하기 

  - MEAN Stack 테스트 전략 

    + AngularJS에서의 테스트 방법 : Karma Framework

    + NodeJS에서의 테스트 방법 : Mocha 그외 



Sprint-2) 서비스 프로토타입 개발하기

  - 가장 중요한 화면 설계하기

    + 클라이언트 화면 Mockup 구체화 하기 

    + NodeJs에서 처리 할 RESTful API 설계하기 

    + MongoDB 에 저장 할 Document Schema 설계하기

  - MongoDB Store 개발 및 테스트

    + Dynamic Schema, Embedded vs Reference 에 대한 이해 

    + Mongoose 기반으로 Schema 만들기

    + Test Framework 이용한 Model 단위 테스트하기 

  - Node.js 기반 RESTful API 개발 및 테스트

    + RESTful API 코드 뼈대 만들기 in NodeJS

    + Test Framework 이용한 서비스 단위 테스트하기

    + RESTful Client 테스트 도구 또는 curl 로 호출 테스트하기

  - Angular.js 기반 화면 개발 및 테스트

    + AngularJS와 Bootstrap 이용하여 화면 구성하기

    + AngularJS 애플리케이션 구조 개발 및 RESTful API 서비스 개발 

    + Test Framework 이용한 화면 단위 테스트하기 

  - Heroku 같은 PaaS에 올려서 통합 테스트 하기 



Sprint-3) 서비스 개발 반복하기

  - 반복하기 

    + sprint-01에서 반복하였던 과정을 지속하기 

  - 메인 및 로그인 화면 만들기 

    + Bootstrap의 다양한 도구 활용

    + OAuth 기능 로그인 하기  

  - 채팅기능 만들기 

    + 채팅 Mockup 만들기

    + NodeJS Socket.io API 구현

    + MongoDB 채팅 Schema 설계

    + AngularJS Socket.io 멀티 케스팅 구현 

    + Bootstrap 통한 화면 구현 및 AngularJS 연동

 - Heroku 같은 PaaS에 올려서 프로토타입 테스트 하기 



Sprint-3) 서비스 런칭하기

  - 서비스 일일 빌드시스템 구성하기 

    + Travis (또는 Jenkins)기반 자동 배포 기능 만들기 

    + Travis 에서 빌드된 것을 클라우드시스템(PaaS) 자동 배포하기 

  - 런칭 페이지 만들기 

    + GitHub Pages를 이용한 런칭 만드는 방법

    + Bootstrap을 이용하여 런칭 페이지만들어 GitHub에 반영하기 

  - 모니터링 하기

    + 클라이언트 AngularJS Log 모니터링 하기
    + NodeJS Log 모니터링 하기 

    + MEAN Stack 운영 및 장애 모니터링 전략 



Next Project) MEAN Stack Advanced

  - 채팅기능 고도화하기 

    + SNS 서비스 아키텍쳐들 알아보기  

    + 대용량 데이터를 처리를 위한 아키텍쳐 설계하기

  - NodeJS + Redis + MongoDB 연동하기 

    + Redis 알아보기 

    + NodeJS-Redis 연동하기 

  - 정제된 데이터 RDBMS와 연동하기

    + NodeJS - MySQL 연동하기 

    + MongoDB의 MapReduce를 이용하여 데이터 마이닝하기 

    + NodeJS에서 MongoDB 마이닝 데이터 MySQL에 넣기 

  - SNS 서비스 운영 고도화

    + NodeJS와 Nginx 연동하기 

    + NodeJS를 Proxy 서버로 만들어 Clustering 하기

    + MongoDB Sharding 구조 만들기 

    + Redis Master/Slave구조 만들기 

  - 유지보수 전략

    + 테스트 환경과 프로덕션 환경의 재구성

    + NodeJS Scale-out 확장 전략

    + MongoDB Sharding 및 Data Backup 전략 

    + Redis Scale-out 전략 

  - 빅데이터의 활용

    + 개인화를 위한 데이터 활용 방안

    + MongoDB Integration Framework을 이용한 MapReducing


전체 서비스를 만들고 런칭하기까지 참 많은 것들을 익히고 개발해야 하지만, 누군가에게 의미있는 가치를 줄 수 있는 SNS(Social Network Service)를 할 수 있다는 것에 만족을 느낀다. 


posted by 윤영식
2013. 11. 20. 10:58 AngularJS/Start MEAN Stack

갑작이 떠오른 아이디어를 모바일 서비스로 빠르게 만들고 싶다. 클라이언트/서버/스토어 이런 것을 해줄 사람은 없다. 나 홀로 만들어 보고 프로토타입핑해서 스타팅해보고 싶을 때 MEAN Stack을 사용하자 




1. 스프링같은 "WebApp Framework" 에 대한 고민

  클라이언트단의 Android 또는 iOS 네이티브 코드는 익히는데 시간은 없고, 다양한 스마트 기기에 대응을 했으면 한다. 그리고 JavaScript를 어느 정도 할 수 있다. 이때 생각할 수 있는 대안이 "웹앱 사이트" 또는 "모바일 웹앱"을 만드는 것이다. 그러나 예전 jQuery 코드를 생각해 보자. 조금만 복잡해 지면 쉽게 스파게티가 되고, 테스트 코드는 엄두도 못내며 결국 유지보수의 골칫덩어리로 남게 된다. 

그렇다면 자바에서 Spring Framework과 같이 DI가 되면서 MVC처럼 모듈화 개발을 가능하게 해주는 클라이언트단 프레임워크는 없을까? 처음엔 Backbone.js를 살펴 보았고, 예로 Trello와 같은 서비스에서 사용을 하고 있다. 하지만 코드의 길이가 길어지고 불필요한 중복코드의 남발이 야기되어 좀 더 간결하면서 예전 Flex Framework과 같은 것은 없을지 고민해 보았다. HTML을 확장해서 사용해도 자동 해석을 하여 브라우져가 이해할 수 있는 HTML로 바꾸어 주면 되는 그런 프레임워크 말이다. 역시 구글 형님들도 똑같은 고민을 하고 프로젝트에 적용하고 놀라운 효과를 본 새로운 프레임워크를 공식 런칭하게 되니 이름하여 AngularJS





2. 새로운 "I/O Machine" 에 대한 고민

  서비스에서 요하는 기능중 가장 중요한 부분은 Push 기능이 아닐까 싶다. 실시간으로 변화하는 정보를 수많은 유저에게 바로 Push 서비스해줄 수 있는 서버가 필요하다. 하지만 Push를 위하여 별도의 Port를 사용하긴 싫고 웹앱이므로 요청하였던 HTTP Port를 사용하고 싶다면 어떤 것을 선택해야 할까?

서버단이 Java라면 vert.x도 고려해 볼 만하다. 하지만 아직 레퍼런스와 문서가 부족하다. 하지만 여기에 우리에게 구세주와 같은 세로운 I/O 머신이 나왔으니 V8엔진을 기반으로 돌아가는 Node.js이다. Node.js를 사용하는 가장 큰 목적중 하나가 Socket.io와 같은 이벤트 기반의 Push 모듈들이 아닐까 생각이든다. Node.js는 모듈기반으로 필요한 것들을 추가하여 사용할 수 있다. 이를 위하여 NPM(Node Package Manager)이 존재하고 방대한 모듈과 레퍼런스들이 존재한다. 또한 Chrome에 적용된 V8 자바스크립트 해석기를 달고 있으므로 모든 코딩은 JavaScript 만으로 이루어진다





3. 스키마에 자유로운 "Store"에 대한 고민 

  서비스를 빠르게 만들고 싶은데 개발을 진행하다 보면 자주 테이블이 변경되고 이에 맞추어 서버코드들도 수정을 해주거나 새롭게 테이블을 생성해 주는 번거로운 작업들을 거친다. 따라서 스키마가 자유로운 데이터베이스를 원한다. 그리고 서비스이므로 사용자의 데이터를 많이 쌓아 두고 싶다. 이를 통해 향후 마케팅 용으로 사용하려 한다. 이왕이면 클라이언트/서버가 모두 자바스크립트이므로 데이터베이스 핸들링도 자바스크립트 이면 좋겠다. 이를 위해 새롭게 나온 NoSQL 이 있으니 이름하여 MongoDB. JSON형태로 데이터를 저장하고 정규화 없이 도큐먼트 형태로 저장을 할 수 있어서 스키마의 제약이 없다. 또한 테라급의 데이터 정도는 MongoDB를 Sharding구조로 만들어 MapReduce를 이용하여 처리할 수 있다. 그리고 Node.js 처럼 V8 해석기를 탑재하여 데이터 핸들링을 JavaScript로 할 수 있다. 마치 Oracle의 SqlPlus에서 Sql을 수행하듯 MongoDB가 정의한 형식의 쿼리를 수행할 수 있다. 





4. Node.js를 기업용 WAS(Web Application Server)로 만들기  

  초창기 Java를 떠올려 보자. 처음엔 Applet에 열광하여 업무를 애플릿으로 만든적이 있다. 그때 서버단은 Apache + PHP정도 였을 것이다. 그리고 SUN에서 J2EE 스팩을 발표하면서 Tomcat, JBoss, WebSphere, WebLogic 같은 WAS가 나왔으며, 이 또한 C계열의 TP-Monitor처럼 Java기반 I/O Machine 이 나오게 된 것이다. Java진영의 WAS는 나름의 공통된 스팩을 기반으로 웹을 이끌어 갈 수 있는 새로운 I/O 머신으로 떠올랐고, 기업에서 요구하는 Transaction 처리에 대한 스팩도 제안하여 많이 사용하게 되었다. 최근에는 Tomcat같은 Servlet 엔진만 갖춘 WAS에 Spring Framework를 사용하면 충분히 기업용 Transaction을 처리하는 업무를 개발할 수 있는 환경까지 오게 되었다. 그러나 Java의 WAS가 web1.0의 중심 머신이었다면 Node.js기반의 WAS가 web2.0의 중심 머신으로 되기 위한 조건은 무엇일까?

  결국 Node.js위에 올라가는 Java의 J2EE 스팩과 같은 견고한 스팩이 필요하다. 현재까지는 아직 그러한 스팩이 나오진 않으나 Java의 Servlet 스팩에 비견할 만한 프레임워크가 나왔으니 그것이 Express.js 이다. 이제 web2.0의 I/O 머신은 Node.js가 될 것이고, 여기에 웹에 대한 표준 스팩구현체는-사실 Node.js를 위한 공통된 스팩은 없다- 아니지만 가장 많이 사용하는 Express.js를 통하여 Node.js가 Tomcat과 같은 기능을 보유하게 되었다.




Angular.js -> Node.js + Express.js -> MongoDB 를 사용하게 되면 

  - JavaScript로 모두 개발을 한다. 클라이언트/서버 분리하여 개발할 필요가 없다. 그냥 FullStack JavaScript개발을 하면 된다

  - 클라이언트 개발이 MV* 개발이 되어 서버 개발에 익숙한 개발자가 쉽게 접근할 수 있다 

  - 서버에서 Push 기능을 쉽게 적용할 수 있으므로 다양한 서비스의 응용이 가능하다 

  - 자바의 서블릿 엔진 구현체인 Tomcat과 같이 충실한 RESTful I/O 머신을 쉽게 구현할 수 있다

  - 서버의 멀티 쓰레드 문제에서 해방될 수 있다. Asynch의 세상에서 놀자 

  - 데이터 스키마를 미리 정의하지 않고 에자일하게 개발을 진행할 수 있다

  - 큰 규모의 데이터를 저장하고 처리 할 수 있다  



그러나 우리에게 남은 과제는 이것들을 어떻게 배우고 사용하는지 잘 모른다는 것이다. 각자를 심도 있게 배우는 것은 별도의 가이드북을 따로 보길 바란다. 대신 여기서는 실전 서비스를 어떻게 하면 빠르게 만들 수 있을지 노하우를 적어보려한다. 낮에는 회사에서 업무를 보면서 향후 스타트업을 준비하는 개발자 또는 사람들에게 좀 더 이로운 가치를 전달해 주고 싶은 개발자가 3개월안에 개발할 수 있는 서비스를 이 책을 통해 런칭할 수 있는 것이 가능해 질 수 있길 바란다. 그래서 책을 쓰면서 동시에 작은 서비스를 만들어 보려 한다. 책은 가이드이고 그물일 뿐이다. 실제 돌아가는 서비스가 물고기이다. 즉, 실제 서비스를 낚지 못하면 이 책의 가치도 없다고 생각한다. 실제로 이것을 증명해 보이고 싶다. 



Getting Start MEAN

          


posted by 윤영식
2013. 9. 30. 16:57 My Services/Smart Visualization

차트는 많은 정보를 내포한다. 사람들끼리 차트를 공유하고 싶을 경우 보통은 엑셀에서 작업을 하고, 보고서를 MS-Word 또는 HWP로 만들어서 메일을 보내거나 채팅창으로 파일을 전송하게 된다. 여러 복잡한 과정을 통하여 자신의 데이터에 대하여 의미를 내포한 차트로 공유하기까지의 과정이 너무 길고 많은 시간이 소요된다. 그리고 지속적인 관심을 가진 데이터라면 이들에 대한 히스토리는 개별 문서마다 보관이 될 것이고, 합쳐서 보고자 할 때 번거로운 재작업을 거쳐야 한다. 우리가 사용하는 페이스북이나 기존 SNS 사용방식처럼 데이터를 시각화한 차트에 대해 공유하여 볼 수 있고, 코멘트를 달고 관련 차트끼리 연결지어서 더 낳은 정보로 만들어 갈 수 있으면 얼마나 재미있을까? 관련성있는 차트들을 모아 보면서 회의에 사용하거나 바로 스마트 기기에서 확인함으로써 업무 효율화를 가져올 수도 있다


이러한 가치하에서 Smart Visualization에 대한 목업화면을 그려 보았다. 



Share Place 

  - Search 입력에 어느 그룹, 코멘트, 의견등의 내용을 검색하여 관련있는 차트 목록을 볼 수 있다

  - 공유하는 공간이다. 차트별로 상단에 자신의 것인지, 어느 그룹과 공유되는 것인지 보이고 등록 시간이 나온다 

     Place : personal 또는 group-01

  - 상단 Place 또는 시간을 클릭하면 해당 차트에 대한 상세 내역을 보여주는 화면이 나온다 

  - 차트 하단에는 해당 차트에 대한 Comment와 관련 차트(Related Chart)를 연결지을 수 있다 

  - 상단 우측에는 현재 채팅진행중이 확인되지 않은 내역 건수와 메세지 건수를 Badge 로 보여준다



Register Place

  - 일정 형식의 .csv 파일을 업로드한다 

  - 해당 차트의 Place (속할 그룹)과 차트 종류를 선택하고 등록한다 



Slide Menu

  - "Smart Visualization" 좌측의 아이콘을 클릭하면 메뉴가 나온다 

  - 개인 또는 그룹을 검색하여 친구맺기를 할 수 있고, 상대가 응답을 해주면 상호 연결이 되어 Share Place에서 차트를 공유한다 

  - 기존 대화목록이나 메세지를 열람할 수 있다

  - 메뉴 상단에 "사용자"검색이나 "그룹" 검색이 들어가겠다

데이터 시각화 차트의 공유에 벗어나는 레이아웃은 제거할 것이다.



* 개념이 유사한 사이트

  https://plot.ly/


  https://infogr.am/


posted by 윤영식
prev 1 next