on
Node.js 학습_파일 시스템(fs) 모듈 / 버퍼와 스트림 / pipe와 스트림...
Node.js 학습_파일 시스템(fs) 모듈 / 버퍼와 스트림 / pipe와 스트림...
728x90
반응형
# fs (파일 시스템 모듈)
파일 시스템에 접근하는 모듈
파일/폴더 생성, 삭제, 읽기, 쓰기 가능
웹 브라우저에서는 제한적이었으나 노드는 권한을 가지고 있음
아래는 파일을 읽는 예제들.
# readfile.js const fs = require('fs'); fs.readFile('./readme.txt', (err, data) => { if (err) { throw err; } console.log(data); console.log(data.toString()); }); # readme.txt 저를 읽어 주세요. 출력 결과 저를 읽어주세요.
fs.promise 사용 예
const fs = require('fs').promises; fs.readFile('./readme.txt') .then((data) => { console.log(data); console.log(data.toString()); }) .catch((err) => { throw err; }); 출력 결과 저를 읽어주세요.
아래는 파일을 생성하는 예제들.
# writefile.js const fs = require('fs').promises; fs.writeFile('./writeme.txt', '글이 입력됩니다.') .then(() => { return fs.readFile('./writeme.txt'); }) .then((data) => { console.log(data.toString()); }) .catch((err) => { throw err; }); 출력 결과 writeme.txt 파일 생성과 함께 아래 문구 출력. 글이 입력됩니다.
콜백이나 프로미스는 대부분의 경우 비동기라 순서대로 처리되지 않음. (매 번 순서가 다르게 실행됨.)
# async.js const fs = require('fs'); fs.readFile('./readme.txt', (err, data) => { if (err) { throw err; } console.log('1번', data.toString()); }); fs.readFile('./readme.txt', (err, data) => { if (err) { throw err; } console.log('2번', data.toString()); }); fs.readFile('./readme.txt', (err, data) => { if (err) { throw err; } console.log('3번', data.toString()); }); fs.readFile('./readme.txt', (err, data) => { if (err) { throw err; } console.log('4번', data.toString()); }); 출력 결과 (변동 됨) 2번 저를 읽어주세요. 3번 저를 읽어주세요. 1번 저를 읽어주세요. 4번 저를 읽어주세요.
아래와 같이 동기 방식으로도 가능. (동시에 돌릴수 없어서 비 효율적_실행 시 다른 작업(다른 사용자)들은 기다려야함. / 딱 한번 실행 or 서버 초기화 작업 시 사용)
# sync.js const fs = require('fs'); let data = fs.readFileSync('./readme.txt'); console.log('1번', data.toString()); data = fs.readFileSync('./readme.txt'); console.log('2번', data.toString()); data = fs.readFileSync('./readme.txt'); console.log('3번', data.toString()); data = fs.readFileSync('./readme.txt'); console.log('4번', data.toString()); 출력 결과 1번 저를 읽어주세요. 2번 저를 읽어주세요. 3번 저를 읽어주세요. 4번 저를 읽어주세요.
가능하면 비 동기작업을 하면서 순서를 유지하는걸 추천함. (콜백 헬 발생 가능성 존재.)
# async.js const fs = require('fs'); fs.readFile('./readme.txt', (err, data) => { if (err) { throw err; } console.log('1번', data.toString()); fs.readFile('./readme.txt', (err, data) => { if (err) { throw err; } console.log('2번', data.toString()); fs.readFile('./readme.txt', (err, data) => { if (err) { throw err; } console.log('3번', data.toString()); fs.readFile('./readme.txt', (err, data) => { if (err) { throw err; } console.log('4번', data.toString()); }); }); }); }); 출력 결과 1번 저를 읽어주세요. 2번 저를 읽어주세요. 3번 저를 읽어주세요. 4번 저를 읽어주세요.
콜백 헬 해결을 위해 프로미스 사용.
const fs = require('fs').promises; async function main() { let data = await fs.readFile('./readme.txt'); console.log('1번', data.toString()); data = await fs.readFile('./readme.txt'); console.log('2번', data.toString()); data = await fs.readFile('./readme.txt'); console.log('3번', data.toString()); data = await fs.readFile('./readme.txt'); console.log('4번', data.toString()); } main();
비동기로 하되 순서를 지키는게 순서를 지키면서 동시성도 지키는 좋은 방법이다.
sync는 편하지만 실제로 서버에서 사용 시 문제 발생 가능성 존재.
# 버퍼와 스트림
버퍼 : 일정한 크기로 모아두는 데이터.
일정한 크기가 되면 한 번에 처리. 버퍼링 : 버퍼에 데이터가 찰 때까지 모으는 작업.
스트림 : 데이터의 흐름
일정한 크기로 나눠서 여러 번에 걸쳐서 처리. 버퍼(또는 청크)의 크기를 작게 만들어서 주기적으로 데이터를 전달. 스트리밍 : 일정한 크기의 데이터를 지속적으로 전달하는 작업.
노드에서는 Buffer 객체 사용.
# buffer.js const buffer = Buffer.from('저를 버퍼로 바꿔보세요.'); console.log(buffer); console.log(buffer.length); console.log(buffer.toString()); 출력 결과 33 저를 버퍼로 바꿔보세요.
const buffer = Buffer.from('저를 버퍼로 바꿔보세요.'); console.log(buffer); console.log(buffer.length); console.log(buffer.toString()); const array = [Buffer.from('띄엄 '), Buffer.from('띄엄 '), Buffer.from('띄어쓰기 ')]; console.log(Buffer.concat(array).toString()); 출력 결과 33 저를 버퍼로 바꿔보세요. 띄엄 띄엄 띄어쓰기
빈 버퍼 생성
console.log('-----Buffer.alloc()-----'); console.log(Buffer.alloc(5));
나눠서 처리 후 최종적으로 모아주기. (Stream은 동시에 오는 것이 아닌, 순서대로 진행 됨)
# createReadStream.js const fs = require('fs'); const readStream = fs.createReadStream('./readme3.txt'); const data = []; readStream.on('data', (chunk) => { data.push(chunk); console.log('data', chunk, chunk.length); }); readStream.on('end', () => { console.log('end : ', Buffer.concat(data).toString()) }); # readme3.txt 저는 조금씩 나눠서 전달됩니다. 나눠진 조각을 chunk라고 부릅니다. 안녕하세요. 헬로 노드 헬로 스트림 헬로 버퍼. 출력 결과 data 153 end : 저는 조금씩 나눠서 전달됩니다. 나눠진 조각을 chunk라고 부릅니다. 안녕하세요. 헬로 노드 헬로 스트림 헬로 버퍼.
비동기는 에러처리 꼭 해줘야 함.
const fs = require('fs'); const readStream = fs.createReadStream('./readme3.txt', { highWaterMark : 16 }); const data = []; readStream.on('data', (chunk) => { data.push(chunk); console.log('data', chunk, chunk.length); }); readStream.on('end', () => { console.log('end : ', Buffer.concat(data).toString()) }); readStream.on('error', (err) => { console.log('error : ', err) }); 출력 결과 data 16 data 16 data 16 data 16 data 16 data 16 data 16 data 16 data 16 data 9 end : 저는 조금씩 나눠서 전달됩니다. 나눠진 조각을 chunk라고 부릅니다. 안녕하세요. 헬로 노드 헬로 스트림 헬로 버퍼.
스트림 방식을 사용하게 되면 메모리를 아낄 수 있어서 좋음.
쓰기 할때도 메모리 효율적.
# createWriteStream.js const fs = require('fs'); const writeStream = fs.createWriteStream('./writeme2.txt'); writeStream.on('finish', () => { console.log('파일 쓰기 완료'); }); writeStream.write('이 글을 씁니다.
'); writeStream.write('한 번 더 씁니다.'); writeStream.end();
## pipe와 스트림 메모리 효율 확인
pipe 이용 파일 복사
const fs = require('fs'); const readStream = fs.createReadStream('./readme3.txt', { highwaterMark : 16 }); const writeStream = fs.createWriteStream('./writeme3.txt'); readStream.pipe(writeStream);
pipe 이용 압축 스트리밍
const fs = require('fs'); const zlib = require('zlib'); const readStream = fs.createReadStream('./readme3.txt', { highwaterMark : 16 }); const zlibStream = zlib.createGzip(); const writeStream = fs.createWriteStream('./writeme3.txt'); readStream.pipe(zlibStream).pipe(writeStream);
큰 파일 만드는 예.
const fs = require('fs'); const file = fs.createWriteStream('./big.txt'); for (let i = 0; i <= 10_000_000; i++) { file.write('안녕하세요. 엄청나게 큰 파일을 만들어 볼 것입니다. 각오 단단히 하세요!
'); } file.end();
큰 파일 만들어서 메모리 비교 (버퍼 방식 / 스트림 방식 비교)
# buffer-memory.js const fs = require('fs'); console.log('before : ', process.memoryUsage().rss); const data1 = fs.readFileSync('./big.txt'); fs.writeFileSync('./big2.txt', data1); console.log('buffer : ', process.memoryUsage().rss); 출력 결과 before : 19705856 buffer : 1020694528 # stream-memory.js const fs = require('fs'); console.log('before : ', process.memoryUsage().rss); const readStream = fs.readFileSync('./big.txt'); const writeStream = fs.createWriteStream('./big3.txt'); readStream.pipe(writeStream); readStream.on('end', () => { console.log('stream : ', process.memoryUsage().rss); }); 출력 결과 before : 19677184 stream : 29220864
## 파일 및 폴더 생성
const fs = require('fs').promises; const constants = require('fs').constants; fs.access('./folder', constants.F_OK | constants.W_OK | constants.R_OK) .then(() => { return Promise.reject('이미 폴더가 존재합니다.'); }) .catch((err) => { if (err.code === 'ENOENT') { console.log('폴더 없음'); return fs.mkdir('./folder'); } return Promise.reject(err); }) .then(() => { console.log('폴더 만들기 성공'); return fs.open('./folder/file.js', 'w'); }) .then((fd) => { console.log('빈 파일 만들기 성공 ', fd); fs.rename('./folder/file.js', './folder/newfile.js'); }) .catch((err) => { console.error(err); });
fs.access(경로, 옵션, 콜백) : 폴더나 파일에 접근할 수 있는지 체크함. 두 번째 인자로 상수들을 넣음. F_OK는 파일 존재 여부 / R_OK는 읽기 권한 여부 / W_OK는 쓰기 권한 여부를 체크한다. 파일/폴더나 권한이 없다면 에러가 발생, 파일/폴더가 없을 때의 에러 코드는 ENOENT로 설정.
fs.mkdir(경로, 콜백) : 폴더를 만든느 메서드. 이미 폴더가 있다면 에러가 발생하므로 먼저 access() 메서드를 호출해서 확인하는 것이 중요.
fs.open(경로, 옵션, 콜백) : 파일의 아이디(fd 변수)를 가져오는 메서드. 파일이 없다면 파일을 생성한 뒤 그 아이디를 가져온다. 가져온 아이디를 사용해 fs.read()나 fs.write()로 읽거나 쓸 수 있다. 두 번째 인자로 어떤 동작을 할 것인지 설정할 수 있다. 쓰려면 w, 읽으려면 r, 기존 파일에 추가하려면 a로 설정. 예제에서는 w로 설정했으므로 파일이 없을 때 새로 만들 수 있었음. r이었다면 에러가 발생하였을 것임.
fs.rename(기존 경로, 새 경로, 콜백) : 파일의 이름을 바꾸는 메서드. 기존 파일의 위치와 새로운 파일 위치를 적어주면 된다. 반드시 같은 폴더를 지정할 필요는 없으며 잘라내기 같은 기능을 할 수도 있다.
## 폴더 내용 확인 및 삭제
fs.readdir(경로, 콜백) : 폴더 안의 내용물을 확인할 수 있다. 배열 안에 내부 파일과 폴더명이 나온다.
fs.unlink(경로, 콜백) : 파일을 지울 수 있다. 파일이 없다면 에러가 발생하므로 먼저 파일이 있는지를 꼭 확인해야 한다.
fs.rmdir(경로, 콜백) : 폴더를 지울 수 있다. 폴더 안에 파일이 있다면 에러가 발생하므로 먼저 내부 파일을 모두 지우고 호출해야 한다.
const fs = require('fs').promises; fs.readdir('./folder') .then((dir) => { console.log('폴더 내용 확인 ', dir); return fs.unlink('./folder/newFile.js'); }) .then(() => { console.log('파일 삭제 성공'); return fs.rmdir('./folder'); }) .then(() => { console.log('폴더 삭제 성공'); }) .catch((err) => { console.error(err); });
## 파일을 복사하는 방법
const fs = require('fs').promises; fs.copyFile('readme4.txt', 'writeme4.txt') .then(() => { console.log('복사 완료'); }) .catch((error) => { console.error(error); })
## 파일을 감시하는 방법(변경 사항 발생 시 이벤트 호출)
target.txt 파일 바뀔 때 이벤트 호출하는 예.
const fs = require('fs'); fs.watch('./target.txt', (eventType, filename) => { console.log(eventType, filename); });
728x90
반응형
from http://dlagusgh1.tistory.com/835 by ccl(A) rewrite - 2021-10-03 21:26:41