Tag Archives: javascript

AngularJS PhoneCat 튜토리얼 앱 – 개요

이 문서는 AngularJS 공식 사이트의 PhoneCat Tutorial App 문서의 내용을 제 임의대로 번역한 글입니다. Mac 환경에서 작성되어 원문과는 차이가 있을 수 있습니다.


AngularJS를 소개할 수 있는 훌륭한 방법중에 하나는 이 튜토리얼을 통하여 Angular 웹 어플리케이션을 구축하는것을 안내하는 방법이라 생각합니다. 지금부터 만들어보려는 앱은 다양한 안드로이드 디바이스의 카탈로그를 보여주고 필터 기능을 사용하여 이중에 당신이 관심 있는 디바이스만을 볼 수 있고 그 디바이스의 상세 정보를 볼 수 있는 앱입니다.

angular-phonecat-start

이 튜토리얼을 따라가면 네이티브 익스텐션 혹은 플러그인을 사용하지 않고 Angular가 어떻게 브라우저를 더 멋지게 만들어 주는지를 볼 수 있습니다.

  • 클라이언트 사이드의 데이터 바인딩 기능을 사용하여 사용자의 액션에 즉시 변화하는 데이터의 동적 뷰를 만드는 방법을 예제를 통해서 확인합니다.
  • Angular가 DOM을 직접 조작할 필요 없이 당신의 데이터를 동기화 하는 방법으로 당신의 뷰를 유지하는것을 방법을 확인합니다.
  • Karma와 Protractor를 사용하여 당신의 웹 어플리케이션을 더욱 쉽고 나은 방법으로 테스트 하는 방법을 공부합니다.
  • 의존성 주입과 서비스를 사용하여 손쉽게 당신의 어플리케이션을 통해 데이터를 가져오는 일반적인 웹 작업을 수행하는 방법을 공부합니다.

당신이 이 튜토리얼을 완료할 때 당신은 다음과 같은 것이 가능해질것입니다.

  • 최근의 모든 브라우저에서 동작하는 동적인 어플리케이션을 만들 수 있습니다.
  • 데이터 바인딩을 사용하여 당신의 데이터 모델을 당신의 뷰에 연결합니다.
  • Karma를 이용하여 유닛 테스트를 만들고 실행할 수 있습니다.
  • Protractor를 사용하여 종단간 테스트(End-to-End Tests)를 만들고 실행할 수 있습니다.
  • 어플리케이션 로직을 템플릿으로부터 컴포넌트(Components)와 컨트롤러(Controller)로 이동시킬 수 있습니다.
  • Angular 서비스를 사용하여 서버로부터 데이터를 가져올 수 있습니다.
  • ngAnimate 모듈을 사용하여 당신의 어플리케이션에 애니메이션을 적용할 수 있습니다.
  • 더 큰 프로젝트에 적합한 모듈형 방식으로 당신의 Angular 어플리케이션을 설계할 수 있습니다.
  • 리소스를 확인하여 AngularJS에 대해 더 자세히 알아보십시오.

이 튜토리얼은 유닛 테스트 및 종단간 테스트를 포함한 간단한 어플리케이션을 개발하는 전체적인 과정을 안내하고 있습니다. 각 단계의 마지막에 있는 실험 부분은 당신이 AngularJS와 어플리케이션 개발에 대해 더 많은것을 배울 수 있도록 제안합니다.

당신은 여기서 안내하는 모든 튜토리얼을 단 몇시간만에 끝낼 수도 있고 자세히 파고 들어보며 정말 즐거운 하루를 보낼 수도 있을것이라 생각합니다. AngularJS에 대한 더욱 간단한 소개글을 보고 싶다면 Getting Started 문서를 확인해 보시기 바랍니다.

튜토리얼을 진행하기 위한 환경 설정하기

Git 설치하고 튜토리얼 받기

튜토리얼은 Github에 등록이 되어있으며 Git 명령을 통해 당신의 로컬 머신에 클론하여 실행해 보는 과정으로 진행됩니다. 이 문서에서는 Git을 설치하는 과정은 기술하지 않습니다. Git을 사용할 수 있는 환경이 되어있다면 다음과 같은 명령을 사용하여 angular-phonecat 저장소를 클론 받습니다.

$ git clone --depth=16 https://github.com/angular/angular-phonecat.git

이 명령을 실행하면 명령을 실행한 디렉토리에 angular-phonecat 디렉토리가 생성됩니다. 여기서 보여지는 –depth=16 명령을 가장 최근의 16개의 커밋만을 Pull 받도록 하는 명령입니다. 이 명령을 통해 다운로드 받아야 하는 커밋의 양을 줄이고 더욱 빨리 실행될 수 있도록 할 수 있습니다.

이제 다운받은 프로젝트의 디렉토리 안으로 진입하여 보도록 하겠습니다.

$ cd angular-phonecat

이제 튜토리얼을 진행할 모든 준비가 완료되었으며 앞으로 사용해보게될 모든 명령들이 이 angular-phonecat 디렉토리에서 실행하게 됩니다.

Node.js 설치하기

미리 설정된 로컬에서 구동가능한 웹서버와 테스트 툴을 실행하기 위해서는 버전4 이상의 Node.js가 필요합니다. 역시 설치과정은 생략하도록 하겠습니다. Homebrew를 사용하시면 간단하게 설치가 가능합니다.

당신의 머신에 Node.js 가 정상적으로 설치가 되었다면 이제 다음과 같은 간단한 명령으로 모든 의존성 있는 툴들을 다운로드 할 수 있습니다.

$ npm install

이 명령은 angular-phonecat 디렉토리 안의 package.json 파일을 읽고 그 안에 적혀 있는 다음과 같은 툴들을 node_modules 디렉토리에 다운받습니다.

  • Bower : 클라이언트 사이드 코드 패키지 관리자
  • Http-Server : 심플 로컬 정적 웹서버
  • Karma : 유닛 테스트 러너
  • Protractor : 종단간 테스트 러너

npm install 명령을 실행함으로써 Bower를 이용하여 자동으로 AngularJS 프레임워크를 app/bower_components 디렉토리 안에 다운받게 됩니다. 이러한 과정을 통해 알 수 있듯이 angular-phonecat 튜토리얼 프로젝트는 프로젝트를 셋업하고 관련된 유틸리티를 다운받는데 npm 스크립트를 사용합니다. 이 말은 즉 이 프로젝트를 구동하기 위해 이러한 유틸리티가 당신의 시스템에 미리 설치되어있을 필요가 없음을 의미합니다.

이 프로젝트는 개발을 진행하는 동안에 일반적으로 실행해야 하는 작업들을 손쉽게 수행할 수 있도록 도와주는 헬퍼 스크립트를 제공하고 있습니다.

  • npm start : 로컬 개발 웹서버를 시작합니다.
  • npm test : Karma 유닛 테스트 러너를 시작합니다.
  • npm run protractor : Protractor 종단간 테스트를 시작합니다.
  • npm run update-webdriver : Protractor가 필요로 하는 드라이버들을 설치합니다.

헬퍼 툴 설치하기 (선택사항)

Bower, Http-Server, Karma 그리고 Protractor와 같은 모듈들은 스스로 실행가능한 모듈들입니다. 당신의 로컬 시스템에 글로벌하게 설치할 수 있으며 이경우 터미널, 커맨드 프롬프트에서 즉시 실행이 가능해집니다. 이 튜토리얼을 진행함에 있어 이러한 명령들을 글로벌하게 설치할 필요는 없지만 당신이 이러한 명령들을 즉시 실행해야 할 필요가 있다면 다음과 같은 형태로 글로벌하게 설치할 수 있습니다.

$ sudo npm install -g bower

설치가 정상적으로 되었다면 다음과 같이 설치한 모듈을 바로 실행할 수 있습니다.

$ bower install

개발 웹서버 실행하기

Angular 어플리케이션은 순수하게 클라이언트 사이드의 코드이기 때문에 웹브라우저에서 파일을 직접 열어 실행해보는것도 가능합니다만 HTTP 웹서버를 통해서 실행해 보는것이 좋습니다. 특히 보안상의 이유로 대부분의 요즘의 브라우저들은 파일시스템에서 직접 로드된 경우 자바스크립트의 실행을 허용하지 않습니다.

이 angular-phonecat 프로젝트는 심플한 정적 웹서버가 포함되어있으며 어플리케이션을 개발하는 동안에 호스팅을 할 수 있습니다. 웹서버를 실행하기 위해서는 다음의 명령을 사용하면 됩니다.

$ npm start

이것은 로컬의 웹서버가 구동되며 8000 번 포트를 리슨하도록 합니다. 이제 당신은 http://localhost:8000/index.html 로 접속하여 어플리케이션에 접속해 볼 수 있습니다.

만약 웹 어플리케이션을 다른 아이피 혹은 포트로 구동하고 싶다면 package.json 파일의 start 항목에 있는 -a 및 -p 옵션의 설정을 변경하면 됩니다. 이 값을 변경하였다면 e2e-tests/protractor.conf.js 파일의 내용중에 baseUrl도 수정하여야 합니다.

유닛 테스트 실행하기

우리는 유닛 테스트를 통해 우리의 어플리케이션안에 있는 자바스크립트 코드가 정상적으로 수행되는데에 문제가 없는지 확인할 수 있습니다. 유닛테스트는 어플리케이션을 작은 격리된 부분으로 나누어 테스트를 수행하는데에 초점을 맞춥니다. 이 유닛 테스트가 정의 되어있는 파일(스펙)은 어플리케이션 코드와 함께 나란히 정의되어 있습니다. 이러한 방법은 이 파일들을 찾기 쉽고 코드들의 변경 사항을 따라갈 수 있도록 해줍니다. 이는 리펙토링 작업등을 통해 우리의 어플리케이션의 구조가 개선될 때 소스코드와 함께 테스트도 함께 이동될 수 있도록 도움을 줍니다.

angular-phonecat 프로젝트에는 어플리케이션의 유닛 테스트를 위해 Karma가 미리 설정되어있습니다. 간단하게 다음과 같은 명령으로 테스트를 수행할 수 있습니다.

$ npm test

위의 명령은 Karma 유닛 테스트 러너를 시작하게 합니다. Karma는 프로젝트 루트 디렉토리에 위치한 karma.conf.js 파일을 읽어들입니다. 이 설정 파일의 내용을 보면 다음과 같은 내용을 확인할 수 있습니다.

  • 크롬과 파이어폭스 브라우저를 실행하여 Karma에 연결하게 됩니다.
  • 이 브라우저들을 통해 모든 유닛 테스트를 실행합니다.
  • 이 테스트들의 결과를 실행한 터미널/커맨드 라인 창에 출력합니다.
  • 프로젝트의 모든 자바스크립트 파일들을 감시하며 변경이 발생할 경우 테스트를 재실행합니다.

이 유닛 테스트를 백그라운드에 항상 켜둔채로 놔두어도 좋습니다. 당신이 작업중인 코드가 유닛 테스트를 통과하였는지 즉각적으로 피드백을 줄것입니다.

종단간(End-to-End) 테스트 실행하기

우리는 종단간 테스트를 통해 우리의 어플리케이션의 모든 기능이 기대한대로 정상 동작하는것을 확인할 수 있습니다. 종단간 테스트는 모든 클라이언트 어플리케이션, 특히 뷰가 정상적으로 보여지고 동작하는것을 테스트하기 위해 만들어졌습니다.

이것은 실제 사용자가 브라우저에서 실제로 구동되는 어플리케이션과 상호작용 하는것을 시뮬레이션 함으로써 이루어집니다. 이러한 종단간 테스트는 e2e-tests 디렉토리 안에 정의되어있습니다.

이 angular-phonecat 프로젝트는 Protractor 를 사용하여 어플리케이션의 종단간 테스트를 수행하도록 정의되어있습니다. Protractor는 드라이버 셋에 의존하여 브라우저와 상호작용을 하게 됩니다. 다음의 명령을 사용하여 이러한 드라이버들을 설치할 수 있습니다.

$ npm run update-webdriver

하지만 이 명령을 직접 실행할 필요는 없습니다. 튜토리얼에서 제공하는 프로젝트에 종단간 테스트를 실행할 때 자동으로 실행이 되도록 설정이 되어있습니다.

Protractor는 실행중인 어플리케이션과 상호작용을 함으로써 동작하므로 웹 서버를 시작할 필요가 있습니다.

$ npm start

이렇게 웹서버가 정상적으로 실행이 되었다면 터미널/커맨드라인 창에서 다음의 명령을 사용하여 Protractor 테스트 스크립트를 실행할 수 있습니다.

$ npm run protractor

Protractor는 e2e-tests/protractor.conf.js 파일로부터 설정을 읽어들이게 됩니다. 이 설정파일의 내용을 보면 다음의 내용을 알 수 있습니다.

  • 크롬 브라우저를 실행하고 그 어플리케이션에 연결합니다.
  • 종단간 테스트를 이 브라우저를 통해 실행합니다.
  • 이러한 테스트의 결과를 터미널/커맨드라인 창으로 출력합니다.
  • 브라우저를 닫고 종료합니다.

이러한 종단간 테스트는 당신이 HTML 뷰를 변경하였거나 어플리케이션이 전체적으로 정상적으로 동작하고 있는지 확인하고 싶을 때 유용하게 사용할 수 있습니다. 이 테스트는 새로운 커밋을 원격지 저장소에 Push 하기 전에 실행하는것이 일반적입니다.

참고 : https://docs.angularjs.org/tutorial

[Node.JS] Modules and npm

사용자 삽입 이미지

Node.js modules

Node의 모듈 시스템에 대해 이야기 해보겠습니다.

모듈은 당신의 어플리케이션의 다른 자바스크립트 파일들을 인클루드 할 수 있게 해줍니다. 사실 Node의 대부분의 핵심 기능들은 기존에 자바스크립트로 작성된 모듈을 사용하여 구현됩니다. 이말을 즉 Github에 올라와있는 핵심 라이브러리들의 소스코드를 확인하는것이 가능함을 뜻합니다.

모듈은 데이터베이스에 엑세스 하는 라이브러리 같은 외부 라이브러리를 인클루드할수 있게 해줌으로써 어플리케이션을 개발하는데 있어 필수적인 요소입니다. 그리고 제한적인 역할에 따라 코드를 분리하여 정리될 수 있도록 도와줍니다. 당신의 코드중에 재사용이 가능한 파트를 식별할 수 있고 그 부분을 모듈로 분리하여 파일의 코드량을 줄일 수 있으며 읽기 쉽고 유지보수가 더 쉽도록 만들어 줍니다.

모듈을 사용하는 방법은 간단합니다. 핵심 라이브러리의 이름 또는 로드하고자 하는 모듈 파일의 경로를 하나의 인자로 갖는 require() 함수를 사용하면 됩니다. require() 를 이용하여 몇몇의 모듈을 사용했던 이전의 단순한 메시징 어플리케이션 예제에서 본적이 있을것입니다.

모듈을 만들기 위해서는 익스포트 하길 원하는 오브젝트를 정의하는 코드를 작성하여 주면 됩니다. Node에서는 이런 목적으로 사용하기 위해 최상위 스코프에서 exports 오브젝트를 사용할 수 있습니다.

exports.funcname = function() {
  return 'Hello World';
};

어떠한 프로퍼티라도 exports 오브젝트에 할당 되면 require() 함수의 반환값으로부터 엑세스 할 수 있게 됩니다.

var hello = require('./hello.js');
console.log(hello.funcname()); // "Hello World" 출력

또한 exports 대신에 다음과 같이 module.exports를 사용할 수 있습니다.

function funcname() { return "Hello World"; }
module.exports = { funcname: funcname };

이 대체 문법은 exports에 클래스와 같은 단일 오브젝트를 할당하는것을 가능하게 해줍니다. 우리는 이전에 클래스 작성시에 prototype을 이용한 상속을 구현하는것을 논의하였었습니다. 당신의 클래스를 분리된 모듈로 만든 후 매우 손쉽게 그것들을 당신의 어플리케이션에 인클루드 할 수 있습니다.

// class.js:
var Class = function() { ... }
Class.prototype.funcname = function() {...}
module.exports = Class;

클래스를 포함한 모듈을 작성하였습니다. 이제 require() 를 사용하여 당신의 클래스의 새로운 인스턴스를 만들겠습니다.

// 다른 파일
var Class = require('./class.js');
var object = new Class(); // 새로운 인스턴스 생성

Sharing variables between modules

Node에는 전역 컨텍스트 환경이 존재하지 않습니다. 각각의 스크립트마다 그들의 컨텍스트를 가지고 있습니다. 그렇기 때문에 다수의 모듈을 인클루드 하는것이 현재의 스코프를 더럽히진 않습니다. 최상위 스코프에서 var foo = ‘bar’; 와 같은 코드를 선언했더라도 다른 모듈에서는 foo는 선언되지 않은 변수가 됩니다.

이것이 의미하는것은 Node에서 모듈들간에 변수나 값들을 공유하는 유일한 방법은 다수의 파일들이 동일한 모듈을 인클루드 하는 것입니다. 일단 모듈이 캐시가 되면 데이터를 모듈을 이용하여 가령 설정 옵션들처럼 공유할 수 있습니다.

// config.js
var config = {
  foo: 'bar'
};
module.exports = config;

다른 모듈에서는 다음과 같이 사용 가능합니다.

// server.js
var config = require('./config.js');
console.log(config.foo);

하지만 Node 모듈은 기본적으로 사용 가능한 몇개의 변수를 가지고 있습니다. globalsprocess API 문서를 참고하시기 바랍니다.

  • __filename : 현재 실행된 파일의 이름
  • __dirname : 현재 실행중인 스크립트가 존재하는 디렉토리의 이름
  • process : 현재 실행중인 프로세스에 포함된 정보를 가진 오브젝트, 변수뿐만 아니라 process.exit, process.cwd, process.uptime같은 메소드도 포함
  • process.argv : 커맨드 라인의 매개변수를 가지고 있는 배열, 첫번째 엘리먼트의 값은 ‘node’가 될것이며 두번째 엘리먼트는 자바스크립트 파일의 이름이 될것임. 이후의 엘리먼트들은 추가로 입력되는 매개변수들.
  • process.stdin / process.stdout / process.stderr : 현재 프로세스의 표준입력, 표준출력, 표준에러에 대응하는 스트림
  • process.env : 현재 프로세스의 사용자 환경을 가지고 있는 오브젝트
  • require.main : Node를 통해 바로 실행된 파일이라면 require.main은 module과 동일

다음의 코드는 현재 스크립트에 대한 값들을 출력해 줍니다.

console.log('__filename', __filename);
console.log('__dirname', __dirname);
console.log('process.argv', process.argv);
console.log('process.env', process.env);
if(module === require.main) {
    console.log('This is the main module being run.');
}

require.main은 현재 실행중인 모듈이 메인 모듈인지 여부를 알기 위해 사용 될 수 있습니다. 이것은 모듈을 직접 실행했을 때에 무언가 하고 싶은 작업이 있을 경우 유용합니다. 예를 들어 node filename.js로 실행시에 다음과 같이 무언가를 인클루드하여 테스트 파일을 실행하도록 할 수 있습니다.

// 이 모듈이 직접 실행 된다면 테스트가 실행됩니다.
if (module === require.main) {
  var nodeunit_runner = require('nodeunit-runner');
  nodeunit_runner.run(__filename);
}

process.stdin, process.stdout, process.stderr 들은 스트림에 대해 논의되는 다음 챕터에서 다시 다루도록 하겠습니다.

Organizing modules

require() 를 사용하여 파일을 인클루드 하는 방법은 3가지가 있습니다.

  • 상대 경로 사용하기 : foo = require(‘./lib/bar.js’);
  • 절대 경로 사용하기 : foo = require(‘/home/foo/lib/bar.js’);
  • 검색 사용하기 : foo = require(‘bar’);

첫번째 두번째 방법의 경우 이해하기 쉬울것입니다. 하지만 3번째 방법의 경우 Node가 현재 디렉토리에서 실행될 때 ./node_modules/ 위치 이하의 모듈들을 로드하려고 시도하게 됩니다. 만약 모듈을 찾지 못한다면 부모 디렉토리로 이동하여 같은 시도를 하게 되고 파일 시스템의 루트까지 올라가며 찾게 됩니다.

예를 들어 /home/foo/ 이하의 특정 스크립트에서 require(‘bar’)가 호출 되었다면 원하는 모듈을 찾을 때 까지 다음과 같이 검색을 해 나갑니다.

  • /home/foo/node_modules/bar.js
  • /home/node_modules/bar.ja
  • /node_modules/bar.js

이러한 방법으로 모듈을 로딩하는 것은 상대 경로를 특정짓는것보다 더 간단합니다. 심지어 파일을 이동할 경우에도 패스가 변경되거나 하는것에 대해 걱정할 필요가 없습니다.

Directories as modules

Node를 위한 추가적인 작업을 통해 모듈을 디렉토리 안으로 구성할 수 있습니다.

가장 쉬운 방법은 디렉토리를 생성하는 것입니다. ./node_modules/mymodulename/ 과 같이 디렉토리를 만들고 그 안에 index.js 를 넣으면 됩니다. 이 index.js파일은 기본적으로 로드가 되게 됩니다.

또는 package.json 파일을 mymodulename 폴더 안에 넣으면 모듈의 이름과 메인 파일의 경로를 정의해 줄 수 있습니다.

{
  "name": "mymodulename",
  "main": "./lib/foo.js"
}

이 설정은 require(‘mymodulename’) 을 호출할 경우 ./node_modules/mymodulename/lib/foo.js파일을 로드한 결과가 반환되게 됩니다.

npm

npm은 분산 Node 모듈을 이용하는 패키지 관리자입니다. 이것에 대해 여기서 자세히 다루지는 않을 것입니다. 왜냐하면 인터넷 상에 잘 정리가 되어있기 때문입니다. 참고
npm은 굉장합니다. 그리고 당신은 이것을 이용해야 합니다. 아래에 몇가지 사용 예시를 다룰 것입니다.

Installing packages

npm에서 가장 많이 쓰이는 사용 예는 다른 사람의 모듈을 설치하기 위해서 입니다.

  • npm search packagename
  • npm view packagename
  • npm install packagename
  • npm outdated packagename
  • npm update packagename

패키지는 현재 디렉토리의 ./node_modules/ 아래에 설치가 됩니다.

Specifying and installing dependencies for your own app

npm은 새로운 머신에 당신의 어플리케이션을 설치하는것을 손쉽게 만들어줍니다. 왜냐하면 어플리케이션의 루트 디렉토리에 있는 package.json 파일에 어떤 모듈을 필요로 하는지 정의할 수 있기 때문입니다.

다음은 package.json 파일의 가장 최소화된 모습입니다.

{ "name": "modulename",
  "description": "Foo for bar",
  "version": "0.0.1",
  "repository": {
    "type": "git",
    "url":  "git://github.com/mixu/modulename.git" },
  "dependencies": {
    "underscore": "1.1.x",
    "foo": "git+ssh://git@github.com:mixu/foo.git#0.4.1",
    "bar": "git+ssh://git@github.com:mixu/bar.git#master"
  },
  "private": true
}

이 설정으로 인해 당신의 어플리케이션의 의존성에 적합한 적절한 버전의 모듈을 가져올 수 있게 됩니다. 의존성에 맞게 모듈을 설치하기 위해 단지 다음을 실행하면 됩니다.

$ npm install

Loading dependencies from a remote git repository

제가 좋아하는 기능중 하나는 원격지의 git 저장소에서 git+ssh URL을 이용하여 파일들을 가져올 수 있는 기능입니다. git+ssh://github.com:mixu/nwm.git#master 처럼 URL을 지정해 두면 원격 git 저장소에서 바로 의존성 모듈을 설치할 수 있습니다. 해시(#) 이후의 값은 저장소의 tag나 branch를 나타냅니다.

설치된 의존성 모듈의 리스트를 보기위해서는 다음의 명령을 사용하시면 됩니다.

$ npm ls

Specifying custom start, stop and test scripts

당신은 package.json 파일의 script 설정을 통해 다양한 스테이지에서 하고자 하는 특정 작업을 정의해 줄 수 있습니다.

{ "scripts" :
  { "preinstall" : "./configure",
    "install" : "make && make install",
    "test" : "make test",
    "start": "scripts/start.js",
    "stop": "scripts/stop.js"
  }
}

위의 예제에서, 우리는 인스톨 이전에 무엇을 할지 인스톨 과정에 무엇을 할지 npm test, npm start, npm stop 명령이 호출되었을 때 무엇을 할것인지에 대한 정의를 하였습니다.

참고 : http://book.mixu.net/ch8.html