fs는 컴퓨터의 파일 시스템에 접근해 특정 디렉터리(폴더)나 파일을 읽고 쓰는 기능을 위한 모듈입니다.
const fs = require('fs');
fs.writeFile('./test.txt', 'cliel.com', (err) => {
if (err) {
console.log(err);
}
});
fs를 통해 파일을 쓰려면 writeFile 함수를 사용합니다. 첫 번째 매개변수에 생성 할파 일과 두 번째 매개변수에 파일에 작성할 내용을 전달합니다.
const fs = require('fs');
fs.readFile('./test.txt', (err, data) => {
console.log(data);
console.log(data.toString());
});
반대로 파일을 읽으려면 readFile함수를 사용합니다. 읽은 내용은 data로 알아낼 수 있는데 data에서 toString()을 해줘야 정상적으로 파일의 내용을 확인할 수 있습니다.
readFile함수를 만들때를 보면 콜백 함수로 (err, data) 형태를 만들었는데 이것은 해당 콜백을 promise로 바꿀 수 있음을 의미합니다. 그래서 아예 모듈을 가져올 때 promise형태로 가져와 콜백을 promise로 구현할 수 있습니다.
const fs = require('fs').promises;
fs.readFile('./test.txt')
.then((data) => {
console.log(data);
console.log(data.toString());
})
.catch((err) => {
console.error(log);
});
writeFile()이나 readFile()함수는 콜백 함수를 사용하므로 node.js는 해당 작업을 백그라운드로 돌리게 됩니다(promise도 마찬가지). 백그라운드로 작업을 돌리면 다음 작업이 대기할 필요 없이 바로 처리되지만 문제는 백그라운드로 여러 작업이 들어가 동시에 처리되면 어떤 작업이 언제 끝날 지를 알 수 없게 됩니다. 순서와 상관없이 처리돼도 상관없는 경우라면 괜찮지만 그렇지 않으면 문제가 생길 수 있습니다.
const fs = require('fs').promises;
fs.readFile('./test.txt')
.then((data) => {
console.log('1 : ' + data.toString());
})
.catch((err) => {
console.error(log);
});
fs.readFile('./test.txt')
.then((data) => {
console.log('2 : ' + data.toString());
})
.catch((err) => {
console.error(log);
});
fs.readFile('./test.txt')
.then((data) => {
console.log('3 : ' + data.toString());
})
.catch((err) => {
console.error(log);
});
만약 작업이 처리되는데 특정 순서를 지켜야 한다면 다음과 같이 동기메서드를 사용할 수 있습니다.
const fs = require('fs');
let data = fs.readFileSync('./test.txt');
console.log('1', data.toString());
data = fs.readFileSync('./test.txt');
console.log('2', data.toString());
data = fs.readFileSync('./test.txt');
console.log('3', data.toString());
하지만 백그라운드로 작업을 돌리면서도 순서에 맞춰 처리되도록 하려면 promise 체인을 이용하거나
const fs = require('fs').promises;
fs.readFile('./test.txt')
.then((data) => {
console.log('1 : ', data.toString());
return fs.readFile('./test.txt');
})
.then((data) => {
console.log('2 : ', data.toString());
return fs.readFile('./test.txt');
})
.then((data) => {
console.log('3 : ', data.toString());
})
.catch((err) => {
console.error(err);
})
async - await 사용해야 합니다.
const fs = require('fs').promises;
async function readFile() {
let data = await fs.readFile('./test.txt');
console.log('1 : ', data.toString());
data = await fs.readFile('./test.txt');
console.log('2 : ', data.toString());
data = await fs.readFile('./test.txt');
console.log('3 : ', data.toString());
}
readFile();
지금까지 파일을 읽는 경우는 파일전체를 한 번에 읽어서 출력해주는 형태를 하고 있었습니다. 이것은 시스템의 메모리가 읽어 들일 파일의 데이터만큼 커야 한다는 의미인데 이는 대용량 파일의 경우 문제가 생길 수 있습니다. 이런 경우 node.js에서는 스트림을 통해 적은 메모리로 큰 데이터에 대한 처리를 진행할 수 있습니다.
const fs = require('fs');
const readStream = fs.createReadStream('./test.txt');
const data = [];
readStream.on('data', (chunk) => {
data.push(chunk);
console.log(chunk);
});
readStream.on('end', () => {
console.log(Buffer.concat(data).toString());
});
readStream.on('error', (err) => {
console.log(err);
});
스트림 방식으로 파일을 읽으려면 createReadStream() 함수를 사용하며 기본적으로 한 번에 64kbyte씩 읽습니다. 이렇게 읽어 들인 데이터는 data이벤트를 통해 chunk로 받을 수 있으며 모든 파일을 읽고 나면 end이벤트에서 concat으로 받은 데이터를 조합해 읽어 들인 데이터를 확인합니다.
만약 좀더 작은 크기로 데이터를 읽어 들이려면 highWaterMark속성을 통해 읽어 들일 데이터의 단위를 지정합니다.
const readStream = fs.createReadStream('./test.txt', { highWaterMark : 8 });
이러한 스트림방식은 파일을 읽을 때뿐만 아니라 파일을 생성하는 경우에도 사용할 수 있습니다.
const fs = require('fs');
const writeStream = fs.createWriteStream('./test.txt');
writeStream.on('finish', () => {
console.log('완료');
})
writeStream.write('12345');
writeStream.write('67890');
writeStream.end();
스트림 방식으로 파일을 쓰는 경우에는 createWriteStream()을 사용하며 write() 함수를 통해 필요한 데이터를 작성합니다.
파일 작성이 모두 완료되었으면 end() 함수를 호출하는데, 그러면 finish이벤트가 열리고 이를 통해 완료처리를 진행할 수 있습니다.
스트림은 또한 pipe와 연결하여 한쪽에서 다른 한쪽으로 데이터를 물 흐르듯이 보낼 수 있는 처리를 구현하기도 합니다.
const fs = require('fs');
const readStream = fs.createReadStream('test.txt');
const writeStream = fs.createWriteStream('test2.txt');
readStream.pipe(writeStream);
pipe() 함수를 통해서 읽는 스트림에서 쓰는 스트림으로 데이터를 보내고 있습니다. 스트림으로 64 kbyte씩 읽고 쓰고를 진행하여 파일을 복사하게 되는 것입니다. 이때도 물론 hightWaterMark속성을 통해서 스트림 크기를 조정할 수 있습니다.
이러한 방식을 이용하면 pipe()를 통해 여러 스트림을 이어붙여 원하는 처리를 구현할 수 있습니다.
const fs = require('fs');
const gzip = require('zlib');
const readStream = fs.createReadStream('test.txt');
const gzipStream = gzip.createGzip();
const writeStream = fs.createWriteStream('test2.gzip');
readStream.pipe(gzipStream).pipe(writeStream);
예제는 파일을 읽는 스트림에 데이터를 압축하는 압축스트림과 파일을 쓰는 스트립을 pipe로 연결하여 읽은 데이터를 압축하고 압축한 데이터를 파일로 생성하도록 하고 있습니다.
이외에도 디렉토리를디렉터리를 만드는 mkdir(), 파일의 이름을 바꾸는 rename(), 디렉터리를 지우는 unlink()와 파일을 삭제하는 rmdir(), 파일의 수정이나 삭제 등의 상태를 감시하는 watch() 함수 등 파일과 디렉터리에 관한 수많은 함수가 존재합니다. 해당 함수의 종류와 사용방법에 관한 자세한 내용은 node.js의 공식문서를 통해 확인해 보시기 바랍니다. 현재까지의 예제와 크게 사용방법이 다르지 않습니다.
'Server > node.js' 카테고리의 다른 글
[node.js] 이벤트 처리 (0) | 2021.03.03 |
---|---|
[node.js] 설치 (Windows WSL2) (0) | 2021.03.03 |
[node.js] 기본모듈 (0) | 2021.03.03 |
[node.js] 전역객체 (0) | 2021.03.02 |
[node.js] 모듈(Module) (0) | 2021.03.02 |