* 특징
- 싱글 쓰레드
(사용자 코드 단에서 싱글 쓰레드로 작성 하지만, io작업은 내부적으로 멀티 쓰레드로 동작 한다.)
- 비동기 I/O
- 이벤트 기반(event driven)
* 아키텍처
- 상위레벨 : JS
- 하위레벨 : C
바인딩
v8 엔진
libev : Event
libeio : I/O
* 도큐먼트
- 모듈
Node.js 간단한 구조
필요한 모듈을 로딩
모듈 : 다른 언어에서 라이브러리에 해당
기본 모듈 / 확장 모듈
메소드 -> 모듈 로딩 , 객체 생성 후 사용
모듈 함수 -> 객체 생성 없이 사용
한번 로드를 하면 캐싱을 하기 때문에 두 번 로드를 한다고 해서 서로 다른 로드를 한게 아님
require('모듈 이름')
- 이벤트
객체.on([이벤트 이름], [리스너 함수])
* 기본 모듈
- 주요 기본 모듈
프로세스 환경 : os, process, cluster
파일과 경로, URL : fs, path, URL, querystring, stream
네트워크 모듈 : http, https, net, dgram, dns
- 전역 객체 (global)
별도의 모듈 로딩 없이 사용
global 모듈 ex : global.console.log()
global 생략 가능 ex : console.log()
- 주요 전역 객체
process, console, Buffer(클래스), __filename, __dirname, module, exports, Timeout
- 유틸리티 모듈
문자열 포맷(sprintf() 처럼) , 상속
function Parent(){}
Parent.prototype.sayHello = function(){
console.log('Hello.from Parent Class');
}
function Child(){}
util.inherits(Child, Parent);
var child = new Child();
child.sayHello(); // 부모 멤버 함수 호출
- 이벤트 모듈
이벤트 다루기 : EventEmitter
이벤트 예 : 클라이언트의 접속 요청, 소켓에 데이터 도착, 파일 오픈/읽기 완료
이벤트 처리 : 비동기 처리, 리스너 함수
이벤트 리스너 함수 등록
emitter.addListener( eventname, listener )
emitter.on( eventname], listener )
emitter.once( eventname, listener ) // 단 한번 동작
// eventname : 이벤트 이름, listener : 이벤트 됐을때 동작하는 함수
이벤트 리스너 함수 삭제
emitter.removeListener( eventname, listener )
emitter.removeAllListeners( [eventname] )
최대 이벤트 핸들러 개수 (기본 10개)
emitter.setMaxListeners(n)
emitter.getMaxListeners()
이벤트 발생 시키기 (emit)
emitter.emit(eventname, [param0], [param1], [...])
호출 결과 : true(이벤트 처리), false(이벤트 처리 안됨)
커스텀 이벤트
val customEvent = new event.EventEmitter();
costomEvent.on('tick', function(){
console.log('occur custom event');
});
customEvent.emit('tick');
util 모듈을 이용해서 EventEmitter 상속
var Person = function(){};
// 상속
var util = require('util');
var EventEmitter = require('events').EventEmitter;
util.inherits(Person, EventEmitter);
// 객체
var p = new Person();
p.on('howAreYou', function(){
console.log('Fine, Thank you and you?'(
});
// 이벤트 발생
p.emit('howAreYou');
※ 리스터 함수, 에러
에러 처리
emitter.on('event', function(error, result){
if (error) {
// 에러처리
}
else {
// 정상 처리
}
}
- path 모듈
경로 정규화, 경로 생성, 디렉토리/파일 이름 추출, 파일 확장자 추출
경로 다듬기
path.normalize()
pathUtil.normalize('/user/tmp/../local///bin/');
// return : '/user/local/bin/'
경로 구성 요소 얻기
path.basename()
파일 이름, 경로 중 마지막 요소
path.dirname()
파일이 포함된 폴더 경로
path.extname()
확장자
경로 구성 객체
var info = paht.parse('/home/user/dir/file.txt;);
반환 :
{
root : "/",
dir : "/home/user.dir",
base : "file.txt",
ext : ".txt",
name : "file"
}
경로 만들기
pathUtil.sep // 구분자 반환
pathUtil.join() // 문자열 조합
pathUtil.format() // 객체를 조합
- fs 모듈
파일 디스크립터, 파일 경로
fs.read(fd, buffer, offset, length, position, callback)
fs.readSync(fd, buffer, offset, length, position)
파일 경로로 파일 다루기
fs.readFile(filename, [options], callback)
fs.readFileSync(filename, [options])
File Dexcrioption 얻기
var fd = fs.openSync(path, flags, [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])
파일 쓰기
fs.write(fd, data, [position], [encoding], callback)
fs.writeFile(filename, data, [options], callback)
fs.writeFileSync(filename, data, [options])
파일 종류
문자열 읽기 : 인코딩
바이너리 읽기 : buffer (설정안할 경우 default)
파일 접근
fs.access(path, [mode], callback)
fs.accessSync(path, [mode])
접근 모드
fs.F_OK : 존재 확인
fs.R_OK, W_OK, X_OK : 읽기/쓰기/실행 여부 확인
접근 불가능 하면 에러 발생
try catch를 이용
파일 상태
fs.stat(path, callback)
fs.statSync(path)
파일, 디렉토리 여부 : stats.isFile(), stats.isDirectory()
파일 크기 : stats.size
생성일/접근일/수정일 : stats.birthtime, stats.atime, stats.mtime
파일에 내용 추가
fs.appendFile(file, data, [options], callback)
fs.appendFileSync(file, data, [options])
없으면 생성
파일 삭제
fs.unlink(path, callback)
fs.unlinkSync
파일 없으면 에러
파일 이름 변경 / 이동
fs.rename(oldPath, newPath, callback)
fs.renameSync(oldPath, newPath)
디렉토리
생성 : fs.mkdir(path, [mode], callback), fs.mkdirSync
디렉토리 있으면 실패
삭제 : fs.rmdir(path, callback), fs.rmdirSync
비어 있지 않으면 실패
디렉토리 내 파일 목록
fs.readdir(path, callback), fs.readdirSync
디렉토리 없으면 에러
- 버퍼
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.byteLenth([string], encoding)
Buffer.compare(buffer1, buffer2)
Buffer.concat([list], toalLength)
Buffer.isBuffer(obj)
Buffer.isEncoding(encoding)
객체 메소드
buf.length
buf.fill(value, offset, end)
buf.slice(start, end)
buf.compare(otherBuffer)
buf.copy(targetBuffer, targetStart, sourceStart, sourceEnd)
- 스트림
콘솔 입출력, 파일 읽기쓰기, 서버 클라 간의 데이터 전송
종류
읽기 스트림 : Readable Stream
쓰기 스트림 : Writable Stream
읽기/쓰기 : Duplex
변환 : Transform
스트림 생성
createReadStream()
createWriteStream()
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() 호출
Writable Stream
메소드
쓰기, 인코딩
writable.setDefaultEncoding(encoding)
writable.write(chunk, encoding, callback)
닫기
writable.end(chunk, encoding, callback)
이벤트
drain : 출력 스트림에 남은 데이터를 모두 보낸 이벤트
error : 에러
finish : 모든 데이터를 쓴 이벤트
pipe : 읽기 스트림과 연결(pipe)된 이벤트
unpipe : 연결(pipe) 해제 이벤트
표준 입출력 스트림
process.stdin : 콘솔 입력
process.stdout : 콘솔 출력
스트림 연결
연결과 해제 (Readable)
readable.pipe(destination, options)
readable.unpipe(destination)
연결 이벤트 (Writable)
pipe
unpipe
스트림 연결
입력 스트림 : stdin
출력 스트림 : 파일
- URL
모듈 : var url = require('url')
url.parse() 분석
url.format() 만들기
url.resolve(from, to) 변환
- 쿼리 스트링
URL외에 HTTP 메시지 바디로 정보를 전달할 때도 사용
모듈 : var querystring = require('querystring');
쿼리 문자열 분석
querystring.parse(str, sep, eq, options)
// sep, eq : 쿼리 구분자와 = 기호
쿼리 문자열 만들기
querystring.stringify(queryObj)
- 클러스터
여러 시스템을 하나로 묶어서 사용하는 기술
개별 시스템 내에서 클러스터
멀티 프로세스, 멀티 코어
Node.js 어플리케이션 : 1개의 싱글 쓰레드
멀티 코어 시스템의 장점 살리기 : 클러스터
Node.js 클러스터
클러스터 사용시 포트 공유 - 서버 작성 편리
코어(프로세서)의 개수 만큼 사용
클러스터링
마스터
메인 프로세스
워커 생성
워커
보조 프로세스
마스터가 생성
모듈 : var cluster = require('cluster');
생성 : cluster.fork()
구분 : cluster.isMaster, cluster.isWorker
클러스터의 이벤트
fork : 워커 생성 이벤트
online : 워커 생성 후 동작하는 이벤트
listening : 워커에 작성한 서버의 listen 이벤트
disconnect : 워커 연결 종료
exit : 워커 프로세스 종료
워커의 이벤트
fomessage : 메세지 이벤트
disconnect : 워커 연결 종료
워커
접근 cluster.worker
식별자 worker.id
종료 worker.kill([signal='SIGTERM')
클러스터링 기능 지원 프로세스 모듈
pm2
데이터 전달
마스터가 워커에게 데이터 전달
worker.send( data )
워커의 데이터 이벤트
worker.on( 'message', function(data){ } );
워커가 마스터에게 데이터 전달
process.send( data )
마스터에서의 데이터 이벤트
var worker = cluster.fork();
worker.on( 'message', function(data){ } );
마스터와 워커 분리
cluster.setupMaster(settings)
exe : 워커 파일
args : 실행 파라미터
ex)
cluster.setupMaster({
exec: 'worker.js'
})