[Node] 파일 시스템

Happy_Nerd·2021년 5월 13일
0

Node

목록 보기
3/3
post-thumbnail

fs 모듈

파일 시스템에 접근하는 모듈

노드의 fs모듈을 통해 파일을 생성하거나 삭제할 수 있고, 읽거나 쓸 수 있다.
파일 뿐만 아니라 폴더도 생성/삭제할 수 있다.

readFile, writeFile

readFile()

파일을 읽을 때 사용하는 메소드이다.

const fs = require('fs');

fs.readFile('./readme.txt', (err, data) => {
  if (err) {
    throw err;
  }
  console.log(data);
  console.log(data.toString());
});

readFile의 첫번째 인자는 읽을 파일의 경로이고,
두번째 인자에는 콜백함수를 넣어준다.

콜백함수의 매개변수로 에러데이터를 받아서
파일을 읽는 과정에서 발생한 에러의 내용은 err로 알 수 있고,
읽어들인 결과는 data로 알 수 있다.

파일 내용을 읽은 결과를 console로 출력해보면 Buffer가 출력된다.
Buffer는 우리가 읽을 수 있는 형식이 아니라서 toString()을 사용해 문자열로 변환해야 읽을 수 있다.

writeFile

파일을 쓸 때 사용하는 메소드이다.

const fs = require('fs');

fs.writeFile('./writeme.txt', '입력할 내용', (err) => {
  if (err) {
    throw err;
  }
  fs.readFile('./writeme.txt', (err, data) => {
    if (err) {
      throw err;
    }
    console.log(data.toString());
  });
});

writeFile 메소드에는 생성될 파일의 경로와 해당 파일에 쓸 내용을 입력해주면 된다.

readFileSync, writeFileSync

이 두 메소드는 파일의 읽기와 쓰기를 동기 처리를 해준다.

const fs = require('fs');

let data = fs.readFileSync('./readme.txt');
console.log(data.toString());
data = fs.readFileSync('./readme2.txt');
console.log(data.toString());

위의 코드를 실행해보면 처음 console.log에는 readme.txt파일을 읽은 결과가 출력되고,
두번째에는 readme2.txt파일을 읽은 결과가 출력될 것이다.

readFile, writeFile은 비동기 메소드라서 실행 순서가 보장되지 않지만,
readFileSync, writeFileSync는 동기 메소드라서 순서대로 실행이 된다.
여기서 순서대로 실행이 된다는 것은 하나의 요청이 들어가면 해당 요청이 완료된 후에야 다음 요청이 들어갈 수 있다는 것과 마찬가지이다.

노드는 싱글 스레드이기 때문에 계속해서 readFileSync 같은 동기 처리를 하게 되면 비효율적이게 된다.
그렇기 때문에 sync가 있는 메소드보다는 비동기처리를 해주는 메소드를 사용하는 것이 효율적인 측면에서 좋다.

비동기 방식으로 하면서 순서를 유지하고 싶다면,
promise나 async/await을 사용하는 것이 좋다.

버퍼와 스트림

파일 시스템에서 중요한 개념이 버퍼와 스트림이 아닐까 싶다.

파일을 읽거나 쓰는 방식에는 버퍼를 이용하는 방식스트림을 이용하는 방식이 있다.

버퍼

노드는 파일을 읽을 때 메모리에 파일 크기만큼 공간을 만들고 파일 데이터를 메모리에 저장한 뒤 사용자가 조작할 수 있게 해준다.
여기서 메모리에 저장된 데이터버퍼라고 한다.

버퍼는 Buffer 클래스를 통해 직접 다룰 수 있다.

const buffer = Buffer.from('버퍼로 바꿀 내용');
const makeBuffer = Buffer.alloc(5);

Buffer 객체는 여러가지 메소드를 제공한다.
위의 코드에서 사용된 Buffer.from()은 문자열을 버퍼로 바꿔주고,
Buffer.alloc()은 빈 버퍼를 생성해주는데, 인자로 원하는 크기의 바이트를 넣으면 해당 크기의 버퍼가 생성된다.

스트림

readFile을 실행하면 버퍼가 출력되는데,
버퍼를 사용하면 생기는 문제점도 있다.

만약 100MB 크기의 파일을 읽으려고 readFile을 하면,
메모리에 100MB만큼의 버퍼를 만들어야 한다.
크기가 큰 파일의 경우 더 많은 메모리가 사용이 되어야하기 때문에
메모리에 큰 부하를 줘서 메모리 문제가 발생할 수 있다.

이러한 문제점을 해결하기 위해!
버퍼의 크기를 작게 만들어서 여러 번에 나눠서 보내는 방식이 등장했는데, 이런 방식을 하게 하는 것이 스트림이다.

createReadSream

읽기 스트림을 만들어준다.
첫번째 인자로는 읽을 파일의 경로를 받고,
두번째 인자에는 옵션 객체를 받는다.

readStream은 이벤트 리스너를 붙여서 사용한다.
사용하는 이벤트에는 data, end, error 등이 있다.

const fs = require('fs');

const readStream = fs.createReadStream('./readme.txt');
const data = [];

readStream.on('data', (chuck) => {
  data.push(chuck);
  console.log('data: ', chunk);
});

readStream.on('end', () => {
  console.log(Buffer.concat(data).toString());
});

readStream.on('error', (err) => {
  console.log(err);
});

createWriteStream

쓰기 스트림을 만들어준다.
첫번째 인자에 출력할 파일의 경로를 넣어주고
두번째 인자는 읽기 스트림과 마찬가지로 옵션 객체를 넣어준다.

write() 메소드로 내용을 작성할 수 있는데, 여러번 호출할 수도 있다.

파일을 쓰는 것이 완료되면, 즉 end()가 호출되면 finish 이벤트가 발생한다.

const fs = require('fs');

const writeStream = fs.createWriteStream('./writeme.txt');

writeStream.on('finish', () => {
  console.log('파일 쓰기 종료');
});

writeStream.write('내용 적기');
writeStream.end();

pipe

createReadStream으로 파일을 읽고 그 스트림을 전달받아서 바로 createWriteStream으로 파일을 쓸 수도 있다.
이때 사용하는 것이 pipe 메소드이다.

const fs = require('fs');

const readStream = fs.createReadStream('readme.txt');
const writeStream = fs.createWriteStream('writeme.txt');

readStream.pipe(writeStream);

이렇게 하면 읽어들인 것을 바로 writeStream에 넘겨서 파일을 쓸 수 있다.

참고
- node.js 교과서

0개의 댓글