들어가며

자, 오늘도 TDD에 대한 얘기입니다. :-)


Mars(마르스 or 마즈, 마스 아님!!) version은 ionic2를 지원하죠.

ionic2는 Angular2를 근간으로 하고 있으며, Angular2의 문법은 Typescript입니다. (꿈과 같은 Google과 Microsoft의 콜라보레이션?)

Mars 미국식 [mɑ:rz] 발음 듣기 영국식 [mɑ:z] 발음 듣기 중요

화성


Mars [maʀs] 발음 듣기 

1. 마르스 2. 화성


이런 새로운 환경 하에서 우리는 또 다시 어떻게 하면 TDD를 할 수 있을지를 심각하게 고민해 보아야 합니다.


간단하게 ionic2 test with jasmine으로 검색해 봤습니다.


AngularJs를 공부하면서 신세를 졌던 조쉬 모로니(joshmorony) 사이트가 나오네요.

들어가서 읽어보고 테스트를 해 봅니다.

https://www.joshmorony.com/how-to-unit-test-an-ionic-2-application/


으흠...

테스트 환경을 구축하는 방법이 분리된 post에 정리되어 있네요.

https://www.joshmorony.com/introduction-to-testing-ionic-2-applications-with-testbed/


엄청 깁니다! 그렇지만 포기하지 않고 열심히 따라해 보았습니다.

그런데 이런 된장!! 안되네요...


예제에서 import 하는 일부 class들이 현재와 조금 달라 보입니다.

Angular2 guide에서 찾아보니 이제는 사용하지 않는다네요... OTL


open source라는 게 이렇게 위험합니다 여러분~!! (!true)


...


잠깐 정신을 깨끗하게 하려 해우소에 다녀왔습니다.


어떻게 구글신에게 물으면 원하는 답변을 얻을 수 있을 지 잠깐 고민해 봅니다.

Simple is best!!


'ionic2 unit test' 으로 검색합니다.


ionic2 forum이 보입니다. 아하!!

들어가서 열심히 뒤져봅니다.


건졌습니다!!

http://roblouie.com/article/376/ionic-2-set-up-unit-testing-the-best-way/


몇 가지 수정해야 할 부분이 있지만, guide 거의 그대로 test 가능한 project를 만들 수 있었습니다. :D



Install ionic2

당연히 ionic2 app이 있어야 겠지요.

http://ionicframework.com/getting-started/

물론, 그 전에 node.js가 필요합니다. (# => LTS version을 다운로드 받아 설치해 줍시다.)


install ionic

npm install -g cordova ionic

ionic에는 cordova가 필요하죠. 한 번에 같이 설치하는 명령입니다.


start an app

ionic start {appname} blank

빈페이지를 시작페이지로 갖는 ionic app을 만듭니다. {appname}으로 folder가 생기고 그 아래에 필요한 파일들이 설치됩니다. (대략 150MB)


run your app

ionic serve

app folder로 이동해서 명령어를 실행하면 빈 페이지를 가지는 app이 실행됩니다.



Setup for test

먼저 karma를 설치해야 합니다.

(app folder로 이동해서 실행해주세요.)

npm install -g karma-cli

-g option은 global하게 쓰게 설치하는 option입니다.


그 뒤 테스트에 필요한 module들을 추가로 설치합니다. 

npm install --save-dev @types/jasmine@2.5.41 @types/node html-loader jasmine karma karma-webpack ts-loader karma-sourcemap-loader karma-jasmine karma-jasmine-html-reporter angular2-template-loader karma-chrome-launcher null-loader

뭔가 엄청나게 많은 모듈들이 필요하네요.

가만히 보면 webpack, karma, jasmine 이런 녀석들입니다.


karma를 실행하기 위해서는 몇 가지 configuration이 필요합니다.

보통 config.js파일을 미리 만들어 두고, 이를 로드해서 테스트를 수행하는 형태를 띕니다.

아래 파일을 다운로드 받아서, project folder에 'test-config'라는 folder를 만들고 그 아래에 붙여 넣으세요.


test-config.zip


앞으로 우리가 만들 test code는 실제로 배포를 할 성질의 것이 아니므로, tsconfig.json파일을 수정해서 exclude하도록 합시다.

"exclude": [
"node_modules",
"src/**/*.spec.ts"
],


준비는 모두 마쳤습니다.

이제 test가 잘 되는지 test해 보죠. :-)


karma start test-config/karma.conf.js

참조한 post에서는 해당 명령어를 package.json파일에 선언해 두고, npm test라는 명령어로 수행했는데, 왠지 저는 안되더라고요. 혹시 solution을 아시면 댓글 남겨주세요.


문제 없이 세팅이 되었다면 아래와 같은 메시지를 보실 수 있을 겁니다.

webpack: Compiled successfully.

webpack: Compiling...


그러면, 이제부터는 unit test code를 만들어 보겠습니다.



Write a test

module로 구성된 Angular2의 기능들 중에서 Service라는 녀석은 공용으로 사용할 library같은 녀석입니다.

그런 역할을 가진 만큼 다른 어떤 module 보다도 테스트의 우선순위가 높은 녀석이라고 생각합니다.


그래서 간단한 기능을 가진 serivce를 만들고, 이를 테스트해 보겠습니다.

(Component module은 test하기 어려워서 대는 핑계입니다...)


파일의 위치는 아래와 같습니다.



book.service.ts

import { Injectable } from '@angular/core';

@Injectable()
export class BookService {

constructor() { }

public greeting(): string {
return 'hello';
}
}

greeting이라는 function을 호출하면 'hello'를 반환하는 기능 밖에는 없는 단순한 service입니다.

실제 service는 보통 ajax call 등을 수행하고 그 결과를 반환하는 역할을 하겠지요.

우리의 경우는 native call이 될 수도 있고요.


Angular2의 서비스는 어디에든 가져다 쓸 수 있게 끔 @Injectable이라는 데코레이션을 달아줘야 합니다. (.net에서는 비슷한 용도의 코드를 attribute라고 불렀던 것 같은데)


book.service.spec.ts

import { BookService } from './book.service';

describe('Service: BookService', () => {
let service : BookService;

beforeEach(() => {
service = new BookService();
});

it('should be create', () => {
expect(service).toBeTruthy();
});

it('should be say hello', () => {
expect(service.greeting()).toEqual('hello');
});
});

BookSerivce를 참조하기 위해서 import해주었습니다.

서비스는 test 이전에 매번 새로운 instance를 만들도록 beforeEach구문을 통해서 생성해 주었고요.


여기 두 개의 test가 있습니다.

하나는 service가 제대로 instance화 되었는 지를 먼저 확인합니다.

다른 하나는 greeting function이 'hello'를 반환하는 지 확인합니다.


결과가 다음과 같이 나왔습니다.




참고로, test code는 대상 source code와 같은 folder에 위치하고, 그 파일의 이름을 .spec.ts이라고 명명합니다.

왜냐하면, test-config/karma-test-shim.js 파일에 그렇게 찾아서 테스트하라고 정의했거든요.

var appContext = require.context('../src', true, /\.spec\.ts/);


저는 이제부터 M모 방송사의 POC 앱을 만들러 갑니다.


References

http://roblouie.com/article/376/ionic-2-set-up-unit-testing-the-best-way/

'Javascript' 카테고리의 다른 글

Implement base controller  (0) 2017.07.26
Service workers.  (0) 2017.07.06
npm lite-server  (0) 2017.07.06
Calculate distance between two geolocations.  (1) 2017.06.28

NY** project를 리뷰하려고 이전 메일을 찾아보던 중에, project 진행 간 인상적이었던 부분들을 정리했던 내용이 있어 다시 한 번 공유합니다.


abstract class를 상속(inherits)하여 구현하는 예제입니다.

Controller 생성자에 $controller를 전달한 뒤 하이라이트 코드와 같은 형태로 상속이 가능합니다.

$scope injecting하기 위해서 두 번째 param를 전달합니다.

이를 이용하면 duplicate code between controllers 문제를 풀 수 있습니다.

 

'use strict';
angular.module('Diary')
// base controller containing common functions for add/edit controllers
.controller('Diary.BaseAddEditController',
['$scope', 'DiaryService',
function ($scope, DiaryService) {
$scope.diaryEntry = {};
$scope.saveDiaryEntry = function () {
DiaryService.SaveDiaryEntry($scope.diaryEntry);
};
// add any other shared functionality here.
}])
.controller('Diary.AddDiaryController',
['$scope', '$controller',
function ($scope, $controller) {
// instantiate base controller
$controller('Diary.BaseAddEditController', { $scope: $scope });
}])
.controller('Diary.EditDiaryController',
['$scope', '$routeParams', 'DiaryService', '$controller',
function ($scope, $routeParams, DiaryService, $controller) {
// instantiate base controller
$controller('Diary.BaseAddEditController', { $scope: $scope });
DiaryService.GetDiaryEntry($routeParams.id).success(function (data) {
$scope.diaryEntry = data;
});
}]);


Reference

http://jasonwatmore.com/post/2014/03/25/angularjs-a-better-way-to-implement-a-base-controller

'Javascript' 카테고리의 다른 글

Ionic 2 Unit test  (0) 2017.07.31
Service workers.  (0) 2017.07.06
npm lite-server  (0) 2017.07.06
Calculate distance between two geolocations.  (1) 2017.06.28

AppCache(Application Cache)를 적용할 때, 가끔씩 석연치 않은 동작이 발견될 때가 있다는 제보가 들어왔습니다.

하여, AppCache를 사용할 때의 유의점 또는 자체적인 문제가 있는 지를 알아보겠습니다.



AppCache?

AppCache는 웹어플리케이션을 오프라인에서 동작할 수 있도록 지원하기 위해서 개발된 메카니즘입니다.

이 기술을 적용함으로써 다음과 같은 이점을 가질 수 있습니다.


오프라인 브라우징: 사용자가 오프라인인 상태에서도 페이지를 전환할 수 있다.

속도: 캐시된 리소스는 로컬에 위치하며, 더욱 빠르게 로드된다.

서버 부하 감소: 서버에서 변경된 리소스만을 다운로드 한다.


자세한 내용은 아래의 링크를 참조하세요.

https://developer.mozilla.org/ko/docs/Web/HTML/Using_the_application_cache

https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache


여기에 중요한 메시지가 있는데, 영문 페이지에서만 보여집니다. -_-;;

그 내용은 아래와 같습니다.

Deprecated

This feature has been removed from the Web standards. Though some browsers may still support it, it is in the process of being dropped. Avoid using it and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.

AppCache는 웹표준에서 삭제되었으며, 아직까지는 많은 browser가 이를 지원하지만 점차 퇴출될 것이다.


영문 위키피디아에서 AppCache를 찾아보니, 대신 Service worker를 사용하라고 권고하고 있네요.

https://en.wikipedia.org/wiki/Cache_manifest_in_HTML5


AppCache는 어떤 문제가 있을까요?


재미있게도 Service worker guide 문서에서 잘 정리된 내용을 찾을 수 있었습니다.

AppCache의 디자인이 단일페이지에서만 특히 잘 동작하고 복수의 페이지를 가진 환경에서 훌륭하게 동작하지 않으며, 실제로 많은 문제를 가지고 있다고 합니다. 문제에 대한 내용은 link를 참조하세요. (#)



Service worker?

오프라인을 통제할 수 있는 권한을 개발자에게 부여하여 오프라인 환경을 지원할 수 있도록 해주는 API 입니다.

chrome browser를 통해 google, youtube, facebook이 notification을 띄우는 녀석의 정체가 드러났습니다. :-)


Service worker는 DOM에 직접 접근할 수 없습니다.

Service worker를 사용하기 위해서는 Promise에 대한 이해가 필수입니다.

또한 개발 중에 localhost를 통해서 사용할 수는 있으나, 이를 사용하기 위해서는 서버에 https 설정이 필요합니다.


Service worker 등록

if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}


serviceWorker를 모든 browser가 지원하는 것이 아니기 때문에, 우선 지원여부를 판단해야 합니다.

mobile 쪽 browser별 지원 상황을 보면, 현재는 chrome, fireFox만 지원하고 있습니다.

https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API


지원하는 browser라면 별도의 파일(sw.js)에 선언한 내용을 service worker로 등록합니다.

그 뒤 등록 결과를 log로 출력합니다.


설치 (Offline cache 예제)

var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
'/static_pages/greeting.html',
'/assets/test.jpg'
];

self.addEventListener('install', function (event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function (cache) {
console.log('설치완료');
return cache.addAll(urlsToCache);
}).catch(function () {
console.log('설치실패')
})
);
});


urlsToCache에 선언한 resource를 cache에 등록합니다.

특정 이름의 cache에 url array를 등록하는 절차를 거칩니다.

이름이 필요한 이유는 향후 업데이트 등에서 활용하기 위함입니다.

이를테면, 더 이상 cache할 필요가 없는 url에 대해서 삭제를 하는 등의 활용이 있겠죠.



Service worker의 문제

개발되고 있는 최신 기술이기 때문에 아직 문제가 있다고 합니다.

지원하지 않는 browser가 있다는 것이 가장 큰 문제일 것이며, 설치 실패 시 처리가 매끄럽지 않은 부분이 있을 수 있습니다.

또한 https 환경이 아닌 경우 적용할 수 없다는 점도 AppCache의 대용으로 사용하기에는 조금 문제가 있는 부분입니다.


처참한 mobile browser 지원 현황



Service worker 관리

위의 예제를 테스트하면 service worker가 browser에 등록됩니다.

등록된 service workers는 chrome의 개발자 도구에서 관리할 수 있습니다.



Application tab의 Service workers sub tab을 click하면 목록이 나타나며,

등록한 항목을 찾아 Unregister 버튼을 클릭하는 것으로 삭제할 수 있습니다.



더 자세한 내용은 공식 guide를 참조하시길 바랍니다.

https://developers.google.com/web/fundamentals/getting-started/primers/service-workers?hl=ko



References

AppCache

https://developer.mozilla.org/ko/docs/Web/HTML/Using_the_application_cache

https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache

https://msdn.microsoft.com/ko-kr/library/hh673545(v=vs.85).aspx

http://b.mytears.org/2010/09/2272

https://mytory.net/archives/12616

http://blog.jamesdbloom.com/ProblemsWithApplicationCache.html

https://en.wikipedia.org/wiki/Cache_manifest_in_HTML5


ServiceWorker

https://developers.google.com/web/fundamentals/getting-started/primers/service-workers?hl=ko

https://vnthf.github.io/blog/serviceworker/



'Javascript' 카테고리의 다른 글

Ionic 2 Unit test  (0) 2017.07.31
Implement base controller  (0) 2017.07.26
npm lite-server  (0) 2017.07.06
Calculate distance between two geolocations.  (1) 2017.06.28

Client의 View(html, css) 및 Controller(javascript)를 browser를 간단하게 browser를 통해서 확인하려면 어떻게 할까요?

WAS를 설치하고 파일을 복사하고 web browser를 실행시켜서 url로 접속??

이건 정석적이지만, 절차가 너무 번거롭습니다.


혹은 file:// protocol을 이용하는 것도 하나의 방법입니다.

다만, 이 경우에는 localhost의 특수성을 이용할 수 없습니다.

이를테면 registration of worker service, navigator.geolocation 등은 https protocol인 경우 또는 localhost인 경우에만 정상적으로 동작합니다.



이를 어떻게 손쉽게 테스트할 수 있을까 고민하게 됩니다.

그리고 npm에서 그 해결책을 발견했습니다.


npm은 정말 보물창고이네요. :)


이러한 목적에 부합하는 lite-server라는 module이 있습니다.


lite-server? (#)

공식적인 소개 문구는 다음과 같습니다.

"Lightweight development only node server that serves a web app, opens it in the browser, refreshes when html or javascript change, injects CSS changes using sockets, and has a fallback page when a route is not found."

웹앱을 브라우저에서 열거나 html 또는 javascript의 변경을 감지하고, socket을 이용하여 css변경을 주입하는 등의 기능을 제공하는 가벼운 노드 서버입니다. 그리고, route를 찾을 수 없는 경우에는 fallback페이지를 출력합니다.


local NPM install

설치: npm install lite-server --save-dev


global installation

설치: npm install -g lite-server

실행: lite-server


command를 실행하는 folder의 ./index.html 파일을 launch합니다.


References

https://www.npmjs.com/package/lite-server

'Javascript' 카테고리의 다른 글

Ionic 2 Unit test  (0) 2017.07.31
Implement base controller  (0) 2017.07.26
Service workers.  (0) 2017.07.06
Calculate distance between two geolocations.  (1) 2017.06.28

두 좌표 간의 거리를 구하라는 task가 떨어졌습니다.

어떻게 계산하면 좋을까요?


저는 처음에 피타고라스의 정리를 이용하면 될 것 같았어요.

피타고라스의 정리에 대한 이미지 검색결과

a^{2}+b^{2}=c^{2}\,


이 그림 어때요? 오랜만이죠??



그런데, 지구 상의 두 좌표의 거리를 구하기 위해서는 Haversine formula를 적용해야 할 필요가 있습니다.

왜냐하면 지구는 평면이 아니기 때문입니다.


그 공식은 아래와 같습니다.



코딩해 보죠...

자 그럼 먼저...



으음...




하아...


찾아봅시다... :)



function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  var dLon = deg2rad(lon2-lon1); 
  var a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * 
    Math.sin(dLon/2) * Math.sin(dLon/2)
    ; 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  var d = R * c; // Distance in km
  return d;
}

function deg2rad(deg) {
  return deg * (Math.PI/180)
}

Stack overflow를 보니 Chunck라는 친구가 javascript function을 작성해서 공유해 주었습니다.

친절하게 단위도 km네요. 저는 mile을 써야 하는데 ㅋ


그리고 Salvador Dali라는 친구가 코드를 최적화 해 줬습니다.

처리 속도가 2배 빠르다고 합니다.


function distance(lat1, lon1, lat2, lon2) {
  var p = 0.017453292519943295;    // Math.PI / 180
  var c = Math.cos;
  var a = 0.5 - c((lat2 - lat1) * p)/2 + 
          c(lat1 * p) * c(lat2 * p) * 
          (1 - c((lon2 - lon1) * p))/2;

  return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
}

이렇게요.

최종 반환 값에 mile 변환을 위한 상수 0.621371를 곱하면 원하는 mile단위의 거리를 가져올 수 있겠네요.



결과를 검증해 보겠습니다.



google map에서 사무실 근처의 좌표를 가져왔습니다.

좌표는 각각 37.4137701, 127.1284930 / 37.4073826, 127.1338793이구요.

직선 거리는 약 853.75m가 나왔습니다.




jsFiddle에서 해당 function을 테스트해 보았습니다. (#)

좌표를 넣고 계산한 값을 console로 출력했습니다. km/mile 단위로


그 결과는 아래와 같습니다.

distance 0.854860114404647 km

distance 0.5311852841477299 mile


853.75 | 854.86 약 1km 거리에서 약 1m 정도 차이가 나네요.

허용 가능한 오차 수준이라고 우겨봅니다.



References

https://en.wikipedia.org/wiki/Haversine_formula

https://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula

https://jsfiddle.net/n5d7tgez/

'Javascript' 카테고리의 다른 글

Ionic 2 Unit test  (0) 2017.07.31
Implement base controller  (0) 2017.07.26
Service workers.  (0) 2017.07.06
npm lite-server  (0) 2017.07.06

+ Recent posts