1. 노드의 파일 시스템은 파일을 다루는 기능, 디렉터리를 다루는 기능으로 구성되어 있다. 또한, 동기식 I/O(Input output), 비동기식 I/O 기능을 함께 제공한다.
2. 동기식 IO 는 파일 작업이 끝날 때까지 대기하며, 비 동기식 IO는 파일작업을 요청하고 그 다음 작업을 바로 수행한다. 이후 파일 작업이 끝나면 그 상태는 이벤트로 받아서 처리한다. 동기식 IO와 비동기식 IO를 구별하기 위해 동기식 IO 메소드는 Sync라는 단어를 붙인다.
(동기식 IO가 더 느리다. 대기하므로)
동기적
// 파일을 동기식 IO로 읽어 들인다.
var fs = require('fs');
// 파일 시스템에 접근 하기 위해 fs 모듈 사용
var data = fs.readFileSync('./package.json','utf-8');
// readFileSync 이므로 동기식 동작
console.log(data);
// 파일을 다 읽을때 까지 실행 되지 않는다. why? 동기식이므로 모든 동작이 끝나야함.
{
"name": "server_side_javascript",
"version": "1.0.0",
"description": "server side javascript tutorials",
"main": "module.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"body": "^5.1.0",
"express": "^4.15.4",
"jade": "^1.11.0",
"underscore": "^1.8.3"
}
}
비동기적
var fs = require('fs');
// 파일 시스템에 접근 하기 위해 fs 모듈 사용
fs.readFile('./package.json','utf-8',function(err,data){
// readFile 이므로 비 동기식이며, readFile()메소드를 실행하면서 세번쨰 파라미터로 전달된 함수는 파일을 읽어들이는 작업이 끝났을때 호출이 된다. 이때, err,data 를 전달받아 오류 발생여부 확인할 수 있다.
console.log(data);
//에러 발생시 err은 오류 데이터가 들어가고 에러 발생하지 않았을 경우 null 값이 들어간다.
});
console.log('프로젝트 폴더 안의 package.json file Read require');
// readFile메소드를 먼저 호출했지만, 출력이 먼저나온다 why? 비동기적 동작을 하였기 때문에.
{
"name": "server_side_javascript",
"version": "1.0.0",
"description": "server side javascript tutorials",
"main": "module.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"body": "^5.1.0",
"express": "^4.15.4",
"jade": "^1.11.0",
"underscore": "^1.8.3"
}
}
메소드 이름 |
설명 |
readFile(filename,[encoding],[callback] |
비동기식 IO로 파일을 읽어 들인다. |
readFileSync(filename,[encoding]) |
동기식 IO로 파일을 읽어 들인다. |
writeFile(filename,data,encoding='utf',[callback]) |
비동기식 IO로 파일을 쓴다. |
writeFileSync(filename,data, encoding='utf8')
|
동기식 IO로 파일을 쓴다. |
=> 대부분은 비동기 방식으로 파일을 읽고 쓰기 때문에 비동기 방식의 메소드를 주로 사용한다.
fs 모듈 (file System)
1. 비동기식 IO
readFile,writeFile
2. 동기식 IO
readFileSync,writeFileSync
var fs = require('fs');
fs.writeFile('./output.txt', 'hello world!',function(err){
// writeFile()메소드는 1파라미터: 파일이름 2파라미터: 파일에 쓸 내용 3파라미터: 콜백 함수
if(err){
console.log('Error' + err);
}
console.log('output.txt 파일에 데이터 쓰기 완료');
});
output.txt 파일에 데이터 쓰기 완료
=> output.txt 파일에 글자가 쓰여진것을 알수 있다.
메소드 이름 |
설명 |
open(path,flags,[mode] ,[callback]) |
파일을 엽니다. |
read(fd,buffer,offset,length ,positon,[callback]) |
지정한 부분의 파일 내용을 읽어 들입니다. |
write(fd,buffer,offset,length ,position,[callback]) |
파일의 지정한 부분에 데이터를 씁니다. |
close(fd,[callback]) |
파일을 닫아준다. |
var fs = require('fs');
// fs 모듈 가져옴
//파일 열기
fs.open('./output.txt','w',function(err,fd){
if(err) throw err;
// 버퍼 객체 생성.
var buf = new Buffer('안녕!\n');
// 파일 쓰기 fs.write(fd,buf,0,buf.length,null,function(err,written,buffer){
if(err) throw err;
console.log(err,written,buffer);
// 파일 닫기
fs.close(fd,function(){
console.log('파일 열고 데이터 쓰고 파일 닫기 완료')
})
})
})
null 8
파일 열고 데이터 쓰고 파일 닫기 완료
=> 코드 설명 : 'w' 플래그를 사용하였으므로, 쓰기 작업만을 위해 파일을 연다. 콜백 함수 안에는 버퍼 객체를 하나 만든다. 그 이후 write() 메소드를 호출 하여 파일에 내용을 쓴다. 파일이 열리면 fd 객체를 전달 받을 수 있으므로 이 fd 객체로 파일을 구별한다. 파일에 데이터를 쓸때 어느 위치에 쓸 것인지 정할 수 있다.
함수 호출 순서 = 'open -> write -> close'
open() 메소드 호출 하면 파일 열수 있다. 그리고 파일을 열면 write()메소드를 사용해서 데이터를 쓸 수 있다. 데이터는 필요한 만큼 Buffer 객체 안에 쓴다. 파일에 데이터를 쓰고 나면 close() 메소드를 호출하여 파일을 닫습니다.
이렇게 각 메소드를 호출 할 때 마다 콜백 함수를 파라미터로 전달 하므로 각각의 기능이 실행을 끝냈을 때 그 다음메소드를 전달한다.
플래그 |
설명 |
'r' |
읽기에 사용하는 플래그 파일이 없으면 예외 발생 |
'w' |
쓰기에 사용하는 플래그 파일이 없으면 만들어 지고 파일이 있으면 이전내용을 모두 삭제한다 |
'w+' |
읽기와 쓰기에 모두 사용하는 플래그. 파일이 없으면 만들어 지고 파일이 있으면 이전 내용을 모두 삭제 한다 |
'a+' |
읽기와 추가에 모두 사용하는 플래그 파일이 없으면 만들어지고 있으면 이전 내용에 새로운 내용을 추가 한다. |
- 위의 예제를 통해 만들어진 파일을 읽어 들여 볼까요?
var fs = require('fs');
fs.open('./output.txt','r',function(err,fd){
if(err) throw err;
var buf = new Buffer(10);
// Buffer 객체는 바이너리 데이터를 읽고 쓰는데 사용한다. 새로운 버퍼 객체를 만들기 위해서는 new 연산자 사용하며, 그 안에 들어갈 바이트(byte) 데이터의 크기만 지정하면 된다.
write() 메소드를 사용해 문자열-> 버퍼에 쓰거나, 처음부터 문자열을 -> 버퍼객체를 만들수 있다.
console.log('버퍼 타입 : %s',Buffer.isBuffer(buf));
// 파일 읽기
fs.read(fd, buf, 0, buf.length, null, function(err,bytesRead,buffer){
if(err) throw err;
var inStr = buffer.toString('utf8',0,bytesRead);
console.log('파일에서 읽어온 데이터 : %s', inStr);
console.log(err,bytesRead,buffer);
//파일 닫기
fs.close(fd, function(){
console.log('output.txt 파일을 열고 읽기 완료');
})
})
})
버퍼 타입 : true
파일에서 읽어온 데이터 : 안녕!
null 8
output.txt 파일을 열고 읽기 완료
// 1.버퍼 객체를 크기만 지정하여 만든 후 문자열을 씁니다.
var output = '안녕 1!';
var buffer1 = new Buffer(10);
var len = buffer1.write(output, 'utf8');
console.log('첫번째 버퍼의 문자열 : %s', buffer1.toString());
// 2.버퍼 객체를 문자열을 이용해 만듭니다.
var buffer2 = new Buffer('안녕 2!', 'utf8');
console.log('두번째 버퍼의 문자열 : %s', buffer2.toString());
=> 두개의 버퍼를 서로 다른 방식으로 만든다.
1. 빈버퍼 먼저 만들고 그안에 문자열 넣기
2. 버퍼를 만들면서 문자열을 파라미터로 전달
// 타입을 확인합니다. isBuffer() 메소드로 확인
console.log('버퍼 객체의 타입 : %s', Buffer.isBuffer(buffer1));
// 버퍼 객체에 들어있는 문자열 데이터를 문자열 변수로 만듭니다.
var byteLen = Buffer.byteLength(output);
var str1 = buffer1.toString('utf8', 0, byteLen);
var str2 = buffer2.toString('utf8');
// 두번째 버퍼 객체의 문자열을 첫 번째 버퍼 객체로 복사합니다. copy() 메소드 사용
buffer1.copy(buffer2, 0, 0, len);
console.log('두번째 버퍼에 복사한 후의 문자열 : %s', buffer2.toString('utf8'));
// 두 개의 버퍼를 붙여줍니다. 두개의 버퍼를 붙여서 새로운 버퍼 객체를 만들 때
var buffer3 = Buffer.concat([buffer1, buffer2]);
console.log('두 개의 버퍼를 붙인 후의 문자열 : %s', buffer3.toString('utf8'));
첫번째 버퍼의 문자열 : 안녕 1!
두번째 버퍼의 문자열 : 안녕 2!
버퍼 객체의 타입 : true
두번째 버퍼에 복사한 후의 문자열 : 안녕 1!
두 개의 버퍼를 붙인 후의 문자열 : 안녕 1! 안녕 1!
1. 파일을 읽거나 쓸 때는 데이터 단위가 아닌 스트림 단위로 처리 할 수 있다.
2. 스트림은 데이터가 전달되는 통로
3. 버퍼는 임시 저장소
4. 파일을 읽을 때는 createReadStream(), 파일을 쓸때는 createWriteStream() 메소드로 스트림 객체를 만든 후 데이터를 읽고 쓰게 된다.
메소드 이름 |
설명 |
createReadStream(path,[options]) |
파일을 읽기 위한 스트림 객체를 만든다. |
createWriteStream(path,[options]) |
파일을 쓰기 위한 스트림 객체를 만든다. |
Example) output.txt파일 읽어 들인후 -> output2.txt 파일로 쓰는 예제
// 스트림 단위로 파일 읽고 쓰기
var fs = require('fs');
var infile = fs.createReadStream('./output.txt',{falgs : 'r'});
var outfile = fs.createWriteStream('./output2.txt',{flags :'w'});
// Stream 이벤트 등록
infile.on('data',function(data){
console.log('읽어 들인 데이터',data);
outfile.write(data);
});
// Stream 이벤트 등록
infile.on('end',function(){
console.log('파일 읽기 종료');
outfile.end(function(){
console.log('파일 쓰기 종료');
})
읽어 들인 데이터
파일 읽기 종료
파일 쓰기 종료
- 위의 예제를 두 개의 스트림을 붙여 주면 더 간단하게 만드는 것을 알아볼까요?
var fs = require('fs');
var inname = './output.txt';
var outname = './output2.txt';
// outname의 파일을 모두 삭제 하기 위함.
fs.exists(outname, function(err){
if(err){
fs.unlink(outname,function(err){ // link를 끊어 버리기 위해 unlink(파일 삭제를 의미한다.)
if(err) throw err;
console.log('기존 파일 [' + outname +']삭제함');
})
}
})
// infile 과 outfile 변수에 스트림을 쓴다.
var infile = fs.createReadStream(inname,{flags:'r'});
var outfile = fs.createWriteStream(outname,{flags : 'w'});
infile.pipe(outfile); // infile 스트림과 outfile 스트림을 객체를 연결하기 위한 pipe() => 파일 내용 복사
console.log('파일 복사 [ ' + inname + '] -> ' + outname + ']');
파일 복사 [ ./output.txt] -> ./output2.txt]
- http 모듈로 요청받은 파일 내용을 읽고 응답하기
var fs= require('fs');
var http = require('http');
var server = http.createServer(function(req,res){ // 웹 서버에서 클라이언트로 부터 요청을 받으면
// 파일을 읽어 응답 스트림과 pite로 연결 한다.
var instream=fs.createReadStream('./output.txt'); // output.txt 스트림을 생성
instream.pipe(res); // output.txt 스트림과 응답 res 를 연결 한다 pipe() 메소드로 pipe() 메소드는 연결할 수 있다는것만 이해하면 된다.
// 쉽게 말해서 output.txt 파일의 통로가 열리게 되면서 pipe에 의해 요청된 파일의 응답을 할수 있게 된다. pipe() 메소드는 읽기 스트림과 쓰기 스트림을 연결 시킨다.
});
server.listen(7001,'127.0.0.1');
var fs = require('fs');
fs.mkdir('./docs',0666,function(err){
if(err) throw err;
console.log('새로운 docs 폴더를 만들었습니다.');
fs.rmdir('./docs',function(err){
if(err) throw err;
console.log("docs 폴더가 삭제 되었습니다.")
})
})