2017년 12월 23일 토요일

[STAN] Structure Analysis for Java > Tasks > Switching between Flat Packages and Package Trees

Switching between Flat Packages and Package Trees

자바의 패키지 컨셉은 클래스를 높은 레벨 구성단위로 그룹핑하는 기본적인 방법이다. 그러나 패키지 구조 또한 트리를 구성한다. 예를 들어, com.stan4j.db와 com.stan4j.ui 패키지는 com.stan4j의 sub-packages 이다. 이를 고려해서 STAN은 flat packages를 Flat Packages와 Package Tree 모드 사이를 토글하도록 해준다.
Java's package concept is the basic way for grouping classes into higher level units. However, the package structure also builds a tree. For example, the packages com.stan4j.db and com.stan4j.ui are sub-packages of package com.stan4j. To take this into account, STAN allows you to toggle between the flat packages Flat Packages and Package Tree modes.
  • Flat package 모드에서 STAN 완전히 super와 sub-package 관계를 무시한다. 모든 패키지들은 explorer tree 상에서 동일한 레벨로 보인다.
  • Package Tree 모드에서는 STAN은 super/sub-package 관계를 유지한다. Structure Explorer는 "normal" 패키지를 말단으로해서 전체 package 계층을 보여준다.
    • package tree는 package 뿐만 아니라 서브트리를 포함하는 package tree를 의미한다.
    • folder는 subtree 노드들만 포함하는 package tree를 의미한다.
    • leaf package는 말단 패키지를 의미한다.

Flat 대 Tree switch는 Structure Explorer에 여양을 주지는 않는다. 그 switch는 STAN이 전역적으로 다른 구조 모델을 사용하게 해준다. 그러므로, toggle은 또한 다른 뷰에서 무엇이 보이는지 무엇이 보이는지에 대해 암시를 갖는다. 예를들어
The Flat versus Tree switch does not only affect the Structure Explorer. Rather, it makes STAN use the different structure models globally. Therefore, the toggle also has implications on what is shown in other views. For example,
  • 패키지를 위한 Dependency Landscape는 다른 (flat) 패키지나 package trees 에 의존할 것이다.
  • Composition View는 오직 flat package 모드에서만 모든 package들을 포함하는 (flat) package dependency graph를 보여줄 것이다.
  • Couplings View는 오직 package tree 모드에서만 package tree node들을 사용할 것이다.
  • Query View와 Violations View는 flat package mode에서 package tree rows를 gray out 시킬 것이다.

[STAN] Structure Analysis for Java > Tasks > Using the Structure Explorer

Using the Structure Explorer

Structure Explorer는 STAN의 주요 선택 도구이다. Structure Explorer는 인공물 관리 계층을 보여둔다. 인공물을 선택하면 그것이 현재 노드가 된다: 모든 다른 뷰들은 해당 노드에 대응되는 컨텐츠를 보여줌으로써 이를 반영한다.
explorer의 root node는 전체 project/application에 대응된다. root의 컨텍스트 메뉴는 Properties 아이템을 포함하는데 이는 application properties 다이얼로그를 열어준다(단축키는 ALT+ENTER).
만약 분석이 클래스를 제외하면, 이들은 root와 분리되어 "Filtered Classes"로 라벨링되어 보여진다.

Refresh 컨텍스트 메뉴 아이템을 사용하여(또는 explorer가 활성화된 상태에서 F5를 누른다) 분석을 재실행한다. 이는  밑에 포함된 리소스들이 디스크 상에서 변경되었다면 유용하다(클래스 파일들과 JAR들)

인공물들은 인공물 자체나 그것의 자식들중 하나가 특정 측정 기준으로 등급이 지정되는 경우 에러 마크를 보여준다. 경고 표시는 인공물이나 그것의 자식을 위한 최악의 등급이 주황색 범위에 빠지게 됨을 가리킨다. 결과적으로 에러나 경고가 없는 인공물들은 측정 기준을 위반하지 않고 있다는 것을 의미한다.

Excluding/Including Artifacts from the Structure Explorer

패키지 트리의 경우 평범한 패키지들과 클래스들은 Exclude 아이템 항목을 포함하는 컨텍스트 메뉴를 포함한다. Explorer로부터 인공물을 제외하는 것은 자동적으로 적절한 exclusion 패턴을 추가하고 분석을 재가동함으로써 어플리케이션 특성들을 변경할 것이다.

Note
Application properties를 열고 Filter tab으로 가서 현재 활성화된 패턴 리스트들을 손수 관리할 수 있다. 필터 패턴에 대한 더 많은 정보는 Specifying Class Filter Patterns를 참고해라.

필터링된 클래스들은 grayed-out icons으로 분리된 root 밑에 보여진다. 명확하게 제외된 패키지 트리들, 평범한 패키지들 그리고 클래스들은 In-out 마커로 장식되고 컨텍스트 메뉴의 Include로 다시 포한될 수 있다(부모가 제외되었을 때는 제외된다)

Warning
필터 패턴들은 어플리케이션 전체 영역이다. Thus, using the Exclude menu item while in Show Libraries mode may cause artifacts from other libraries be excluded as well (e.g. when excluding a package that extends over several libraries.

Linking the Structure Explorer with Views

synched Link with View button은 explorer와 뷰를 연결하는데 사용된다. 이 모드에서,
The synced Link with View button is used to link the explorer to views. In this mode,
  • Composition View에서 artifact를 탐색하는 것은 그 artifact를 현재 노드로 만들 것이다.
  • Couplings View에서 artifact를 탐색하는 것은 그 artifact를 현재 노드로 만들 것이다.

Structure Treemap

Structure Explorer가 활성화되었을 때(포커스를 가지고있는) OverView는 트리맵으로써 현재 노드 현재 노드에 뿌리를 둔 서브트리를 형상화해준다. 트리맵 셀을 더블 클릭하면 explorer에서 대응되는 노드가 선택되고 그것이 새로운 현재 노드가 된다.

  • 트리맵 셀의 색깔은 artifact 타입에 대응된다.(예를 들어, 클래스는 녹색, 인터페이스는 분홍색이다)
  • 트리맵 셀의 사이즈는 artifact에 포함된 코드의 양을 가리킨다.

[STAN] Structure Analysis for Java > Tasks > Analyzing Code > Setting Analysis Preferences

Setting Analysis Preferences

Analysis Preference 페이지는 Level of Detail과 Exclusion Patterns을 위한 기본값을 명세하게 해준다.(Specifying Class Filter Patterns을 보아라).
새로운 analysis를 설정할 때, preferences를 초기값으로 취하게 된다.

[STAN] STAN - Structure Analysis for Java > Tasks > Analyzing Code > Specifying Class Filter Patterns

Specifying Class Filter Patterns

Class filters는 분석을 형성하는 라이브러리들로부터 클래스의 부분집합들을 조정게 해준다. 두가지 종류의 필터가 있다.
  • Inclusions 특정 클래스들이 분석되게 하는 규칙을 명세하는 Ant-style 패턴들의 부분집합으로 주어질 수 있다. 만약 inclusions이 정의되지 않았다면 기본 패턴이 적용된
  • Exclusions 틀정 클래스들이 무시되도록 하는 규칙을 명세하는 Ant-style 패턴의 집합이 주어질 것이다.
만약 클래스가 적어도 하나의 inclusion 패턴에 매칭되고 어떤 exclusion 패턴에 매칭되지 않는다면 클래스는 분석의 부분이 된다.
패턴은 패스 구분자로 점'.' 대신에 슬래시'/'를 사용한다. 다음의 와일드카드들이 사용가능하다:

  1. ? 는 하나의 캐릭터와 매칭된다(드물게 사용된다)
  2. * 는 '/'를 포함하지 않는 연속된 캐릭터와 매칭된다.
  3. ** 는 어떤 캐릭터들과도 매칭된다.
패턴을 쉽게 사용하기 위해서는 몇몇 standard pattern 타입들과 일치시키자:

  1. "/*"으로 끝나고 어떤 와일드카드를 포함하지 않는다면 하나의 패키지와 매칭된다. 예를 들어 com/foo/bar/* 는 com.foo.bar 패키지의 모든 클래스들과 매칭된다.
  2. "/**"으로 끝나고 어떤 와일드카드를 포함하지 않는다면 패키지 트리와 매칭된다. 예를 들어, com/foo/bar/**는 com.foo.bar 패키지 트리로부터의 모든 클래스들과 매칭된다.  com.foo.bar.baz 등등.
  3. 어떤 와일드 카드도 없는 패턴은 하나의 클래스와 매칭된다(inner class를 포함하여). 예를 들어 com/foo/bar/Baz는 com.foo.bar.Baz와 com.foo.bar.Baz$1과 같은 중첩 클래스와 매칭된다.
위의 표준 타입의 패턴들은 만약 Structure Explorer로부터 인공적으로 제외되지 않는다면 자동으로 분석에 적용된다. 우리는 complex patters으로서 표준 타입과 매칭되지 않는 패턴들을 참조한다. complex patterns의 예는 다음과 같다.
Patterns of the above standard types are automatically applied to the analysis if you exclude artifacts from the Structure Explorer. We refer to patterns that do not match the standard types as complex patterns. Examples for complex patterns are:
  • **/test/** 는 "test" 세그먼트를 포함하는 패키지명의 모든 클래스들과 매칭된다. 
  • **/Test* 는 "Test"로 시작하는 모든 package 내의  클래스들과 매칭된다.
새로운 분석을 생성할 때, 기본 inclusion 패턴은 라이브러리와 기본 exclusion 패턴으로부터 모든 클래스들과 매칭한다.(이 패턴은 inclusion pattern 리스트에는 표시되지 않는다)

2017년 12월 22일 금요일

[STAN] Structure Analysis for Java > Tasks > Analyzing Code > Selecting Libraries

Selecting Libraries


Library Page는 분석의 코드 베이스를 형성하는 라이브러리의 리스트를 유지한다.

  • JAR 파일들을 추가하기 위해 Add JARs... 버튼을 누른다.
  • 클래스 폴더를 추가하기 위해 Add Foldr... 버튼을 누른다.
Up과 Down버튼은 라이브러리 아이템들의 순서를 정렬하기 위해 사용할 수 잇다. Structure Explorer은 Show Libraries 모드에서 이 순서대로 라이브러리들을 보여준다. 선택된 아이템을 제거하려면 Remove button을 사용한다.

[STAN] Structure Analysis for Java > Tasks > Analyzing Code > Choosing the Level of Detail

Choosing the Level of Detail

Level of Detail은 분석의 상세 정도를 결정한다.
  • Class - 분석의 상세 레벨은 클래스 계층으로 제한될 것이다. 필드나 메소드의 어떤 정보도 수집되지 않는다. 이 모드는 매우 큰 코드 베이스이거나 클래스의 상세 정보에는 관심이 없는 경우 시간과 메모리 사용을 감소시키기 위해 사용된다.
  • Member - 분석은 전체 레벨의 상세정보에 대해 수행된다. 필드와 메소드 정보가 수집된다. 메소드 매트릭스와 멤버 의존성 그래픽을 사용할 수 잇다.
새로운 분석을 생성할 때 상세정보의 기본 레벨은 Analysis Preferences로부터 취하게 된다.

[STAN]Structure Analysis for Java > Tasks > Analyzing Code

Analyzing Code

STAN의 RCP(stand-alone) 변종은 이른바 Projects라 불리는 것을 사용한다. Project settings은 workspace에 저장되어 수정될 수 있고 언저든지 실행할 수 있다.

Running an Analysis

Projects View는 workspace에 있는 모든 STAN 프로젝트들을 보여준다. 더블 클릭을 통해 프로젝트를 실행시키거나 컨텍스트 메뉴의 Analyze를 선택해서 실행시킬 수 있다.

Creating a new project

새로운 프로젝트를 열기 위해 메뉴바의 File > New 를 선택하거나 ALT+SHIFT+N을 선택한다. 다른 방법 : CTRL+N을 누르고 리스트에서 위저드를 선택한다.
프로젝트의 이름을 입력하고 아래에 설명하는데로 프로젝트 설정으로 통과하기 위해 다음을 누룬다.

Configuring an Analysis

Project Configuration 다이얼로그는 다음의 페이지를 갖고 있다.
  1. General - 이 페이지에서 Level of Detail을 선택할 수 있다.
  2. Libraries - 이 페이지는 코드베이스를 형성하는 라이브러리의 리스트를 유지한다.
  3. Filter - 이 페이지는 당신의 Class Filter Patterns을 설정할 수 있게 해준다.
다 설정했으면, Finish를 누른다.
존재하는 프로젝트의 설정을 변경하기 위해서는 Projects View의 컨텍스트 메뉴에서 Properties를 선택한다. 현재 분석중인 프로젝트를 위해서는 Structure Explorer에 있는 root의 컨텍스트 메뉴로부터 선택하면 된다.

2017년 11월 24일 금요일

Chapter8. HTTP 서버



학습 목표


  1. HTTP 모듈을 이용해서 서버를 작성할 수 있습니다.
  2. HTTP 요청을 분석하는 코드를 작성할 수 있습니다.
  3. HTTP 응답 메시지를 작성하는 코드를 작성할 수 있습니다.
  4. 정적 파일 요청과 서비스 제공하는 서버를 작성할 수 있습니다.


1. HTTP 서버

◎ HTTP 서버 구동 코드
var http = require('http');
var server = http.createServer(function(req, res) {
    res.end('Hello World');
}).listen(3000);

2. HTTP 요청

◎ HTTP 요청
  • 클라이언트 요청 분석
  • request 이벤트 리스너의 파라미터
  • req : IncomingMessage
    var server = http.createServer(function(req, res) {} )
▷ 클라이언트 요청 분석
▷ IncomingMessage
  • message.url : 요청 url, 경로와 쿼리 문자열
  • message.method : 요청 메소드
  • message.headers : 요청 메소드의 헤더
  • message(streamable) : 요청 메시지 바디
▷ 요청 URL 경로와 쿼리 문자열 분석
▷ URL 분석하기 : URL 모듈
var url = require('url');
url.parse(req.url, true);

▷ 요청 메시지 헤더 분석
▷ 헤더 정보 : request.header
var headers = request.headers;
headers.host;
headers.content-type;
headers.user-agent;
. . .

▷ 요청 메시지 바디 분석
  • IncomingMessage의 data 이벤트 사용
  • POST 요청을 다루는 장에서 다룬다.
◎ HTTP 요청 출력
▷ 요청 내용 콘솔에 출력하기
var server = http.createServer(function(req, res) {
    console.log('HTTP Method : ' + req.method);
    console.log('HTTP URL : ' + req.url);
    console.log('== HEADERS ==');
    console.log(req.headers);

    res.end('Hello Node.js');
});

3. HTTP 응답

◎ 응답 메시지

  • 응답 메시지 : http.ServerResponse
  • 상태 코드와 상태 메시지
  • 응답 메시지 헤더
  • 응답 메시지 바디
▷ 메시지 상태
response.statusCode
response.statusMessage

▷ 메시지 헤더
response.writeHead(statusCode[, statusMessage][, headers])
response.removeHeader(name)
response.getHeader(name)
response.setHeader(name, value)

▷ 메시지 바디
response.end([data][, endcoding][, callback])
response.write(chunk[, encoding][, callback])

▷ HTTP 상태 코드와 메시지
// 200 OK
response.statusCode = 200;
response.statusMessage = 'OK';

// 404 Error
response.statusCode = 404;
response.statusMessage = 'Not found';

▷ 응답 메시지 헤더

▷ 응답 코드, 메시지 헤더 작성 함수 : writeHead
res.writeHead(200, {'Content-Length': body.length,
                          'Content-Type': 'text/plain'});

▷ 헤더 작성하기 : setHeader
res.setHeader("Content-Type", "text/html");
res.setHeader("Content-Length", body.length);

▷ 응답 메시지 바디
▷ 응답 메시지 종료
response.write(chunk[, endcoding][, callback])
response.end()

▷ 응답 메시지 작성 시 주의

▷ 응답 메시지 종료 - 클라이언트 대기 - timeout
http.createServer(function(req, res) {
    res.write('Hello Node.js');
    res.end(); // 응답 메시지를 완료시켜주어야 한다.
});

▷ 메시지 헤더는 메시지 바디 작성 전에
res.write('Hello World');
// 바디를 작성한 이후에 헤더를 작성하면 에러가 발생한다.
res.setHeader('Conent-Type', 'text/plain');

4. HTTP 서버 작성

◎ 정적 파일 요청
▷ 정적인 컨텐츠(이미지, html, 음악 등) 요청
  • 미리 작성된 파일로 응답
▷ 요청 예
  • http://myServer.com/image/cat.jpg
  • http://myservice.net/music/sing.mp3
▷ 요청 경로
  • req.url
▷ 요청 경로 분석
  • path 모듈
▷ 정적 파일 요청 : 응답
  • 정적 파일 찾기
  • 파일 로딩, 응답
▷ fs 모듈
  • fs.readFile(FILEPATH, CALLBACK);
▷ 응답 메시지에 파일 내용 쓰기
  • res.write();
  • res.end();
▷ 정적 파일 요청에 대한 응답
fs.access(path, fs.F_OK, function(exist) {
    fs.readFile(path, function(err, data) {
        res.end(data);
    });
});

◎ 정적 파일 요청
  • 정적 파일 요청에 대한 응답
  • 정적 파일 체크
  • 상태 코드
  • 컨텐츠 타입
▷ 파일이 없으면?

▷ 404 에러 처리 코드
fs.access(path, fs.R_OK, function(err) {
    // 접근 불가능 시 404 에러
    if( err ) {
        res.statusCode = 404;
        res.end('Not Found');
        return;
    }
}

▷ 정적 파일 응답
fs.access(path, fs.F_OK, function(err) {
    if( err ) {
        res.statusCode = 404;
        res.end('Not found');
        return;
    }
    fs.readFile(path, function(err, data) {
        res.statusCode = 200;
        res.setHeader('Content-type', 'image/jpg');    // 이미지라면
        res.end(data);
    });
});

▷ 스트림 파이프
  • 입력 스트림 : fs.createReadStream()
  • 출력 스트림 : res
    fs.createReadStream(path).pipe(res);
▷ 파미콘 : 웹 사이트의 아이콘 이미지
▷ 파비콘 요청
  • GET /favicon.ico
▷ 파비콘 응답 코드
if( request.url == '/favicon.ico' ) {
    // 파비콘 처리
    return;
}

▷ 컨텐츠 타입
var server = http.createServer(function(req, res) {
    if(req.url == '/favicon.ico') {}
    else if(req.url == '/image.png' ) {
        res.writeHeader(200, {'Content-Type':'image/png'});
        fs.read...
    }
    else if(req.url == '/music.mp3') {
        res.writeHeader(200, {'Content-Type':'audio/mp3'});
        fs.createReadStrea...
    }
    else if(req.url == '/movie.mp4') {
        res.writeHead(200, {'Content-Type':'video/mp4'});
        fs.createReadStream...
    }
});

◎ 정적 파일 서비스
▷ 요청 URL의 경로를 실제 파일 경로 매핑
  • myServier.com/resource/image.png -> ./resources/image.png
  • myServier.com/resource/audio.mp3 -> ./resources/audio.mp3
▷ 요청 url에서 경로 생성
URL로부터 파일의 패스 정보를 가져와서 바로 파일에 대한 경로를 만들어 줄 수 있다.
파일을 찾고 파일에 대한 읽기위한 경로를 만드는 과정이 단순해짐.

var pathUtil = require('path');
var path= __dirname + pathUtil.sep + 'resources' + req.url;

◎ 웹 페이지 서비스
▷ 요청
  • 인덱스 요청
  • 파비콘 요청
  • JS와 css요청
  • 이미지 요청 : <img>
▷ 서비스 용 html
<html>
<link rel="stylesheet" type="text/css" href='public/style.css'>
<body>
    <h1>Hello Kitty</h1>
    <img src='image/cat.jpg'>
</body>
</html> 

html요청
css 요청
image 요청
파비콘 요청 에 대한 응답 처리를 모두 해주저야 한다.

▷ 서버 코드
var server = http.createServer(function(req, res) {
    if(req.url == '/favicon.ico') {
        // 파비콘 응답
    }
    else if(req.url == '/') {
        // 기본 페이지 : index.html
        fs.createReadStream('./public/index.html').pipe(res);
    }
    else {
        var path = __dirname + req.url;
    }
})

◎ 서비스
▷ 서비스
▷ URL 쿼리 문자열 : 합계 구하기
http://127.0.0.1:3000/count?start=1&end=10

▷ 서버 코드
// URL 분석 : 쿼리 문자열
var parsed = url.parse(request.url, true);
var query = parsed.query;

// start와 end
var start = parseInt(query.start);
var end = parseInt(query.end);

if(입력값 오류) {
    response.statusCode = 404;
    response.end('Wrong Parameter');
}
else {
    // 합계 구하기
    response.statusCode = 200;
    response.end('Result : ' + result);
}

학습 정리

◎ 지금까지 'HTTP 서버'에 대해 살펴보았습니다.
  • HTTP 요청
    클라이언트의 HTTP 요청을 분석했습니다.
  • HTTP 응답
    클라이언트에게 HTTP 응답 메시지를 작성하고 전송하는 방법을 알아봤습니다.
  • HTTP 서버
    다양한 형태의 HTTP 서버 작성 방법을 알아봤습니다.
   



2017년 11월 13일 월요일

Chapter7. HTTP 통신


학습 목표


  1. HTTP 통신의 특징을 이해할 수 있습니다.
  2. HTTP 통신을 다루는 HTTP 모듈 사용 방법을 알 수 있습니다.
  3. HTTP 서버와 클라이언트 클래스와 주요 메소드를 알 수 있습니다.

1. HTTP 통신

◎ HTTP
  • 웹(WWW)의 주역!
  • HTTP : Hyper Text Transfer Protocol
  • 하이퍼 텍스트 - 하이퍼 링크로 논리적으로 연결된 문서
  • ex) HTML
◎ HTTP 통신의 특징
▷ HTTP 통신
  • 요청(Request)
  • 응답(Response)
▷ 요청과 응답 과정
  • 웹 브라우저 → 주소 입력 → 요청 → 웹 서버 → 응답 → 웹 브라우저
◎ HTTP 통신

◎ URL
▷ 리소스의 위치 : URL(Uniform Resource Locator)
  • http://username:password@host:8080/directory/file?query#ref:
◎ URL 인코딩
▷ URL에 허용되는 문자
  • 알파벳, 숫자, 하이픈, 언더스코어, 점 틸드
▷ URL 인코딩하기
  • https://www.google.com/search?q=아이폰
  • https://www.google.com/search?q=%EC%95%84%EC%9D%B4%ED%8F%B0
◎ 웹 브라우저에서 요청/응답 확인해보기
▷ 웹브라우저
  • 개발자 도구


2. HTTP 요청

◎ HTTP 메시지 구조
▷ 요청 메시지
  • 요청 라인
  • 요청 헤더
  • 요청 바디(엔티티)
http body를 사용하지 않는 요청 메시지

◎ 요청 라인
  • 요청 메소드
  • 요청 URL
  • HTTP 버전
    GET http://en.wikipedia.org/wiki/The_Scream HTTP/1.1
◎ HTTP 메소드
  • HTTP 메소드 : 리소스를 다루는 행위
    GET : 리소스를 얻어오는 요청
    POST : 리소스 전송 요청
    PUT : 저장 요청(수정)
    DELETE : 삭제
    . . . 
◎ 요청 헤더

  • 헤더는 키 : 값 방식으로 작성
▷ 주요 요청 헤더
Accept : 클라이언트가 받을 수 있는 컨텐츠
Cookie : 쿠키
Content-Type : 메시지 바디(엔티티)의 종류
Content-Length : 메시지 바디의 길이
if-Modified-Since : 특정 날짜 이후에 변경됐을 때만

◎ 요청 정보 전달
▷ URL을 이용해서 요청 정보 전달
  • GET 메소드, TRACE 메소드 : 메시지 바디를 사용하지 않으므로 URL만으로 클라이언트가 원하는 정보를 전달
▷ 경로와 쿼리 스트링 사용
  • http://idols.com/snsd/taeyon.png
  • http://idols.com/q?group=snsd&name=jessica
▷ 메시지 바디를 사용하지 않는다.

◎ 요청 정보 전달
▷ URLEncoded 방식
▷ 메시지 헤더(컨텐츠 타입)
  • application/x-www-form-urlencoded
▷ 메시지 바디 : 쿼리 문자열
▷ 메시지 예
Content-Type : application/x-www-form-urlencoded
title=Madmax&Director=George+Miller

◎ 요청 정보 전달
▷ JSON/XML
▷ 메시지 헤더(컨텐츠 타입)
  • application/json
▷ 메시지 예
Content-Type:application/json
{
    "name":"iu", "best":"좋은 날"
}

◎ 요청 정보 전달
▷ 멀티 파트를 이용한 요청 정보 전달
  • 바이너리 파일을 올리는 경우에 주로 사용
  • 하나의 메시지 바디에 파트를 나눠서 작성
▷ 메시지 헤더

  • 컨텐츠 타입 ; 파트 구분자(boundary)
  • multipart/form-data; boundary=frontier
▷ 각 파트별 컨텐츠 타입
▷ 파트로 구성된 바디

◎ 요청 정보 전달
▷ 요청 정보 전달
  • 메시지 바디를 사용 여부
  • 바디의 인코딩 방식
▷ URL로 요청 정보 전달 : 바디 분석 불필요

3. HTTP 응답

◎ HTTP 메시지 구조
▷ 응답 메시지
  • 응답 라인
  • 응답 헤더
  • 응답 바디
◎ HTTP 메시지
◎ 응답 메시지
▷ 응답 라인
  • 버전
  • 상태 코드
  • 상태 메시지 → HTTP/1.1 200 OK
◎ 상태 코드
▷ 상태 코드 : 서버의 응답 코드
▷ 대 분류
  • 1xx : 정보
  • 2xx : 성공
  • 3xx : 리다이렉션
  • 4xx, 5xx : 오류
▷ 상태 코드 : 2xx
  • 200 : OK. 요청 성공
  • 201 : Created. 생성 요청 성공
  • 202 : Accepted : 요청 수락. 요청 처리는 보장 안됨
  • 203 : Non-authoritavive Information
  • 204 : Non Content...
▷ 상태 코드 : 3xx
  • 300 : Multiple choices. 여러 리소스에 대한 요청 결과 목록
  • 301, 302, 303 : Redirect. 리소스 위치가 변경된 상태
  • 304 : Not Modified. 리소스가 수정되지 않았음...
▷ 상태 코드 : 4xx
  • 400 : Bad Request. 요청 오류, 요청을 처리하는 필수 데이터가 없는 경우
  • 401 : Unauthorized. 권한 없는 상태
  • 403 : Forbidden. 요청 거부 상태
  • 404 : Not Found. 리소스가 없는 상태...
▷ 상태 코드 : 5xx
  • 500 : Internal Server Error. 서버가 요청 처리를 못하는 상태
  • 501 : Not Implemented. 서버가 지원하지 않는 요청
  • 503 : Service Unavailable. 과부하 등으로 당장 서비스가 불가능한 상태 ...
◎ 응답 메시지 헤더
▷ 응답 메시지 헤더
▷ 주요 헤더 필드
  • Content-Type : 바디 데이터의 타입
  • Content-Length : 바디 데이터 크기
  • Set-Cookie : 쿠키 설정
  • ETag : 엔티티 태그
◎ 응답 메시지 바디
▷ 바디 데이터
  • HTML
  • XML/JSON
  • Octet Stream 등
▷ 바디 기록 방식 : Content-Type 헤더 필드

▷ 컨텐츠 타입
  • 메시지 헤더에 기록
  • 필드 이름 content-type
  • 대분류/소분류
▷ 주요 컨텐츠 타입
  • text/plain, text/html
  • application/xml, application/json
  • image/png, image/jpg
  • audio/mp3, video/mp4
◎ 응답 메시지
▷ HTML 응답 메시지 헤더
  • content-type:text/html
▷ HTML 응답 메시지 바디
<html>
<body>
<h1>Hello Node.js</h1>
</body>
</html>

▷ HTML 응답 메시지 헤더
  • content-length: 73228
  • content-type: image/jpeg
▷ HTML 응답 메시지 바디 - 이미지를 헥사타입으로 출력

◎ 응답 메시지
  • 컨텐츠 타입이 안 맞으면?
  • 컨텐츠 타입 : application/json
  • 실제 바디 데이터 : 이미지
  • 제대로 표현하지 못한다.

4. HTTP 모듈

◎ HTTP 모듈
▷ HTTP 모듈, 기본 모듈
  • var http = require('http');
▷ HTTP 서버
  • 클라이언트의 요청 메시지 수신
  • 클라이언트에게 응답 메시지 전송
▷ HTTP 클라이언트
  • 서버로 요청 메시지 전송
  • 서버의 응답 메시지 수신
◎ HTTP 모듈 클래스
▷ 서버용 클래스
  • http.Server : HTTP 서버
  • http.IncomingMessage : HTTP 서버의 요청 메시지, Readable Stream
  • http.ServerResponse : HTTP 서버의 응답 클래스
▷ HTTP 클라이언트
  • http.Client : HTTP 클라이언트
  • http.ClientRequest : HTTP 클라이언트 요청 메시지
  • http.IncomingMessage : HTTP 서버의 응답 메시지, Readable Stream
◎ HTTP 모듈

5. HTTP 서버

◎ HTTP 서버
▷ HTTP 서버
  • http.Server
▷ HTTP 서버 생성
  • var server = http.createServer([requestListener])
▷ HTTP 서버 주요 이벤트
  • request : 클라이언트의 요청 도착
  • connection : 소켓 연결
  • close : 서버 종료
▷ HTTP 서버 메소드
  • server.listen()
  • server.close()
  • server.setTimeout()
◎ HTTP 서버
▷ HTTP 서버 동작시키기

  • 서버 객체 생성
  • 클라이언트 접속 대기(listening)
    var server = http.createServer();
    server.listen(PORT);
▷ 포트
  • 0~1023 : well-known port, 미리 정의된 포트, 관리자 권한 필요
  • 1024 ~ 49151 : registered port
  • 49151 ~ 65535 : dynamic port
▷ 포트 바인딩 실패
  • 이미 사용 중
  • 권한 없음
▷ 서버와 이벤트 리스너
var http = require('http');
var server = http.createServer();
server.on('request', function(request, response) {
});
server.on('connection', function(socket) {
    console.log('connection event');
});
server.on('close', function() {
    console.log('close');
});
server.listen(3000);

▷ HTTP 서버 request 이벤트 리스너
var http = require('http');
var server = http.createServer(function(req, res) {
    res.end('Hello World');
}).listen(3000);

웹 브라우저 : http://127.0.0.1:3000

6. HTTP 클라이언트

◎ HTTP 클라이언트
▷ 클라이언트 객체
  • http.create([port][, host]) // deprecated
▷ 클라이언트 요청
  • http.request(options[, callback])
▷ 바디 없이 요청 보내기(GET)
  • http.get(options[, callback])

학습정리

◎ 지금가지 'HTTP 통신'에 대해 살펴보았습니다.
  • HTTP 통신
    요청과 응답 메시지를 사용하는 HTTP 통신을 알아봤고, 메시지 구조를 알아봤습니다.
  • HTTP 모듈
    HTTP 통신 기능을 제공하는 HTTP 모듈을 알아봤습니다.
  • HTTP 서버
    HTTP 모듈 중 서버를 작성하는 클래스와 이벤트를 알아봤습니다.
  • HTTP 클라이언트
    HTTP 모듈 중 클라이언트를 작성하는 데 필요한 기능을 간단히 알아봤습니다.





2017년 10월 29일 일요일

Chapter6. 흐름 제어



학습 목표

1. 콜백과 콜백으로 발생하는 복잡한 상황을 확인할 수 있습니다.
2. Async 모듈과 Promise를 이용해서 복잡도를 낮추면서 작업 흐름 제어를 할 수 있습니다.

1. 콜백과 콜백 헬

◎ 콜백 함수 보기
▷ 비동기 함수 - 콜백 함수 사용
function asyncTask(path, function(result) {
    // 결과 처리
});

◎ 비동기 동작과 콜백
▷ 비동기 동작의 연속

  • task1 실행 이수에 task2 실행
  • task1 실행 결과를 이용해서 task2 실행
▷ 콜백의 연속된 호출

◎ 연속된 비동기 동작의 예
  • 이미지 업로드 후 데이터 베이스에 저장
  • 다수의 이미지에서 썸네일 생성 후 업로드
◎ 연속된 비동기 동작 코드
▷ 비동기 동작 연속 실행
function task1(args, function(result) {
    // 결과 처리
});
function task2(args, function(result) {
    // 결과 처리
});

▷ 실행
task1()
task2()

▷ task1 실행 이후에 task2 실행
task1(arg1, function(result) {
    task2(arg2, function(result) {
    });
};

▷ task1 실행 결과를 task2 에서 사용
task1(arg1, function(result) {
    var arg2 = result.value;
    task2(arg2, function(result) {
    });
};

◎ 콜백의 연속으로 발생한 현상
▷ 그러다 콜백 지옥에 도착
task1(a, b, function(err, result1) {
    task2(c, function(err, result2) {
        task3(d, e, f, function(result3) {
            task4(h, i, function(result4) {
                // 비동기 동작
            }); // task4
        }); // task3
    }); // task2
}); // task1

◎ 콜백 지옥 탈출 시도
▷ 콜백 지옥 탈출하기
- function을 인라인방식으로 작성하지 않고 분리해서 작성하기
- 중간에 비동기 동작을 부르는 순서가 바뀌거나 로직이 바뀌는 경우
이 코드를 유지하기 어려워짐. -> 근본적인 해결책이 되지는 못함. => Async, promise 도입
task1('a', 'b', task1Callback);

function task1Callback(result) {
    task2('c', task2Callback);
}

function task2Callback(result) {
    // task3 호출
}
// callbackhell.js
function task1(callback) {
    console.log('Task1 시작');
    setTimeout(function() {
        console.log('Task1 끝');
        //callback();
    }, 300);
}

function task2(callback) {
    console.log('Task2 시작');
    setTimeout(function() {
        console.log('Task2 끝');
        //callback();
    }, 200);
}

task1();
task2();

PS C:\project\nodetest> cd chapter6
PS C:\project\nodetest\chapter6> node callbackhell.js
Task1 시작
Task2 시작
Task2 끝
Task1 끝

// callbackhell2.js
function task1(callback) {
    console.log('Task1 시작');
    setTimeout(function() {
        console.log('Task1 끝');
        callback();
    }, 300);
}

function task2(callback) {
    console.log('Task2 시작');
    setTimeout(function() {
        console.log('Task2 끝');
        callback();
    }, 200);
}

task1(function() {
    task2(function() {
        
    });
});


PS C:\project\nodetest\chapter6> node callbackhell2.js
Task1 시작
Task1 끝
Task2 시작
Task2 끝

2. Async

◎ 비동기 동작의 흐름 제어
▷ Async 모듈

  • https://github.com/caolan/async
    모듈이름 js (async js) 라고 검색하면 자바스크립트용 모듈(라이브러리)을 쉽게 찾을 수 있다.
▷ 모듈 설치
  • npm install async
◎ Async의 대표적인 기능
▷ 행위 순서 제어
  • series seriesEach
  • paralles
  • waterfall
▷ 콜렉션(배열, 객체)
  • eachcallback
  • forEachOf
  • map, filter
◎ Async의 순차 실행
▷ series(tasks, [callback])
비동기 태스크들을 배열 형태로 넘김
async.series(
    [
        태스크1,
        태스크2,
        태스크3
    ],
    function(err, results) {
        완료 콜백
    }
);

◎ 순차 실행
▷ callback 호출 : 다음 태스크로 진행
▷ 태스크 완료 : 다음 태스크 실행
function(callback) {
    // 태스크 성공
    callback(null, result);
}

▷ 완료 콜백으로 동작 결과 전달
▷ 태스크 에러 발생 : 에러 전달
function(callback) {
    // 에러 발생
    callback(err, null);
}
▷ 다음 태스크 실행 안함
▷ 마무리 콜백으로 에러 전달

◎ 연속 동작 마무리
▷ serial 마무리 콜백
async.series([태스크1, 태스크2, 태스크3],
    function(err, results) {
        if(err) {
            // 태스크 진행 중 에러 : callback(err, null)
            return;
        }
        // 마무리 동작
    }
);

results에는 Async가 제공해주는 함수마다 각각 다른 형태을 제공해주는데
API를 참조해야 한다. Async.series는 각각 태스크에서 전달한 result 데이터가 배열
형태로 넘어옴.

◎ async.series 예제코드
▷ async.series
async.series([
    function task1(callback) {
        callback(null, 'result1');
    ),
    function task2(callback) {
        callback(null, 'result2');
    },
    function task3(callback) {
        callback(null, 'result3');
    }
    ],
    function (err, results) {
        // results : ['result1', 'result2', 'result3']
    }
);

◎ 순차 실행
▷ 태스크로 정보를 전달하려고 할 때 : async.waterfall
  • 다음 태스크로 전달할 값을 콜백의 파라미터로
  • 태스크 함수의 파라미터로 전달 이전 태스크의 값 전달
  • series는 최종 마무리에 있는 콜백으로 데이터를 바로 전달하게 되는데
    waterfall은 그 다음에 있는 task로 값을 전달한다.
function task1(callback) {
    callback(null, 'value');
},
function task2(arg, callback) {
    callback(null, 'hello', 'world');
}

어떤 비동기 동작이 필요하냐에 따라 series와 waterfall을 선택적으로 사용

◎ async.waterfall 예제 코드
▷ async.waterfall 샘플 코드
async.waterfall([
    function task1(callback) {
        callback(null, 'value');
    },
    function task2(arg, callback) {
        callback(null, 'value1', 'value2');
    },
    function task3(arg1, arg2, callback) {
        callback(null, 'result');
    }
    ],
    function(err, results) {
    }
);

◎ 동시 실행
▷ 여러 태스크를 동시에 실행
▷ 모든 태스크를 마치면 완료 콜백

  • parallel(tasks, [callback])
▷ 사용 방식
async.parallel([ task1, task3, task3 ],
    function(err, results) {
        // ['태스크 1 결과', '태스크2 결과', '태스크3 결과']
    }
);

▷ async.parallel 예제 코드
async.parallel(
    [
        function(callback) {
            callback(null, '태스크1 결과');
        },
        function(callback) {
            callback(null, '태스크2 결과');
        },
        function(callback) {
            callback(null, '태스크3 결과');
        }
    ],
    function(err, results) {
        console.log('모든 태스크 종료, 결과 : ', results); // ['태스크1 결과', '태스크2 결과', '태스크3 결과']
    ]
);

◎ 콜렉션과 비동기 동작
▷ 콜렉션 내 각 항목을 사용하는 비동기 동작
  • 다수의 파일(배열)을 비동기 API로 읽기
  • 다수의 파일을 비동기 API로 존재하는지 확인하기
ex) 파일 다섯개를 비동기 API와 같이 사용해야 할 때 각각 동작시키는 것은 가능하나 
비동기 동작이 모두 끝났을 때의 처리를 해줄 수가 없다.
ex) 여러개의 파일이 있는지 없는지 비동기 API를 통해 확인하고 싶다. fs모듈의 access(), stat() 같은 비동기 API를 이용해 파일 10개가 있는지 없는지 체크하고 난 이후에 뭔가 하고 싶을 때
ex) 콜렉션의 개수 만큼 비동기 동작을 수행

◎ 콜렉션과 비동기 동작
▷ 비동기 순회 동작
  • each, eachSeries, eachLimit
  • map, filter
  • reject, reduct
  • . . .
▷ 콜렉션과 사용하기 : each
  • each(arr, iterator, [callback])
▷ 예제 코드
async.each(array, function (item, callback) {
    // 베열 내 항목 item을 사용하는 비동기 동작
    callback(null);
}, function(err) {
    // async.each 완료
});

// async.js
function task1(callback) {
    console.log('Task1 시작');
    setTimeout(function() {
        console.log('Task1 끝');
        callback(null, 'Task1 결과');
    }, 300);
}

function task2(callback) {
    console.log('Task2 시작');
    setTimeout(function() {
        console.log('Task2 끝');
        callback('null', 'Task2 결과');
    }, 200);
}

var async = require('async');
async.series([task1, task2], function(err, results) {
    console.log('비동기 동작 모두 종료', results);
});

PS C:\project\nodetest\chapter6> node async.js
Task1 시작
Task1 끝
Task2 시작
Task2 끝
비동기 동작 모두 종료 [ 'Task1 결과', 'Task2 결과' ]

// async_error.js
function task1(callback) {
    console.log('Task1 시작');
    setTimeout(function() {
        console.log('Task1 끝');
        callback(Error, null);
    }, 300);
}

function task2(callback) {
    console.log('Task2 시작');
    setTimeout(function() {
        console.log('Task2 끝');
        callback('null', 'Task2 결과');
    }, 200);
}

var async = require('async');
async.series([task1, task2], function(err, results) {
    if(err) {
        console.error('Error : ', err);
        return;
    }
    console.log('비동기 동작 모두 종료', results);
});

PS C:\project\nodetest\chapter6> node async_error.js
Task1 시작
Task1 끝
Error :  function Error() { [native code] }

3. Promise

◎ Promise

  • 비동기 동작의 흐름 제어
  • 사이트 : www.promisejs.org
  • JavaScript ES6에 추가
    → Node.js4.x 이후 모듈 설치 필요 없음
◎ Promise 생성
▷ Promise 객체 생성
new Promise(function() {
    // 비동기 동작
});

◎ Promise 상태
▷ Promise의 상태
  • pending : 동작 완료 전
  • fulfilled : 비동기 동작 성공
  • rejected : 동작 실패
◎ Promise 상태 반영
▷ Promise 생성, 상태 반영
  • 성공적으로 완료 : fullfill 호출
  • 에러 상황 : reject 호출
▷ Promise 생성, 상태 반영
new Promise( function(fullfill, reject) {
    // 비동기 동작
    if( err )
        reject(err);
    else
        fullfill(result);
});

◎ Promise 이후 동작
▷ Promise 이후의 동작 : then
  • fullfilled 상태일 때의 콜백
  • rejected 상태일 때의 콜백
▷ 코드 형태
new Promise(task).then(fullfilled, rejected);
function fullfilled(result) {
    // fullfilled 상태일 때의 동작
}
function rejected(err) {
    // rejected 상태일 때의 동작
}


◎ Promise를 사용하는 태스크
▷ Promise를 사용하는 태스크
function task() {
    return new Promise(function(fullfill, reject) {
        if( success )
            fullfill('Success');
        else
            reject('Error');
    });
}
비동기 태스크 객체 자체가 Promise를 리턴하는 방식으로 사용
Promise를 세련되게 사용하는 방법임.

▷ 태스크 사용 코드
task(arg).then(fullfilled, rejected);

◎ 흐름 제어
▷ Async, Promise

  • Async : Flow Control
  • Promise : Chain

// promise.js
function task1(fullfill, reject) {
    console.log('Task1 시작');
    setTimeout(function() {
        console.log('Task1 끝');
        fullfill('Task1 결과');
    }, 300);
}

function fullfilled(result) {
    console.log('fullfilled : ', result);
}

function rejected(err) {
    console.log('rejected : ', err);
}

new Promise(task1).then(fullfilled, rejected);

PS C:\project\nodetest\chapter6> node promise.js
Task1 시작
Task1 끝
fullfilled :  Task1 결과

// promise_error.js
function task1(fullfill, reject) {
    console.log('Task1 시작');
    setTimeout(function() {
        console.log('Task1 끝');
        //fullfill('Task1 결과');
        reject('Error msg');
    }, 300);
}

function fullfilled(result) {
    console.log('fullfilled : ', result);
}

function rejected(err) {
    console.log('rejected : ', err);
}

new Promise(task1).then(fullfilled, rejected);

PS C:\project\nodetest\chapter6> node promise_error.js
Task1 시작
Task1 끝
rejected :  Error msg

학습정리

◎ 지금까지 '흐름 제어' 에 대해 살펴보았습니다.

  • 콜백과 콜백 지옥
    비동기 동작을 연속적으로 수행하는 경우 콜백의 중첩으로 복잡도가 증가하는 현상을 살펴보았습니다.
  • Async
    Async 모듈을 이용해서 비동기 동작의 흐름을 제어했습니다.
  • Promise
    자바 스크립트의 표준에 추가된 Promise를 살펴봤습니다. 체인 방식으로 비동기 동작을 제어합니다.


2017년 10월 25일 수요일

Chapter5. npm을 이용한 모듈 관리



학습목표


  1. npm을 이용한 모듈 관리 방법을 이해할 수 있습니다.
  2. 패키지 정보에 대해 알 수 있습니다.
  3. 확장 모듈 설치에 대해 이해할 수 있습니다.
  4. 커스텀 모듈을 만들 수 있습니다.

1. npm


◎ npm

  • 홈페이지 : https://www.npmjs.org/
◎ js 패키지 포털
  • 확장 모듈(패키지) 검색
◎ 패키지 매니저

◎ npm 사이트
  • 모듈 검색
  • 모듈 상세 정보
  • 모듈 설치 방법
  • API 설명, 예제
◎ 모듈
▷ 패키지 매니저 : npm
  • 모듈 설치, 삭제(확장 모듈)
  • 모듈 검색
  • 모듈 정보
  • 패키지 정보 작성
◎ 콘솔 명령 : npm
◎ npm 주요 옵션
  • init : 패키지 준비, package.json 생성
  • install : 패키지에 필요한 모듈 설치
  • install [PACKAGE] : 개별 패키지 설치
  • list : 설치된 모듈 목록 보기
  • info : 모듈 정보
  • search : 모듈 검색
  • update : 모듈 업데이트
  • uninstall : 모듈 삭제
◎ 모듈 설치
▷ 전역 설치
  • 한번 설치로 모든 프로젝트에서 사용
  • 라이브러리 폴더(lib/node_modules)
  • 관리자 권한 필요, -g 옵션
▷ 지역 설치
  • 프로젝트마다 설치
  • 현재 폴더 내 node_modules 폴더
▷ 모듈 전역 설치

  • npm install [Module][@Version]
▷ 모듈 지역 설치 예
$npm install async
$npm install jade@1.0.0
  • node_modules 폴더 내 모듈 설치
▷ 모듈 목록 보기
  • npm list
  • npm list async
◎ 모듈 검색
▷  모듈 검색 : npm search
▷  정보 보기 : npm info

◎ 모듈 업데이트
▷ npm update [모듈명]

◎ 모듈 삭제
▷ npm uninstall [모듈 이름]

◎ 전역 모듈
▷ 전역 모듈
  • Node.js의 중앙 라이브러리에 설치 : lib/modeules
  • 위치는 시스템마다 달라짐
▷ 전역 모듈 다루기
  • npm 명령에 -g 옵션만 추가
  • 관리자 권한
◎ 전역 모듈 - 모듈, 전역 설치와 삭제
▷ 설치

  • [sudo] npm install -g nodemon
▷ 삭제
  • [sudo] npm uninstall -g nodemon
◎ 전역 모듈과 지역 모듈 중
▷ 지역 모듈 권장
  • 개발에 필수 라이브러리
  • 특정 버전 모듈에 의존적인 상황
▷ 유틸성(mocha, nodemon)은 전역 설치 권장

2. 패키지 정보

◎ 패키지 설정 파일
  • package.json
  • npm init 으로 생성
  • 패키지에 대한 정보 입력
◎ 패키지 정보 파일 : package.json
{
    "name": "package_sample",
    "version": "1.0.0",
    "description": "패키지 설정",
    "main": "main.js",
    "scripts": {
        "test": "echo \"Enter: no test specified\" && exit 1"
    }
    "author": "",
    "license": "ISC"
}

◎ 패키지 정보 중 의존성 정보
"dependencies": {
    "async":"^1.5.0",
    "jade": "^1.0,0"
},
"devDependencies": {
    "mocha": "^2.3.4"
},

◎ 패키지 정보 기록하기

  • 의존성 정보 기록 : npm install async --save
◎ 패키지 의존성 정보 사용하기
  • 패키지 정보 파일에 기록된 모듈 설치/업데이트
    → npm install
    → npm update

3. 확장 모듈 : nodemon

◎ nodemon
▷ 소스 코드 수정 후 재시작

  • 동작 멈추기, 다시 시작
▷ 수정 후 자동 재시작
  • nodemon source.js
◎ 모듈 nodemon
▷ 설치
  • 글로벌로 설치
  • 관리자 권한 필요
    [sudo] npm install -g nodemon
▷ 실행
  • nodemon app.js

4. 모듈 만들기

◎ 모듈 만들기
▷ 소스 코드 분리
  • 모듈
▷ 모듈 작성 방법
  • module.exports
▷ 모듈 사용하기
  • 모듈 로딩 : require
  • require('./greeting.js'); // 경로에 대한 명시가 필요, 경로 없으면 node_module에서 찾음
▷ 모듈 로딩 에러
  • require('greeting.js');
  • Error: Cannot find module 'greeting.js'
◎ 함수 exports
▷ 모듈 greeting.js
module.exports.goodMorning = function() {
    // 모듈 함수 기능 작성
}

exports.goodNight = function(arg, callback) {
    // module 생략 가능
}

▷ 사용하기
var greeting = require('./greeting.js');
greeting.goodMorning();

◎ 모듈 만들기
▷ exports 하지 않은 함수는 사용 불가
function goodAfternoon() {
    console.log('goodAfternoon');
}
▷ 사용하기
var greeting = require('./greeting.js');
greeting.goodAfternoon();
▷ 에러

  • TypeError: greeting.goodAfternoon is not a function
▷ 클래스 정의 exports
function BusDef() {
    this.take = function() {}
}

function MetroDef() {
    this.ride = function() {}
}
module.exports.Bus = BusDef;
exports.Metro = MetroDef; // module 생략 가능
▷ 사용하기
var Bus = require('./transport').Bus; // 클래스
var bus = new Bus(); // 객체생성
bus.take();

▷ 클래스 exports
function Exercise() {
    this.pushup = function();
}
Exercise.prototype.run = function() {
}
module.exports = Exercise; // module 생략 불가
// 하나의 모듈 내에서 단 하나의 class만 export할 때 사용

▷ 사용하기
var Exercise = require('./exercise');
var exercise = new Exercise();
exercise.pushup();

▷ 객체 exports - 객체 단위로
var student = {
    hour : 0,
    study : function() {
        this.hour++;
        console.log(this.hour + '시간째 공부 중');
    }
}
module.exports = student;

▷ 사용하기
var you = require('./student.js');
you.study();

▷ 모듈은 캐쉬됨
var you = require('./student.js');
you.study(); // 1
you.study(); // 2

// 모듈 로딩
var him = require('./student.js');
him.study(); // 3시간째 공부 중

학습정리

◎ 지금까지 'npm을 이용한 모듈 관리'에 대해 살펴보았습니다.

▷ npm
npm을 이용해서 모듈을 찾고 설치할 수 있습니다.

▷ 패키지 정보
패키지 정보 파일 사용 방법을 알아봤습니다.

▷ 확장 모듈, nodemon
확장 모듈 nodemon을 설치해보고 사용해봤습니다.

▷ 모듈 만들기
커스텀으로 모듈을 만들어봤습니다.


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