2017년 10월 25일 수요일

Chapter4. NodeJS의 기본 모듈3



학습 목표

1. 스트림에서 이벤트와 데이터를 다룰 수 있습니다.
2. URL 모듈로 URL을 분석하고 생성할 수 있습니다.
3. queryString 모듈로 쿼리 문자열을 다룰 수 있습니다.
4. 클러스터 모듈을 이용해서 클러스터링의 장점을 활용할 수 있습니다.

1. 스트림

◎ 스트림 : 데이터의 전송 흐름

  • 콘솔 입력/출력
  • 파일 읽기/쓰기
  • 서버/클라이언트 - 데이터 전송
◎ 스트림 모듈
  • 스트림을 다루기 위한 추상 인터페이스
  • 다양한 스트림을 같은 인터페이스로 다룰 수 있다.
◎ 스트림 종류
  • 읽기 스트림 : Readable Stream
  • 쓰기 스트림 : Writable Stream
  • 읽기/쓰기 : Duplex
  • 변환 : Transform
◎ Readable Stream
  • 읽기 스트림 : Readable
  • 모드 : flowing, paused
  • flowing mode
    → 데이터를 자동으로 읽는 모드
    → 전달되는 데이터를 다루지 않으면 데이터 유실
  • paused mode
    → 데이터가 도착하면 대기
    → read() 함수로 데이터 읽기
▷ Readable 메소드
  • 읽기
    → readable.read([size])
    → readable.setEncoding(encoding)
  • 중지/재개
    → readable.pause()
    → readable.resume()
  • 파이프
    → readable.pipe(destination[, options])
    → readable.unpipe([destination])
▷ Readable 이벤트
  • readable : 읽기 가능한 상태
  • data : 읽을 수 있는 데이터 도착
  • end : 더 이상 읽을 데이터가 없는 상태
  • close : 스트림이 닫힌 상태
  • error : 에러
▷ flowing mode
  • data 이벤트 구현
  • pipe 연결
  • resume() 호출
▷ 파일 스트림에서 읽기 : flowing mode
var is = fs.createReadStream(file);
is.on('readable', function() {
    console.log('== READABLE EVENT');
});

// flowing모드
is.on('data', function(chunk) {
    console.log('== DATA EVENT');
    console.log(chunk.toString());
    // buffering 필요
});

// end 이벤트
is.on('end', function() {
    console.log('== END EVENT');
});

▷ 파일 스트림에서 읽기  : paused mode
var is = fs.createReadStream(file);

// 'data'이벤트가 없으면 pause mode
is.on('readable', function() {
    console.log('== READABLE EVENT');

    // 10바이트씩 읽기
    while(chunk == is.read(10)) {
        console.log('chunk : ', chunk.toString());
    }
});

◎ Writable Stream
▷ Writable Stream : 데이터 출력
▷ 예

  • http 클라이언트의 요청
  • http 서버의 응답
  • 파일 쓰기 스트림
  • tcp 소켓
▷ 메소드
▷ 데이터 쓰기, 인코딩
  • writable.setDefaultEncoding(encoding)
  • writable.write(chunk[, encoding][, callback])
▷ 스트림 닫기
  • writable.end([chunk][, encoding][, callback])
▷ 버퍼
  • writable.cork()
  • writable.uncork()
▷ 이벤트
  • drain : 출력 스트림에 남은 데이터를 모두 보낸 이벤트
  • error : 에러
  • finish : 모든 데이터를 쓴 이벤트
  • pipe ; 읽기 스트림과 연결(pipe)된 이벤트
  • unpipe : 연결(pipe)해제 이벤트
◎ 출력 스트림에 쓰기
▷ 파일 기반의 출력 스트림에 쓰기
var os = fs.createWriteStream('output.txt');
os.on('finish', function() {
    console.log('==FINISH EVENT');
});

os.write('1234\n');
os.write('5678\n');

os.end('9\n'); // finish event

◎ 표준 입출력 스트림
▷  표준 입출력 스트림

  • process.stdin : 콘솔 입력
  • process.stdout : 콘솔 출력
◎ 스트림 연결
▷ 스트림 연결과 해제(Readable)
  • readable.pipe(destination[, options])
  • readable.unpipe([destination])
▷ 연결 이벤트(Writable)
  • pipe
  • unpipe
▷ 스트림 연결
  • 입력 스트림 : stdin
  • 출력 스트림 : 파일
▷ 스트림 연결 예제
var is = process.stdin;
var os = fs.createWritableStream('ouput.txt');

os.on('pipe', function(src) {
    console.log('pipe event');
});

// exist 입력이 오면 파이프 연결 해제
is.on('data', function(data) {
    if(data.trim() == 'exit') {
        is.unpipe(os);
    }
});

is.pipe(os);


2. URL 다루기

◎ 네트워킹
▷ 네트워킹의 시작

  • 서버 주소
  • 서버에서 요청 위치
  • 서버에서 리소스의 위치
▷ URL : Uniform Resource Locator
  • http://nodejs.org/api/
  • http://nodejs.org/api/http.html
  • http://nodejs.org/api/http.html#http_event_connect // 뒤에 프레그먼트가 붙음
◎ URL
▷ UR L 구성 요소
  • 프로토콜(Protocol)
  • 호스트(Host)
  • 포트번호(Post)
  • 경로(Path)
  • 쿼리(Query)
  • 프래그먼트(Fragment)

http://images.apple.com/mac/home/images/tap_hero_macpro_2x.jpg
scheme  host            path
http://www.google.com/search?q=iphone&format=json
            host                    query

▷ URL 모듈
var url = require('url');

▷ URL 모듈
url.parse(urlStr[, parseQueryString][, slashesDenoteHost])
  • urlStr : URL 문자열
  • parseQueryString : 쿼리 문자열 파싱 여부, 기본값 false
  • slashesDenoteHost : //로 시작하는 주소의 경우, 호스트 인식 여부
    기본값 false
▷ URL 분석하기
var urlStr = 'http://idols.com/q?group=EXID&name=하니&since=';
var parse = url.parse(urlStr);

▷ 결과
host : 'idols.com'
search : '?group=EXID&name=하니&since=',
query : group=EXID&name=하니&since=,
pathname : '/q',
path : '/q?group=EXID&name=하니&since=,

◎ URL과 쿼리 문자열
▷ 쿼리 문자열(query string)
이름=값&이름=값 형태로 정보 전달
http://idolos.com/q?group=EXID&name=하니&since=
▷ URL 모듈로 쿼리 문자열 파싱
url.parse('http:/...', true);

▷ URL 분석하기
var urlStr = 'http://idols.com/q?group=EXID&name=하니&since=';
var parsed = url.parse(urlStr, true);
var query = parsed.query;

▷ 결과
query.group // EXID
query.name  // 하니
query.since   //

◎ URL 만들기
▷ URL 만들기
url.format(urlObj)

▷ URL 변환
url.resolve(from, to)

▷ URL 만들기 : format

  • protocol : 프로토콜
  • host : 서버 호스트 주소
  • pathname : 경로
  • search : 쿼리 스트링
  • auth : 인증 정보
var urlObj = {
    protocol : 'http',
    host : 'idols.com',
    pathname : 'schedule/radio',
    search : 'time=9pm&day=monday'
}

var urlStr = url.format(urlObj);
// http://idols.com/schedule/radio?time=9pm&day=monday

◎ URL 인코딩
▷ URL에 허용되는 문자

  • 알파벳, 숫자, 아이폰, 언더스코어, 점, 틸드
▷ URL 인코딩하기
https://www.google.com/search?q=아이폰
https://www.google.com/search?q=%EC%95%84%EC%9D%B4%ED%8F%B0

▷ 써드 파티 모듈
  • urlencode

3. 쿼리 스트링


◎ 쿼리 문자열

  • 쿼리 문자열은 URL 외에도 사용
  • 쿼리 문자열(query string)
    name1=value1&name2=value2&name3=&name4=
  • HTTP 메시지 바디로 정보를 전달할 때도 사용
◎ URL과 쿼리 문자열
▷ querystring 모듈
var querystring = require('querystring');

▷ 쿼리 문자열 분석하기
querystring.parse(str[, sep][, eq][, options])

sep, eq : 쿼리 구분자와 = 기호(&, = 대체)
var querystring = require('querystring');
var str = 'group=EXID&NAME=하니&since=';

var parsed = querystring.parse(str);

parsed.group // EXIT
parsed.name // 하니
parsed.since // ''
parsed.last // undefined

◎ 쿼리 스트링 중 배열
▷ 쿼리 스트링의 배열

  • qroup=걸스데이&member=혜리&member=유라&member=민아
◎ 쿼리 문자열 만들기

  • querystring.stringfy(obj[, sep][, eq][, options])
  • sep, eq : 쿼리 구분자와 = 기호
  • 인코딩 자동
var queryObj = {
    name : 'IU',
    best : '좋은날'
};

var queryStr = querystring.stringfy(queryObj);
// name=IU&best=%EC%A2%8B%EC%9D%80%EB%82%A0

4. 클러스터

◎ 클러스터(Cluster)
여러 시스템을 하나로 묶어서 사용하는 기술

개별 시스템 내에서 클러스터

  • 멀티 프로세스
  • 멀티 코어
▷ NodeJS의 클러스터
  • Node.js 애플리케이션 : 1개의 싱클 스레드
  • 멀티 코어 시스템의 장점을 살리기 - 클러스터
  • Node.js 클러스터
    → 클러스터 사용시 포트 공유 - 서버 작성 편리
    → 코어(프로세서)의 개수 만큼 사용
▷ 클러스터 생성시 개념
  • 클러스터링 : 마스터와 워커 프로세스
  • 마스터
    → 메인 프로세스
    → 워커 생성
  • 워커
    → 보조 프로세스
    → 마스터가 생성
◎ 클러스터 모듈
▷ 클러스터 모듈
  • var cluster = require('cluster');
▷ 클러스터 생성(마스터)
  • cluster.fork()
▷ 구분하기
  • cluster.isMaster
  • cluster.isWorker
◎ 클러스터 생성과 동작
클러스터링을 사용하는 대략적인 구조

마스터 - 워커 생성
if( cluster.isMaster ) {
    // 마스터 코드
    cluster.fork();
}
else {
    // 워커 코드
}

◎ 클러스터의 이벤트
▷ 클러스터의 이벤트
  • fork : 워커 생성 이벤트
  • online : 워커 생성 후 동작하는 이벤트
  • listening : 워커에 작성한 서버의 listen 이벤트
  • disconnect : 워커 연결 종료
  • exit : 워커 프로세스 종료
▷ 워커의 이벤트
  • message : 메시지 이벤트
  • disconnect : 워커 연결 종료
◎ 워커
▷ 워커 접근
// 워커 접근
  • cluster.worker
▷ 워커 식별자
// 워커 id
  • worker.id
◎ 워커 종료
▷ 워커 종료
  • worker.kill([signal='SIGTERM'])

◎ 클러스터 생성과 동작
▷ 클러스터를 사용하는 대략적인 구조
if(cluster.isMaster) {
    cluster.fork();
    cluster.on('online', function(worker) {
        // 워커 생성 후 실행
        console.log('Worker #' + worker.id + ' is Online');
    });
    cluster.on('exit', function(worker, code, signal) {
        // 워커 종료 이벤트
        console.log('Worker #' + worker.id + ' exit');
    });
}
else {
    var worker = cluster.worker;
    // 워커 종료
    worker.kill();
}

◎ 서버 클러스터
▷ 서버에 클러스터 적용
if(cluster.isMaster) {
    cluster.fork();
} else {
    http.createServer(function(req, res) {
        // 서버 코드
    }).listen(8000);
}

▷ 클러스터링 기능 지원 프로세스 모듈

  • pm2

◎ 데이터 전달
▷ 마스터가 워커에게 데이터 전달

  • worker.send(data)
▷ 워커의 데이터 이벤트
  • worker.on('message', function(data) {
    });
▷ 워커가 마스터에게 데이터 전달
  • process.send(data)
▷ 마스터에서의 데이터 이벤트
  • var worker = cluster.fork();
  • worker.on('message', function(data) {
    });
if(cluster.isMaster) {
    var worker = cluster.fork();
    worker.on('message', function(message) {
        console.log('Master received : ', message);
    });

    cluster.on('online', function(worker) {
        worker.send({message : 'Hello Worker'});
    });
} else {
    var worker = cluster.worker;

    worker.on('message', function(message) {
        process.send({message:'Fine thank you!'});
    });
}

◎ 마스터와 워커 분리
▷ 별도의 파일로 분리하기
cluster.setupMaster([settings])
  • exec : 워커 파일
  • args : 실행 파라미터
▷ 마스터 - fork
cluster.setupMaster({
    exe:'worker.js'
});
cluster.fork();

학습정리

◎ 지금까지 'Node.JS의 기본모듈 3'에 대해 살펴보았습니다.

▷ 스트림
네트워크를 이용한 통신은 스트림을 이용해서 데이터를 주고받습니다. 스트림
모듈을 이용해서 데이터와 이벤트를 알아봤습니다.

▷ URL 다루기
네트워크를 이용한 통신에서 URL은 리소스 위치를 다룹니다. URL 모듈로 정보를
분석하고 생성할 수 있었습니다.

▷ 쿼리 문자열 다루기
쿼리 문자열은 정보를 전달하는 방법으로 자주 사용합니다. querystring 모듈로
정보를 분석하고 생성할 수 있었습니다.

▷ 클러스터링
클러스터 모듈을 이용해서 다중 프로세서의 장점을 살릴 수 있는 클러스터링을
구현해봤습니다.





2017년 10월 23일 월요일

Chapter3. NodeJS의 기본 모듈2



학습 목표

1. 자주 사용하는 기본 모듈에 대해서  알 수 있습니다.
2. 파일을 다루기 위해 path, fs 모듈을 사용할 수 있습니다.
3. 이진 데이터를 다루는 버퍼 모듈을 사용할 수 있습니다.

01. 경로 다루기

◎ path 모듈 : 파일 경로 다루기

  • 경로 정규화
  • 경로 생성
  • 디렉토리/파일 이름 추출
  • 파일 확장자 추출
◎ 경로 정보
▷ 현재 실행 파일 경로, 폴더 경로
▷ 전역 객체(global)
  • __filename
  • __dirname
▷ 같은 폴더 내 이미지 경로
  • var path = __dirname + '/image.png';
◎ 경로 다듬기
▷ 경로 다듬기
  • path.normalize()
▷ 경로 구성
  • '..' : 부모 폴더
  • '.' : 같은 폴더
▷ 예제
pathUtil.normalize('/user/tmp/../local///bin/');
// returns
/user/local/bin/

◎ 경로 구성 요소
▷ 경로 구성 요소 얻기

  • path.basename() : 파일 이름, 경로 중 마지막 요소
  • path.dirname() : 파일이 포함된 폴더 경로
  • path.extname() : 확장자
▷ 예제
var path = '/foo/bar/baz/asdf/quux.html';

// /foo/bar/baz/asdf
pathUtil.dirname(path);
//quux.html
pathUtil.basename(path);
// .html
pathUtil.extname(path);

▷ 경로 구성 객체
var info = path.parse('/home/user/dir/file.txt')
{
    root : "/",
    dir : "/home/user/dir",
    base : "file.txt",
    ext : ".txt",
    name : "file"
}
// 구성요소얻기
info.base
info.name

◎ 경로 만들기

  • pathUtil.sep // '/,\'
  • pathUtil.join()
  • pathUtil.format()
▷ 경로 연산
  • __dirname + pathUtil.sep + 'image.png';
  • 현재 폴더 내 image.png
▷ 경로 붙이기
  • path.join
    pathUtil.join('/foo', 'bar', 'baz/asdf', 'quux', '..')
    // returns
    '/foo/bar/baz/asdf'
▷ path.format
var path = pathUtil.format({
    root : "/",
    dir : "/home/user/dir",
    base : "file.txt",
    ext : ".txt",
    name : "file"
});

▷ '/home/user/dir/file.txt'

02. 파일 시스템 다루기

◎ 파일 시스템 다루기
▷ 파일 시스템 모듈 : fs
var fs = require('fs');
▷ 주요 기능

  • 파일 생성/읽기/쓰기/삭제
  • 파일 접근성/속성
  • 디렉토리 생성/읽기/삭제
  • 파일 스트림
주의 : 모든 플랫폼에 100% 호환되지 않음
◎ fs 모듈의 특징
▷ 특징
  • 비동기와 동기 방식 함수 모두 제공
▷ 비동기식
  • callback 사용
  • 논-블럭 방식
▷ 동기식
  • 이름 규칙 + Sync(readFileSync)
  • 블록(block)방식 - 성능상 주의
  • 반환값 이용
◎ 비동기식/동기식 API
▷ 동기식과 비동기식 API 사용 방법
▷  비동기식
var data = fs.readFileSync('textfile.txt', 'utf8);
▷  동기식
fs.readFile('textfile.txt', 'utf8', function(error, data) {
});
▷ 동기식과 비동기식 API 에러 처리 방법

  • 동기식 : try~catch사용
try {
    var data = fs.readFileSync('none_exist.txt', 'utf-8');
}
catch( exception ) {
    console.error('Readfile Error : ', exception);
}


  • 비동기식 : 콜백 함수의 에러 파라미터 사용
fs.readFile('none_exist.txt', 'utf-8', function(err, data) {
    if(err) {
        console.error('Readfile error', err);
    }
    else {
        // 정상 처리
    }
});

◎ 파일 다루기
▷  파일 다루기

  • 파일 디스크립터
  • 파일 경로
▷ FileDescription로 파일 다루기
  • fs.read(fd, buffer, offset, length, position, callback)
  • fs.readSync(fd, buffer, offset, length, position)
▷ 파일 경로로 파일 다루기
  • fs.readFile(filename[, options], callback)
  • fs.readFileSync(filename[, options])
◎ 파일 디스크립터
▷ FileDescription 얻기 : open 함수
var fd = fs.openSync(path, flag[, mode])
fs.open(path, flags[, mode], function(err, fd) {
});
▷ flag

  • r(읽기), w(쓰기), a(추가), ...
▷ 파일 닫기
  • fs.close(fd, callback);
  • fs.closeSync(fd);
◎ 파일 읽기
▷ 파일 내용 읽기
  • fs.read(fd, buffer, offset, length, position, callback)
  • fs.readFile(filename[, options], callback)
  • fs.readFileSync(filename[, options])
▷ 파일 종류
  • 문자열 읽기 : 인코딩
  • 바이너리 읽기 : buffer
▷ 인코딩 설정 안하면 - buffer

◎ 파일 읽기 예제
▷ 파일 읽기 예제 - 파일 디스크립터, 동기식
var fd = fs.openSync(file, 'r');
var buffer = new Buffer(10);

var byte = fs.readSync(fd, buffer, 0, buffer.length, 0);
console.log('File Contents : ', buffer.toString('utf-8'));

// 파일 디스크립터 닫기
fs.closeSync(fd);

▷ 파일 읽기 예제 - 파일 디스크립터, 비동기
fs.open(file, 'r' function(err, fd2) {
    var buffer2 = new Buffer(20);
    fs.read(fd2, buffer2, 0, buffer2.length, 10, functon(err, byteRead, buffer) {
        console.log('File Read', byteRead, 'bytes');
        console.log('File Content : ',  buffer.toString('utf-8'));

        fs.close(fd, function(err)[]);
    });
});

▷ 파일 읽기 - 동기식
경로를 이용하면 fd를 이용하는 것보다 편한점이 있다.
// 파일 읽기, 인코딩
console.log('File Reading, with Encoding');
var data = fs.readFileSync(file, 'utf-8);
console.log(data);

// 바이너리 파일 읽기
var imageData = fs.readFileSync('./image.jpg');
console.log('Read Image File');
console.log(imageData);

▷ 에러 처리 : try~catch

▷ 파일 읽기 : 비동기 , 인코딩
fs.readFile(file, 'UTF-8', function(err, data) {
    if(err) {
        console.error('File Read Error : ', err);
        return;
    }
    console.log('Read Text File, UTF-8 Encoding');
    console.log(data);
});

◎ 파일 상태 확인

  • 파일 다루기 : 파일 상태에 따라서 에러 발생
  • 파일 다루기 전 : 파일 상태 확인
◎ 파일 상태 - 존재 확인
▷ 파일 존재 확인하기


  • deprecated
    fs.exists(path, callback)a
    fs.existsSync(path)
▷ 대신
  • fs.access(Sync) 사용
  • fs.stat(Sync)
◎ 파일 접근 상태 확인
▷ 파일 접근 가능 확인하기
  • fs.access(path[, mode], callback)
  • fs.accessSync(path, [, mode])
▷ 접근 모드
  • fs.F_OK : 존재 확인
  • fs.R_OK, W_OK, X_OK : 읽기/쓰기/실행 여부 확인
▷ 결론
  • 접근 불가능하면 에러 발생 : try~catch 사용
◎ 파일 접근 여부 확인 후 읽기
▷ 파일 접근 여부 확인 후 읽기 - 동기식
try {
    fs.accessSync(file, fs.F_OK);
    console.log('파일 접근 가능');
    var data = fs.readFileSync(file, 'utf8');
    console.log('파일 내용 : ', data);
}
catch( exception ) {
    // 파일 없음
    console.log('파일없음 : ', excpetion);
}

◎ 파일 접근 여부 확인 후 읽기
▷ 파일 접근 여부 확인 후 읽기 - 비동기식
fs.access(file, fs.F_OK | fs.R_OK, fuction(err) {
    if(err) {
        // 에러 처리
    }
    fs.readFile(file, 'utf8', function(err, data) {
        if(err) {
            // 에러 처리
        }
        
        console.log(data);
    });
});

◎ 파일 상태
▷ 파일 상태 얻기

  • fs.stat(path, callback)
  • fs.statSync(path)
▷ 파일 상태 : fs.stats
  • 파일, 디렉토리 여부 : stats.isFile(), stats.isDirectory()
  • 파일 크기 : stats.size
  • 생성일/접근/수정일 : stats.birthtime, stats.atime, stats.mtime
▷ 파일 상태 확인 : 동기
try {
    var stats = fs.statSync(file)
    console.log('Create : ', stats.birthtime);
    console.log('size : ', stats.size);
    console.log('isFile : ', stats.isFile());
    console.log('isDirectory : ', stats.isDirectory());
}
catch(err) {
    console.error('파일 접근 에러', err);
}

▷ 파일 상태 확인 : 비동기
fs.stat(file, function(err, stats) {
    if(err) {
        console.error('File Stats Error', err);
        return;
    }

    console.log('Create : ', stats.birthtime);
    console.log('size : ', stats.size);
    console.log('isFile : ', stats.isFile());
    console.log('isDirectory : ', stats.isDirectory());
}

▷ 파일 상태 확인 후 읽기
fs.stat(path, function(err, stats) {
    if(stats.isFile()) {
        fs.readFile(path, 'utf-8', function(err, data) {
            console.log('파일 읽기 : ', data);
        });
    }
}

◎ 파일에 저장
▷ 파일에 데이터 저장

  • fs.write(fd, data[, position[, encoding]], callback)
  • fs.writeFile(filename, data[, option], callback)
  • fs.writeFileSync(filename, data[, options])
▷ 파일에 데이터 저장
  • fd, filename : 파일 디스크립터, 파일 경로
  • data : 문자열 혹은 Buffer
  • encoding : 문자열 저장 시 인코딩
▷ 같은 파일 이름 - 덮어쓰기

◎ 파일에 저장
▷ 문자열 데이터 저장
fs.writeFile('./textData.txt', 'Hello World', function(err) {
    if(err) {
        console.error('파일 저장 실패 : ', err);
        return;
    }
    console.log('파일 저장 성공');
});

◎ 파일에 추가
▷ 기존 파일에 내용 추가

  • fs.appendFile(file, data[, options], callback)
  • fs.appendFileSync(file, data[, options])
▷ 파일이 없으면? : 새 파일 생성

▷ 파일에 내용 추가
fs.appendFile(path, 'Additional data', function(err) {
    if(err) {
        console.error('파일 내용 추가 실패 : ', err);
    }
    console.log('파일 내용 추가 성공');
});

◎ 파일 삭제
▷ 파일 삭제

  • fs.unlink(path, callback)
  • fs.unlinkSync(path)
▷ 파일이 없으면 에러
▷ 예제 코드
fs.unlink('./binaryData.da, function(err) {
    if(err) {
        console.error('Delete Error : ', err);
    }
});

◎ 파일 이름 변경/이동
▷ 파일 이름 변경/이동

  • fs.rename(oldPath, newPath, callback)
  • fs.renameSync(oldPath, newPath)
◎ 디렉토리 다루기
▷ 디렉토리 생성

  • 같은 이름의 디렉토리가 있으면 실패
    fs.mkdir(path[, mode], callback), fs.mkdirSync
▷ 디렉토리 삭제
  • 디렉토리가 비어있지 않으면 실패
    fs.rmdir(path, callback), fs.rmdirSync
▷ 예제코드1
fs.mkdir('testdir', function(err) {
    if(err) {
        console.error('mkdir error:', err);
        return;
    }
}
▷ 예제코드2
try {
    fs.rmdirSync('test');
}
catch(error) {
    console.log('디렉토리 삭제 에러');
}

▷ 디렉토리 내 파일 목록

  • fs.readdir(path, callback), fs.readdirSync
▷ 디렉토리가 없으면 에러

▷ 디렉토리 내용 읽기
fs.readdir(path, function(err, files) {
    if(err) {
        console.error('디렉토리 읽기 에러');
        return;
    }
    console.log('디렉토리 내 파일 목록(Async)\n', files);
});

◎ 파일 스트림
▷ 스트림 만들기

  • fs.createReadStream(path[, options])
  • fs.createWriteStream(path[, options])
상세한 내용은 스트림 모듈에서

03.버퍼

◎ 버퍼
▷ JavaScript

  • 문자열 다루는 기능 제고
  • 바이너리 데이터를 다루는 기능이 없음
▷ Buffer : 바이너리 데이터 다루는 모듈
▷ 글로벌이므로 별도의 로딩(require) 불필요

◎ 버퍼 얻기
▷ 파일에서 읽기
var fileBuffer = fs.readFileSync('image.jpg');
▷ 네트워크에서 얻기
socket.on('data', function(data) {
    // data - buffer
});

◎ 버퍼 만들기
▷ 생성 후 크기 변경 불가

  • new Buffer(size)
  • new Buffer(array)
  • new Buffer(str[, encoding])
◎ 버퍼 다루기
▷ 모듈 함수
  • 바이트 길이 - Buffer.byteLength(string[, encoding])
  • 비교 - Buffer.compare(buf1, buf2)
  • 붙이기 - Buffer.concat(list[, totalLength])
  • 버퍼 확인 - Buffer.isBuffer(obj)
  • 인코딩 - Buffer.isEncoding(encoding)
▷ 객체 메소드
  • 길이 - buffer.length
  • 채우기 - buf.fill(value[, offset][, end])
  • 자르기 - buf.slice([start[, end]])
  • 비교하기 - buf.compare(otherBuffer)
  • 복사하기 - buf.copy(targetBuffer[, targetStart][, sourceStart][, sourceEnd])
◎ 문자열과 버퍼
▷ 문자열 - 바이너리 데이터로 다루기
▷ 문자열에서 버퍼 생성
  • new Buffer(str[, encoding])
▷ 문자열 인코딩 필요
  • ascii, utf8, ...
▷ 잘못된 인코딩 -> 에러
▷ 버퍼에 문자열 쓰기
  • buf.write(string[, offset][, length][, encoding])
▷ 변환
  • buf.toString([encoding][, start][, end])
▷ 문자열에서 버퍼 생성
var strBuffer = new Buffer('Hello World');
strBuffer.toString('utf-8');
strBuffer.toString('base64'); // SGVsbG8gV29ybGQ=

▷ 버퍼에서 문자열 작성
var buffer = new Buffer(10);
// 버퍼에 문자열 쓰기
buffer.write('Hello World');
buffer.toString(); // Hello Worl , 크기를 벗어남

▷ 문자열의 바이트 길이

  • Buffer.byteLength(string[, encoding])
▷ 예제
var str1 = 'Hello World';
str1.length // 11
Buffer.byteLength(str1); // 11
// 이모지라는 이모티콘은 문자열 길이와 바이트 사이즈가 틀림

◎ 버퍼 - 데이터 읽기/쓰기
▷ 데이터 읽기/쓰기

  • buf.readInt8(offset[, noAssert])
  • buf.writeInt8(value, offset[, noAssert])
▷ 16비트 크기의 정수형 데이터 읽고 쓰기
  • buf.readUInt16LE(offset[, noAssert])
  • buf.writeUInt16LE(value, offset[, noAssert])

▷ 실수형 데이터 읽고 쓰기
cpu의 인코딩 방식에 따라 LE, BE

  • buf.writeFloatLE(value, offset[, noAssert])
  • buf.writeFloatBE(value, offset[, noAssert])
  • buf.readFloatLE(offset[, noAssert])
  • buf.readFloatBE(offset[, noAssert])
▷ Endian
  • require('os').endianness()
▷ 버퍼 쓰기(value, offset)

  • buffer.writeInt8(0, 0); // 01
  • buffer.writeUInt8(0xFF, 1); // FF
  • buffer.writeUInt16LE(0xFF, 2); // FF 00
  • buffer.writeUInt16BE(0xFF, 4); // 00 FF
  • // 01 FF FF 00 00 FF
▷ 버퍼 읽기

  • buffer.readInt8(0) // 1
  • buffer.readUInt8(1) // 255
  • buffer.readUInt16LE(2) // 255
  • buffer.readUInt16BE(4) // 255

학습정리




◎ 지금까지 'Node.JS의 기본모듈2'에 대해 살펴보았습니다.
▷ 경로 다루기
파일이나 디렉토리를 다루려면 경로를 먼저 알아야 했습니다.
경로 모듈을 이용해서 경로 정보를 상세하게 얻어낼 수 있었습니다.

▷ 파일 시스템
파일 시스템 모듈을 이용해서 파일과 디렉토리를 다룰 수 있었습니다.

▷ 버퍼
2진 데이터를 다루는 타입인 버퍼를 사용해 봤습니다.



2017년 10월 21일 토요일

Chapter2. NodeJS의 기본 모듈1


학습 목표

1. Node.js의 모듈 시스템을 이해할 수 있습니다.
2. 전역 객체를 알고 사용할 수 있습니다.
3. 콘솔, 유틸리티 모듈을 사용할 수 있습니다.
4. 이벤트가 동작하는 원리를 이해할 수 있습니다.

1. 기본 모듈

◎ 기본 모듈

  • Node.js와 함께 설치
  • 별도의 설치 과정 불필요
◎ 홈페이지 > 도큐 먼트
◎ 주요 기본 모듈
◎ 프로세스 환경
  • os, process, cluster
◎ 파일과 경로, URL
  • fs, path, URL, querystring, stream
◎ 네트워크 모듈
  • http, https, net, dgram, dns

2. 전역 객체

◎ 전역 객체(global)
  • 별도의 모듈 로딩없이 사용
  • global 모듈
    global.console.log()
  • global 생략 가능
    console.log()
◎ 주요 전역 객체

  • process
  • console
  • Buffer(클래스)
  • require
  • __filename, __dirname
  • module
  • exports
  • Timeout 함수
◎ 전역객체 : process
▷ 애플리케이션 프로세스 실행 정보
  • env : 애플리케이션 실행 환경
  • version : Node.js 버전
  • arch, platform : CPU 와 플랫폼 정보
  • argv : 실행 명령 파라미터
▷ 이벤트
  • exit : 애플리케이션 종료 이벤트
  • beforeExit : 종료 되기 전에 발생하는 이벤트
  • uncaughtException : 예외 처리되지 않은 이벤트
▷ 함수
  • exit : 애플리케이션 종료
  • nextTick : 이벤트 루프 내 동작을 모두 실행 후 콜백 실행
▷ 프로세스 실행 정보
  • process.env : {TERM_PROGRAM:'iTerm.app',
    SHELL:'/bin/bash',
    TERM:'xterm-256color',
    PWD:'/Users/wannabewize/Projects/TAcademy/Node-
    Samples/BasicModules',
    ITERM_PROFILE:'Default',
    HOME:'/Users/wannabewize',
    LOGNAME:'wannabewize',
    LC_CTYPE:'UTF-8'}
  • process.arch:x64
  • process.platform: darwin
◎ 프로세스 실행 환경
▷ 실행파라미터 얻기
  • process.arv
▷ 실행 환경
  • $ node processAdd.js 3 5
▷ 결과
  • // 0, 1은 node, processAdd.js
    var i = process.argv[2];
    var j = process.argv[3];
    var sum = parseInt(i) + parseInt(j);
    console.log(sum); // 8

3. 타이머

◎ 타이머 함수
  • 지연 동작 : settTimeout
  • 반복 동작 : setInterval
◎ Timeout
▷ 일정 시간 뒤 호출
  • setTimeout(callback, delay, arg, ...)
  • clearTimeout()
▷ 파라미터
  • callback : 함수 형태
  • delay : 초(milli second)
  • arg : callback 함수의 파라미터
▷ 예제 코드
function.sayHello() {
    console.log('Hello World');
}
// 3초뒤 실행
setTimeout(sayHello, 3*1000);

▷ 타이머 취소
var t = setTimeout(sayHello, 10);
clearTimeout(t);

▷ 반복
setInterval(callback, delay, arg, ...)
clearInterval()

▷ 예제 코드
function sayGoodbye(who) {
    console.log('Good bye', who);
}
setInterval(sayGoodbye, 1*1000, 'Friend');

4. 콘솔

◎ 콘솔(Console)
  • 로그 남기기
  • 실행 시간 측정
◎ 수준별 로그 남기기
  • console.info()
  • console.log()
  • console.warn()
  • console.error
◎ 로그 남기기
▷ 로그 남기기 예
  • console.log('log', 'log message');
  • console.info('info', 'info message');
  • console.warn('warn', 'warn message');
  • console.error('error', 'error message');
▷ 값 출력
var intValue = 3;
console.log('int Value ' + 3);

▷ 객체형 출력
var obj = {
    name : 'IU',
    job : 'Singer'
}
console.log('obj: ' + obj);
console.log('obj: ', obj);

◎ 커스텀 콘솔
▷ 콘솔 타입 로딩

  • var Console = require('console').Console;
▷ 콘솔 객체 생성
  • new Console(stdout[, stderr])
▷ 파라미터 : 출력 스트림
  • stdout : 표준 출력 스트림, infor, log
  • stderr : 에러 출력, warn, error
▷ 파일로 로그 남기는 커스텀 콘솔

  • var output = fs.createWriteStream('./stdout.log');
  • var errorOutput = fs.createWriteStream('./stderr.log');
  • var logger = new Console(output, errorOutput);
◎ 실행 시간 측정
▷ 콘솔 객체로 실행 시간 측정하기
▷ 시작 시점 설정하기
  • console.time(TIMER_NAME)
▷ 종료 시점, 걸린 시간 계산해서 출력
  • console.timeEnd(TIMER_NAME)
▷ 예제 코드
console.time('SUM');
var sum = 0;
for(var i = 1; i < 100000 ; i++) {
    sum += i;
}

console.log('sum : ', sum);

// 시간 측정 시작
console.timeEnd('SUM');

5. 유틸리티

◎ 유틸리티 모듈

◎ 모듈 로딩

  • var util = require('util');
◎ 주요 기능
  • 문자열 포맷
  • 상속
  • is 함수(deprecated)
◎ 유틸리티 - 포맷
▷ 문자열 포맷
  • util.format(format[, ...])
▷ placeholder(형식문자열)
  • %s : String
  • %d : Number
  • %j : JSON
▷ 예제 코드
var str1 = util.format('%d + %d = %d', 1, 2, (1+2));
=> 1 + 2 = 3
var str2 = util.format('%s %s', 'Hello', 'World');
=> Hello World

◎ 유틸리티 - 상속
▷ 상속 : inherits
  • util.inherits(constructor, superConstructor)
▷ 사용 방법
  • util.inherits(ChildClassFunction, ParentClassFunction);
▷ 예제 코드
finction Parent() {
}

function Child() {
}

util.inherits(Child, Parent);

▷ 예제 코드
finction Parent() {
}
Parent.prototype.sayHello = function() {
    console.log('Hello. from Parent Class');
}
function Child() {
}

util.inherits(Child, Parent);

var child = new Child();
child.sayHello();

6. 이벤트

◎ 이벤트 모듈

  • 이벤트 다루기 : EventEmitter
  • 이벤트를 다루는 기능 제공
◎ Node.js 애플리케이션의 이벤트들

▷ 이벤트의 예

  • 클라이언트의 접속 요청
  • 소켓에 데이터 도착
  • 파일 오픈/읽기 완료
▷ 이벤트 처리
  • 비동기 처리
  • 리스너 함수
◎ 이벤트를 다룰 수 있는 타입 : Readline 모듈
▷ Class : Interface
  • rl.close()
  • rl.pause()
▷ Events
  • Event : 'close'
  • Event : 'line'
  • Event : 'pause'
  • Event : 'resume'
  • Event : 'SIGCONT'
  • Event : 'SIGINT'
◎ 타입에 정의된 이벤트 다루기
▷ 이벤트 리스너 함수 등록

  • emitter.addListener(event, listener)
  • emitter.on(event, listener)
  • emitter.once(event, listener)
◎ 이벤트 리스너 등록
▷ 이벤트 리스너 등록 예
process.on('exit', function() {
    console.log('occur exit event');
});

// 한번만 동작
process.once('exit', function() {
    console.log('occur exit event');
});

◎ 이벤트 리스너 함수 삭제

  • emitter.removeListener(event, listener)
  • emitter.removeAllListeners([event])
◎ 최대 이벤트 핸들러 개수(기본 10개)
  • emitter.setMaxListener(n)
  • emitter.getMaxListener()
◎ 실습
▷ 애플리케이션 종료 이벤트
  • process.on('exit', function(code))
▷ 예외처리 되지 않는 상황 - 앱 죽는 상황!
  • process.on('uncaughtException', uncaughtExceptionListener);
◎ 이벤트 발생
▷ 이벤트 발생 시키기(emit)
  • emitter.emit(event[, arg1][, arg2][, ...])
  • event : 이벤트 이름
  • arg : 리스너 함수의 파라미터
  • emit 함수 호출 결과 : true(이벤트 처리), false(이벤트 처리 안됨)
▷ 예
process.emit('exit');
process.emit('exit', 0); // 리스너 함수의 파라미터로 0 전달

◎ 커스텀 이벤트
▷EventEmitter 객체에 커스텀 이벤트
var customEvent = new event.EventEmitter();

customEvent.on('tick' function() {
    console.log('occur custom event');
});

customEvent.emit('tick');

customEvent가 EventEmitter 객체가 아니라면 on 이라는 함수가 정의되지
않았기 때문에 에러가 나게 되면서 프로그램이 crash되게 됨.

◎ 커스텀 이벤트, 상속
▷ util 모듈을 이용해서 EventEmitter 상속
var Person = fuction();
// 상속
var util = require('util');
var EventEmitter = require('events').EventEmitter;
util.inherits(Persion, EventEmitter);

// 객체
var p = new Persion();
p.on('howAreYou', function() {
    console.log('Fine, Thank you and you?')
});

// 이벤트 발생
p.emit('howAreYou');

여기서 잠깐!

※ 리스너 함수, 에러
◎ 모든 리스너 함수의 첫 번재 파라미터 : 에러
▷ 에러 처리
emitter.on('event', function(error, result) {
    if(error) {
        // 에러 처리
    }
    else {
        // 정상 처리
    }
}

학습정리

◎ 지금까지 'Node.JS의 기본모듈!'에 대해 살펴보았습니다.
기본 모듈
별도의 설치 과정 없이 사용할 수 있는 모듈로 Node.js와 함께 설치된다.

전역객체
global 모듈에 속하는 객체와 함수로 모듈 로딩 과정 없이 사용할 수 있다. console,
timeout, __dirname, process 등이 있다.

타이머
타이머 함수인 setTimeout()이나 setInterval() 함수를 이용해서 일정 시간 뒤에
동작하거나, 주기적으로 동작하는 기능을 작성할 수 있다.

콘솔
콘솔(Console)을 이용해서 콘솔 화면에 내용을 출력할 뿐만 아니라 실행 시간을
측정할 수 있다.

유틸리티
유틸리티 모듈을 이용해서 형식 문자열을 작성할 수 있었다. 그리고 클래스 간에
상속 관계를 만들 수 있다.

이벤트
이벤트를 다루는 EventEmitter의 특징과 이벤트를 다루는 방법을 알아봤다. 그리고
유틸리티 모듈의 상속을 통해서 커스텀으로 작성한 타입에서도 이벤트를 다룰 수
있다.









2017년 10월 20일 금요일

Chapter1. NodeJS의 개요


학습 목표

1. Node.js의 특징을 이해할 수 있습니다.
2. Node.js 프로그래밍 방법을 이해할 수 있습니다.
3. Node.js개발 환경을 준비할 수 있습니다.
4. API 문서를 보고 코드 작성을 할 수 있습니다.

1. Node.js 소개

◎ Node.js

  • 2009년 Ryan Dahl
  • 자바 스크립트 언어
  • 크롬 V8 엔진
◎ Node.js의 특징
  • 싱클 쓰레드
  • 비동기 I/O
  • 이벤트 기반(event driven)
네트워크 애플리케이션에 적당
- 인터넷 서비스하는 서버
- 디스크, 데이터베이스기반 I/O 
기존 멀티스레드 : 스레드 다루기 어려움, 스레드 개수가 많아질수록 성능↓, 동기식 I/O 기반
싱글 스레드 기반 : 서버작성하는 코드 다루기 쉬움, 비동기 I/O, 성능 향상
atom.io : 데스크탑 에디터의 기반

◎ 비동기 I/O
▷ 시간이 걸리는 I/O
  • 하드 디스크 접근
  • 데이터베이스 서버
  • 네트워크를 이용해서 다른 서비스 접근
▷ I/O 동작이 끝날 때까지 대기 : 동기식
▷ I/O 동작이 끝날 때가지 대기하지 않음 : 비동기식

◎ 비동기 I/O의 장점
아파치 vs Nginx
서능 및 메모리 사용 측면에서 유리

◎ Node.js의 장점
  • 싱글 쓰레드로 작성
  • 비동기 I/O
  • 간단한 구조의 경량 프레임워크와 풍부한 라이브러리
  • 서버와 클라이언트에서 사용하는 언어가 같다.(JavaScript)
◎ Node.js 권장 분야
  • 실시간 소셜 네트워크 서비스
  • 데이터 중심의 서비스
  • IoT 기기 연동
◎ 아키텍처
▷ 상위레벨 - JavaScript
▷ 로우레벨 - C
  • 바인딩
  • V8 엔진
  • libev : Event
  • libeio : I/O
◎ Node.js 홈페이지(nodejs.org)

◎ Node.js 재단
▷ Node.js 재단
  • Node.js 플랫폼과 관련된 모듈 개발 지원하는 협업 오픈 소스 프로젝트
  • open governance model
  • 기술 결정 위원회(Technical Steering Committee)
▷ 주요 멤버
  • IBM, intel, Joyent, Microsoft, PayPal, redhat
◎ 버전 구성과 지원
  • Node.js 버전을 두 단계로 진행
  • 기존 : 짝수버전(Stable), 홀수버전(Unstable)
  • 4.x 이후:Stable, LTS
  • LTS : 짝수 버전 Stable 6개월 이후 LTS로 전환
    LTS(Long Term Support)
    LTS : 호환성이 깨지는 변경 없음.
    LTS 18개월. 그후 Maintain 상태(12개월)
    매년 새로운 메이저 버전의 LTS 시작
  • 현재 4.2가 LTS 상태(2016. 9. 28)
    현재 4.2가 LTS 상태(v4.2.0->v4.2.3)

2. 프로그래밍 모델

◎ 프로그래밍 모델
  • 동기(Synchronous)
    A실행 - A결과 - B실행 - B결과
    실행이 끝나고 다음 실행
  • 비동기(Asynchronous)
    A실행 - B실행 - (B결과) - (A결과)
    실행 결과가 끝날 때까지 기다리지 않는다.
◎ 동기식
▷ 파일 읽기
var fs = require('fs');                             // 1
var content = fs.readFileSync("read.txt", "utf8");  // 2
console.log(content);                               // 3
console.log('Reading file...');                     // 4

◎ 비동기식
▷ 파일 읽기
순차적으로 실행되지 않음.

var fs = require('fs');                                      // 1
fs.readFile("readme.txt", "utf8", function(err, content) {   // 2
    console.log(content);                                    // 4
});
console.log('Reading file...');                              // 3

◎ 동기/비동기 방식의 코드 차이점
▷ 동기식 함수 구현과 사용
동기식 함수 구현
function add(i, j) {
    return i + j;
}

동기식 함수 사용
var result = add(1,2);
console.log('Result: ', reslt);

▷ 비동기식 함수 구현과 사용
비동기식 함수 구현
function add(i, j, callback) {
    var result = i + j;
    callback(result);
}

비동기식 함수 사용
add(1, 2, function(result) {
    console.log('Result:', result);
});

◎ 비동기 방식의 API로 파일 읽는 코드 예
▷ 콜백을 이용한 파일 읽기
fs.readFile('textfile.txt', 'utf8', function(err, text) {
    console.log('Read File Async', text);

◎ 콜백 함수 형태
▷ 비동기 함수의 에러 처리
콜백 함수의 파라미터로
▷ 대부분 비동기 API
callbackFunc(arg1, arg2, function(error, result) {
    if(error) {
        // 에러 처리
        return;
    }
    // 정상 처리
}

3. Node.js 개발환경

◎ 다운로드
▷  홈페이지(nodejs.org)
▷ 플랫폼에 맞는 설치 파일 다운로드
▷ 설치
◎ 설치
▷ 설치 완료
▷ 환경 설정
▷ 자동으로 환경 설정 안되면 수동 설정

  • node 설치 폴더 위치 설정

▷ 콘솔에서 node 명령 실행
▷ node -v

  • v : 버전 확인 옵션
▷ node 콘솔 명령
▷ node [SOURCE.JS][ARGS]
  • v : 버전
  • e, p : 스크립트 평가
  • c : 실행하지 않고 문법 체크
  • r : 모듈을 미리 로딩
◎ REPL
▷ 콘솔 기반의 실행 환경
◎ 개발도구
▷ IDE : Eclipse, WebStorm
▷ Editor : Visual Studio Code, Sublime, ...
◎ 편집기
▷ 비주얼 스튜디오 코드(무료!)
  • http://code.visualstudio.com
  • 설치
▷ 개발툴 환경 설정
  • 사이트에서 Node 설정 보기
  • 콘솔에서 code

4. Hello World

◎ 코드 작성 : helloWorld.js
▷ console.log('Hello World!');
◎ 실행
▷ node helloWorld.js
◎ 서버 코드
▷ helloWorld2.js

var http = require('http');
http.createServer(function(request, response) {
    response.writeHead(200, {'Content-Type':'text/html'});
    response.end('Hello World!');
}).listen(3000);

▷ 웹브라우저로 확인
127.0.0.1:3000

5. 도큐먼트

◎ 모듈

  • Node.js 간단한 구조
  • 필요한 모듈을 로딩
  • 모듈 : 다른 언어의 라이브러리에 해당
◎ 도큐먼트 보기
  • Node.js 사이트
  • DOCS
  • API
◎ API 문서
◎ API 문서 보기
▷ API 안정도
▷ Stability
  • 0 : Deprecated
  • 1 : Experimental
  • 2 : Stable
  • 3 : Locked
◎ 모듈 사용하기
▷ 모듈 문서 보는 법을 알아보자!
▷ 모듈 Readline
  • 클래스 : Interface
  • 메소드
  • 이벤트
  • 모듈 함수
◎ 모듈 로딩
▷ 모듈 로딩
  • require('모듈 이름')
  • 절대 경로 혹은 상대 경로
    var readline = require('readline');
▷ 모듈 종류
  • 기본 모듈 : 미리 컴파일된 상태로 라이브러리 디렉토리 - 설치 불 필요
  • 확장 모듈 : npm으로 별도 설치
◎ 모듈 로딩 위치
▷ 기본 모듈 로딩 위치
  • Node.js 라이브러리 디렉토리
▷ 확장 모듈
  • 같은 폴더
  • node_modules 이름의 폴더(npm)
  • 상위 폴더의 node_modules(npm)
◎ 모듈 로딩 - 객체 생성
▷ 클래스
  • Interface
▷ 모듈 로딩과 객체 생성
var readline = require('readline');
var rl = readline.createInterface();

▷ 객체 생성 함수 옵션
var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

◎ 메소드 사용
▷ 모듈 로딩, 객체 생성 후 메소드 사용
var readline = require('readline');
var rl = readline.createInterface();
rl.setPrompt('>>');

◎ 이벤트
▷ 이벤트 - 이벤트 핸들러
▷ .on([이벤트 이름], [리스너 함수])
rl.on('line', function(cmd) {
    console.log('You just typed: ' + cmd);
});

▷ 리스너 함수의 파라미터

  • API 문서 참조
◎ 모듈 함수
▷ 객체 생성 없이 모듈에 직접 사용
var readline = require('readline');
readline.cursorTo(process.stdout, 60, 30);


  • readline.cursorTo(stream, x, y);
  • readline.moveCursor(stread, dx, dy);
  • readline.clearLine(stream, dir);
  • readline.clearScreenDown(stream);

학습정리

◎ 지금가지 'Node.JS의 개요'에 대해 살펴보았습니다.
  • Node.js란
    비동기 방식으로 자바 스크립트 언어를 이용해서 네트워크 애플리케이션 플랫폼 제작에 적합한 프레임워크
  • Node.js의 프로그래밍 모델
    비동기 방식으로 작성하고 콜백을 이용하는 방식으로 코드를 작성합니다.
  • Node.js 환경
    Node.js를 다운로드해서 설치하고 개발 환경을 준비했습니다.
  • Hello World
    Node.js 애플리케이션을 작성하고 실행해봤습니다.
  • 도큐먼트 보기
    API 문서를 보고 모듈을 로딩하고 객체를 생성해봤습니다.
    그리고 함수 실행하는 방법을 알아봤습니다.

2017년 9월 24일 일요일

5. ENERGY USE CASES
6. ENTERPRISE USE CASES
7. HEALTHCARE USE CASES
8. PUBLIC SERVICES USE CASES
9. RESIDENTIAL USE CASES
9.1 Home Energy Management
9.1.1 Description
이 유스 케이스는 집에서 소비자가 매일 그들 집의 에너지 소비량을 확인하고 홈가전에 대한 원격 액션을 통해 이 소비를 제어할 수 있도록 하여  에너지 소비를 관리하는 것이다. 에너지 데이터를 수집하고 각 데이터를 소비자와 장비들 또는 시장과 시장에 전달함으로써 혁신적인 서비스가 개발될 수 있다.
유스 케이스는 전기 홈 네트워크로부터 에너지 정보를 수집하고 데이터를 수집하고 처리하기 위한 M2M 시스템에 정보를 전달하는 홈 Energy Gateway(EGW)에 집중한다. 그러면 수집된 데이터로부터 서비스가 개발될 수 있다.
EGW는 다음과 같이 다양한 소스(sensors, context)로 부터 수신된 데이터의 초기 처리를 수행한다.

  • 수집된 정보를 합치고 처리한다.
  • 원격의 M2M 시스템으로 어떤 정보를 전달한다. 예를 들어 M2M 시스템을 통해 경보를 전달한다.
  • 어떤 actuators나 appliances의 즉각적인 활성화를 위해 지역적으로 몇몇 정보를 사용한다.
  • 전체적이거나 개별적인 appliance의 소비 정보를 위한 home electrical meter를 포함하여 홈 장치에 연결된다(무선이나 유선으로)
  • 보여줄 수 있는 소비된 에너지 관련 정보를 end-user나 소비자 터미널에 제공한다.(PC, mobile phone, tablet, TV screen, 등)

Ref:[i.6] {HGI-GD017-R3 (Use Cases and Architecture for a Home Energy Management Service}
9.1.2 Srouce
oneM2M-REQ-2012-0058R03 Home Energy Management
Note: from [i.2] ETSI TR 102 935 v2.1.1
9.1.3 Actors
  • User : 홈 가전의 사용자
  • Communication operators : 어떤 프로토콜(예를 들어 지그비, PLC, 블루투스4.0 ...) 을 통해 수집된 정보를 EGW로 전달하고 EGW에서 M2M 시스템으로 전달하는 것을 담당하는
  • Energy gateway SP : 가전으로부터 M2M 시스템으로 까지의 에너지 정보를 수집하고 전송하며 M2M 시스템으로부터 원격 제어/명령을 안전하게 수신하는 역할을 담당하는
  • Application Service Provider : M2M 시스템을 통하여 사용자에게 Home Energy Management(HEM) 어플리케이션을 제공한다.
9.1.4 Pre-conditions
None
9.1.5 Triggers
None
9.1.6 Normal Flow
Figure9-1 Home Energy Management Normal Flow
  1. HEM 어플리케이션(M2M 디바이스)가 home device(s) 정보를 위해 System Operator/SP에 등록한다.
  2. 집에서 M2M 장치(smart meters, eletric lightening, fridge, washing machine 등)일 수 있는 device로부터의 정보는 communication network operator를 통해 Energy Gateway Operator(EGW)에 의해 수집된다. 정보는 방, 온도, 사용, 에너시 소비(room, temperature, occupancy, energy consumption) 등이 될 수 있다.
  3. 수집된 정보는 EGW SP에 저장이 되고 energy gateway에서 처리될 수 있다. 결과적으로 제어 메시지는 energy gateway에 저장된 정책에 따라 energy GW로부터 장치로 돌려보내질 것이다.
  4. 또한 수지보딘 정보는 통신 네트워크를 통해 저장을 위한 M2M 서비스 플랫폼을 포함하고 있는 system operator로 보내질 것이다.
  5. 등록된 어플리케이션(HEM)는 처리할 수 있는 정보를 통보받는다. 그것의 M2M operator는 subscription profile에 따라 HEM 어플리케이션으로 정보를 보내기전에 정보를 처리할 수 있다.
  6. HEM 어플리케이션은 공유되고 수집된 정보에 반응하고 system operator를 통해 제어 메시지를 보낼 수 있다.(예를 들어 홈 장치를 스위치하기 위해 light/appliance 또는 washing machine 등으로)
  7. 제어는 다른 operator를 통과하여 대응하는 M2M 장치로 전파되다.
9.1.7 Alternative Flow
None
9.1.8 Post-conditions
None
9.1.9 High Level Illustration
Figure 9-2 Home Energy Management System High Level Illustration
9.1.10 Potential Requirements
  1. 다음에 요약된 WAMS use case의 그것과 유사하다.
    1. Data collection 과 reporting capability/function
    2. M2M 장치의 원격 제어
    3. 복수의 어플리케이션으로의 정보 수집과 전송
    4. 데이터 저장과 공유
    5. M2M 장치와 collectors로 M2M 시스템의 인증
    6. M2M 어플리케이션으로 M2M 시스템의 인증
    7. 데이터 통합
    8. 네트워크 연결 남용의 방지
    9. Privacy
    10. 어플리케이션 레벨에서의 보안 증명(Security credential) 과 소프트웨어 업그레이드
    11. 더불어 다음 요구사항들을 필요로 한다.
    12. M2M 시스템은 Gateway를 지원할 것이다.
    13. Gateway는 가구 당 또는 복수의 가구당 존재할 수 있다. 예를 들어 Gateway Concentrator
  2. Configuration Management
  3. M2M Devices와 Gateway의 Pre provisioning
    1. M2M 시스템은 M2M Devices/Gateways의 간단하고 확장가능한 pre provisioning을 수행하는 메커니즘을 지원할 것이다.
  4. 복수의 M2M Devices/Gateways의 관리
    1. M2M 어플리케이션 예를 들어 HEM 어플리케이션은 직접 혹은 M2M Service Capabilityies를 통하여서든 하나 이상의 M2M Devices/Gateways과 상호작용 할 수 있을 것이다. 예를 들어 정보 수집, 제어를 위해
    2. HEM 어플리케이션은 소비자들에게 특정 에너지 등급을 제공하기 위해 에너지 파트너들에게 익명의 데이터를 공유할 수 있을 것이다.
  5. 통보를 수신하기 위해 등록을 위한 지원
    1. M2M 시스템은 어플리케이션이 등록하고 변화를 통보받을 수 있도록 하는 메커니즘을 지원할 것이다.
    2. M2M 시스템 operator는 등록하기 위한 HEM 어플리케이션의 등록을 지원할 수 있을 것이다.
  6. 통지의 최적화 지원
    1. M2M 시스템은 혼잡한 통신 네트워크의 상황에서 연결된 장치의 지연된 통지를 위한 메커니즘을 지원할 수 있을 것이다.
  7. 저장과 전달의 지원
    1. M2M 시스템은 다른 연결된 장치들로부터의 정보의 원격접근을 관리하는 메커니즘을 지원할 수 있을 것이다. 지원하는 시점에 M2M 시스템은 주어진 지연과 혹은 범주에 따라 요청을 합치고 요청의 수행을 지연시킬 수 있을 것이다. 예를 들어 M2M 어플리케이션이 장치와 실시간으로 연결될 필요가 없을 때
9.2 Home Energy Management System(HEMS)
10. RETAIL USE CASES
11. TRANSPORTATION USE CASES
12. OTHER USE CASES

2017년 8월 18일 금요일

Testing UI for Multiple Apps

Testing UI for Multiple Apps


Dependencies and Prerequisites

This lesson teaches you to

  1. Set Up UI Automator
  2. Create a UI Automator Test Class
  3. Run UI Automator Tests on a Device or Emulator

You should also read

Try it out

다중앱을 커버하는 사용자 상호작용과 연관된 UI 테스트는 사용자 흐름이 다른 앱들과 시스템 UI와 얽혀있을때 앱이 정확하게 동작하는지 확인해준다. 그러한 흐름의 예는 텍스트를 입력하는 메시징 앱이 안드로이드 contact picker를 띄워서 사용자가 메시지를 보낼 수신자를 선택할 수 있게 하고 나서 사용자가 메시지 보내기 위해 원래 앱으로 제어를 리턴하도록 하는 것이다.
이 문서는  Android Testing Support Library 가 제공하는  UI automator test framework를 사용하여 어떻게 UI 테스트를 작성하는가를 다룬다. UI Automator API는 어떤 액티비티가 포커스를 가지고 있는지 상관없이 장치의 보이는 요소들과 상호작용할 수 있도록 해준다. 당신의 테스트는 컴포넌트에 보여지는 텍스트나 그것의 컨텐츠 디스크립터와 같은 편리한 디스크립터를 사용하여 UI 컴포넌트를 찾을 수 있다. UI Automator 테스트는 안드로이드 4.3(API level 18) 이상에서 동작하는 장치에서 실행할 수 있다. UI Automator 테스팅 프레임워크는 장치 기반의 API이고 Android Testing Support Library test runner와 함께 동작한다.

Set Up UI Automator


UI automator를 사용하여 당신의 UI test를 작성하기 전에 Getting Started with Testing 에 나와 있는데로 당신의 테스트 코드 위치와 프로젝트 디펜던시를 설정해야 한다.
당신의 안드로이드 앱 모듈에 있는 build.gradle 파일에 UI Automator library에 대한 의존성 참조를 셋팅해야 한다:

dependencies {
    ...
    androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1'
}
 
 
UI Automator 테스팅을 최적화 하기 위해서는 먼저 타겟 앱의 UI 컴포넌트들을 조사하고 그것들에 접근 가능한지 확인해야 한다. 이들 최적화 팁은 다음 두 섹션에서 설명한다.

Inspecting the UI on a device

테스트를 디자인하기 전에, 장치에 보이는 UI 컴포넌트들을 조사한다. 당신의 UI Automator 테스트가 이들 컴포넌트에 접근할 수 있는지 확인하기 위해 이들 컴포넌트들이 보이는 텍스트 라벨과 android:contentDescription 혹은 둘다를 가지고 있는지 체크한다.
장치의 foreground 상에 보이는 UI 컴포넌트들의 특성을 보고 레이아웃 계층을 조사하기 위한 편리한 비주얼 인터페이스를 제공하는 uiautomatorviewer 툴을 제공한다. 이 정보들은 UI Automator를 사용하여 더 매끄러운 테스트를 작성할 수 있도록 해준다. 예를 들어 당신은 특정한 보이는 속성과 매치하는 UI selector를 작성할 수 있다.

uiautomatorviewer 툴을 띄우기 위해:
To launch the uiautomatorviewer tool:
  1. 물리 장치에 타겟 앱을 띄운다.
  2. 개발 머신에 물리장치를 연결한다.
  3. 터미널 윈도우를 열어서 <android-sdk>/tools/ 디렉토리로 이동한다.
  4. 다음 명령을 이용하여 툴을 실행한다.:
    $ uiautomatorviewe
애플리케이션을 위한 UI 속성들을 보기 위해:
  1. uiautomatorviewer 인터페이스 상에서 , Device Screenshot 버튼을 클릭한다..
  2. uiautomatorviewer 툴에 의해 인식된 UI 컴포넌트들을 보여주는 왼족 패널 상의 snapshot 상에 마우스 커서를 가져다댄다. 속성들이 오른쪽 패널 하단에 리스팅되고 오른쪽 패널 상단에는 레이아웃 계층이 표시된다.
  3. 선택적으로 UI Automator에서 접근할 수 없는 UI 컴포넌트들을 보기 위해서는 Toggle NAF Nodes 버튼을 클릭한다. 이들 컴포넌트를 위해서는 제한된 정보만 사용할 수 있다.
안드로이드에 의해 제공되는 UI 컴포넌트들의 일반적인 타입들에 대해 알고 싶다면 User Interface.를 보아라.

Ensuring your Activity is accessible

The UI Automator test framework performs better on apps that have implemented Android accessibility features. When you use UI elements of type View, or a subclass of View from the SDK or Support Library, you don't need to implement accessibility support, as these classes have already done that for you.
Some apps, however, use custom UI elements to provide a richer user experience. Such elements won't provide automatic accessibility support. If your app contains instances of a subclass of View that isn't from the SDK or Support Library, make sure that you add accessibility features to these elements by completing the following steps:
  1. Create a concrete class that extends ExploreByTouchHelper.
  2. Associate an instance of your new class with a specific custom UI element by calling setAccessibilityDelegate().
For additional guidance on adding accessibility features to custom view elements, see Building Accessible Custom Views. To learn more about general best practices for accessibility on Android, see Making Apps More Accessible.

Create a UI Automator Test Class


Your UI Automator test class should be written the same way as a JUnit 4 test class. To learn more about creating JUnit 4 test classes and using JUnit 4 assertions and annotations, see Create an Instrumented Unit Test Class.
Add the @RunWith(AndroidJUnit4.class) annotation at the beginning of your test class definition. You also need to specify the AndroidJUnitRunner class provided in the Android Testing Support Library as your default test runner. This step is described in more detail in Run UI Automator Tests on a Device or Emulator.
Implement the following programming model in your UI Automator test class:
  1. Get a UiDevice object to access the device you want to test, by calling the getInstance() method and passing it an Instrumentation object as the argument.
  2. Get a UiObject object to access a UI component that is displayed on the device (for example, the current view in the foreground), by calling the findObject() method.
  3. Simulate a specific user interaction to perform on that UI component, by calling a UiObject method; for example, call performMultiPointerGesture() to simulate a multi-touch gesture, and setText() to edit a text field. You can call on the APIs in steps 2 and 3 repeatedly as necessary to test more complex user interactions that involve multiple UI components or sequences of user actions.
  4. Check that the UI reflects the expected state or behavior, after these user interactions are performed.
These steps are covered in more detail in the sections below.

Accessing UI Components

The UiDevice object is the primary way you access and manipulate the state of the device. In your tests, you can call UiDevice methods to check for the state of various properties, such as current orientation or display size. Your test can use the UiDevice object to perform device-level actions, such as forcing the device into a specific rotation, pressing D-pad hardware buttons, and pressing the Home and Menu buttons.
It’s good practice to start your test from the Home screen of the device. From the Home screen (or some other starting location you’ve chosen in the device), you can call the methods provided by the UI Automator API to select and interact with specific UI elements.
The following code snippet shows how your test might get an instance of UiDevice and simulate a Home button press:
import org.junit.Before;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.Until;
...
@RunWith(AndroidJUnit4.class)
@SdkSuppress(minSdkVersion = 18)
public class ChangeTextBehaviorTest {

    private static final String BASIC_SAMPLE_PACKAGE
            = "com.example.android.testing.uiautomator.BasicSample";
    private static final int LAUNCH_TIMEOUT = 5000;
    private static final String STRING_TO_BE_TYPED = "UiAutomator";
    private UiDevice mDevice;

    @Before
    public void startMainActivityFromHomeScreen() {
        // Initialize UiDevice instance
        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());

        // Start from the home screen
        mDevice.pressHome();

        // Wait for launcher
        final String launcherPackage = mDevice.getLauncherPackageName();
        assertThat(launcherPackage, notNullValue());
        mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)),
                LAUNCH_TIMEOUT);

        // Launch the app
        Context context = InstrumentationRegistry.getContext();
        final Intent intent = context.getPackageManager()
                .getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE);
        // Clear out any previous instances
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
        context.startActivity(intent);

        // Wait for the app to appear
        mDevice.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)),
                LAUNCH_TIMEOUT);
    }
}
In the example, the @SdkSuppress(minSdkVersion = 18) statement helps to ensure that tests will only run on devices with Android 4.3 (API level 18) or higher, as required by the UI Automator framework.
Use the findObject() method to retrieve a UiObject which represents a view that matches a given selector criteria. You can reuse the UiObject instances that you have created in other parts of your app testing, as needed. Note that the UI Automator test framework searches the current display for a match every time your test uses a UiObject instance to click on a UI element or query a property.
The following snippet shows how your test might construct UiObject instances that represent a Cancel button and a OK button in an app.
UiObject cancelButton = mDevice.findObject(new UiSelector()
        .text("Cancel"))
        .className("android.widget.Button"));
UiObject okButton = mDevice.findObject(new UiSelector()
        .text("OK"))
        .className("android.widget.Button"));
// Simulate a user-click on the OK button, if found.
if(okButton.exists() && okButton.isEnabled()) {
    okButton.click();
}

Specifying a selector

If you want to access a specific UI component in an app, use the UiSelector class. This class represents a query for specific elements in the currently displayed UI.
If more than one matching element is found, the first matching element in the layout hierarchy is returned as the target UiObject. When constructing a UiSelector, you can chain together multiple properties to refine your search. If no matching UI element is found, a UiAutomatorObjectNotFoundException is thrown.
You can use the childSelector() method to nest multiple UiSelector instances. For example, the following code example shows how your test might specify a search to find the first ListView in the currently displayed UI, then search within that ListView to find a UI element with the text property Apps.
UiObject appItem = new UiObject(new UiSelector()
        .className("android.widget.ListView")
        .instance(0)
        .childSelector(new UiSelector()
        .text("Apps")));
As a best practice, when specifying a selector, you should use a Resource ID (if one is assigned to a UI element) instead of a text element or content-descriptor. Not all elements have a text element (for example, icons in a toolbar). Text selectors are brittle and can lead to test failures if there are minor changes to the UI. They may also not scale across different languages; your text selectors may not match translated strings.
It can be useful to specify the object state in your selector criteria. For example, if you want to select a list of all checked elements so that you can uncheck them, call the checked() method with the argument set to true.

Performing Actions

Once your test has obtained a UiObject object, you can call the methods in the UiObject class to perform user interactions on the UI component represented by that object. You can specify such actions as:
The UI Automator testing framework allows you to send an Intent or launch an Activity without using shell commands, by getting a Context object through getContext().
The following snippet shows how your test can use an Intent to launch the app under test. This approach is useful when you are only interested in testing the calculator app, and don't care about the launcher.
public void setUp() {
    ...

    // Launch a simple calculator app
    Context context = getInstrumentation().getContext();
    Intent intent = context.getPackageManager()
            .getLaunchIntentForPackage(CALC_PACKAGE);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
            // Clear out any previous instances
    context.startActivity(intent);
    mDevice.wait(Until.hasObject(By.pkg(CALC_PACKAGE).depth(0)), TIMEOUT);
}

Performing actions on collections

Use the UiCollection class if you want to simulate user interactions on a collection of items (for example, songs in a music album or a list of emails in an Inbox). To create a UiCollection object, specify a UiSelector that searches for a UI container or a wrapper of other child UI elements, such as a layout view that contains child UI elements.
The following code snippet shows how your test might construct a UiCollection to represent a video album that is displayed within a FrameLayout:
UiCollection videos = new UiCollection(new UiSelector()
        .className("android.widget.FrameLayout"));
// Retrieve the number of videos in this collection:
int count = videos.getChildCount(new UiSelector()
        .className("android.widget.LinearLayout"));
// Find a specific video and simulate a user-click on it
UiObject video = videos.getChildByText(new UiSelector()
        .className("android.widget.LinearLayout"), "Cute Baby Laughing");
video.click();
// Simulate selecting a checkbox that is associated with the video
UiObject checkBox = video.getChild(new UiSelector()
        .className("android.widget.Checkbox"));
if(!checkBox.isSelected()) checkbox.click();

Performing actions on scrollable views

Use the UiScrollable class to simulate vertical or horizontal scrolling across a display. This technique is helpful when a UI element is positioned off-screen and you need to scroll to bring it into view.
The following code snippet shows how to simulate scrolling down the Settings menu and clicking on an About tablet option:
UiScrollable settingsItem = new UiScrollable(new UiSelector()
        .className("android.widget.ListView"));
UiObject about = settingsItem.getChildByText(new UiSelector()
        .className("android.widget.LinearLayout"), "About tablet");
about.click();

Verifying Results

The InstrumentationTestCase extends TestCase, so you can use standard JUnit Assert methods to test that UI components in the app return the expected results.
The following snippet shows how your test can locate several buttons in a calculator app, click on them in order, then verify that the correct result is displayed.
private static final String CALC_PACKAGE = "com.myexample.calc";
public void testTwoPlusThreeEqualsFive() {
    // Enter an equation: 2 + 3 = ?
    mDevice.findObject(new UiSelector()
            .packageName(CALC_PACKAGE).resourceId("two")).click();
    mDevice.findObject(new UiSelector()
            .packageName(CALC_PACKAGE).resourceId("plus")).click();
    mDevice.findObject(new UiSelector()
            .packageName(CALC_PACKAGE).resourceId("three")).click();
    mDevice.findObject(new UiSelector()
            .packageName(CALC_PACKAGE).resourceId("equals")).click();

    // Verify the result = 5
    UiObject result = mDevice.findObject(By.res(CALC_PACKAGE, "result"));
    assertEquals("5", result.getText());
}

Run UI Automator Tests on a Device or Emulator


You can run UI Automator tests from Android Studio or from the command-line. Make sure to specify AndroidJUnitRunner as the default instrumentation runner in your project.
To run your UI Automator test, follow the steps for running instrumented tests described in Getting Started with Testing.