본문 바로가기

Node.js

03. path, File System 모듈과 스트림

1. path 모듈

path 모듈에는 파일이나 디렉터리 경로를 다룰 수 있는 함수들이 포함되어 있습니다.

경로를 다루는 모듈이 필요한 이유는 운영체제 간에 경로를 구분하는 구분자가 다르기 때문입니다. 

윈도우 운영체제는 파일 경로를 포기할 때 경로 구분자로 역슬래시(\)를 사용합니다. 반면에 유닉스 기반 운영체제인 맥 OS나 리눅스에서는 경로 구분자로 슬래시(/)를 사용합니다.

 

경로를 다루는 주요 함수 살펴보기

path 모듈 가져오기 : 애플리케이션을 제작할 때는 하위 폴더를 여러 개 만들고 그 안에 있는 파일을 가져오가나 하위 폴더에 파일을 저장합니다.

path.함수()
  • 경로를 지정하는 join함수:  join 함수를 사용하면 여러 조각으로 나워서 입력한 경로를 연결해 하나로 만듭니다. 
  • dirname 함수로 경로만 추출하기: path 모듈로 가져온 함수에서  경로 마지막 파일명부분만 제외됨
  • bsaename함수로 파일 이름 추출하기 : 경로에서 파일 이름만 추출합니다.
// path 모듈 연습하기 ( 결과 비교 파일 : 03\results\path.js)
const path = require("path");
const ext = path.extname(__filename);
console.log(`파일 확장자: ${ext}`);
console.log(path.basename(__filename,ext));
  • extname 함수로 확장자 추출하기: 확장자를 모르는 상태라면 extname함수를 사용해서 주어진 경로에서 파일의 확장자만 추출할 수 있습니다.
  • parse 함수로 경로를 객체로 반환하기: parse 함수는 경로를 분해해서 정보를 각각 객체로 반환합니다. 루트 폴저를 비롯해서 파일 경로, 파일 이름, 확장자, 확장자를 제외한 파일 이름 등이 포함되어 있습니다.

2. FS 모듈

파일의 내용을 읽거나 기록하는 등 파일을 관리할 때는 노드이 File System 모듈을 사용할 수 있습니다. File System 모듈을 줄여서 흔히 FS모듈이라고 합니다. FS 모듈은 파일과 디렉터리 살펴보기, 새로운 파일과 디렉터리 만들기, 파일 스트리밍 등 파일이나 디렉터리를 사용하면서 필요한 기능을 제공합니다.

 

비동기 처리 방법에 따라 사용하는 함수가 다르다!

FS 모듈에서는 여러 함수가 있는데 프라미스에서 사용하는 함수와 콜백에서 사용하는 함수, 동기 처리를 할 때 사용하는 함수 이렇게 3가지로 구분됩니다. FS 모듈을 설명하는 내용 중에 Synchronous API 항목을 보면 동기 처리를 하는 함수들이 나열되어 있고, Callback API 항목에는 콜백을 사용해 비동기 처리를 하는 함수들이 나열되어 있습니다. 애플리케이션을 작성하기 전에 동기 처리와 비동기 처리 가운데 어떤 것을 사용할 지 결정하고 비동기로 처리할 떄 사용하는 방법이 콜백과 프로미스입니다.

 

FS 모듈 가져오기: fs 모듈을 가져온 후에는 fs.함수명으로 모듈의 함수를 사용합니다

  • 예를 들어 파일을 읽어노는 readFile 함수를 사용하겠다면 const fs = require("fs");로 가져온 후 fs.readFile("example.txt",(err,data) => {...});

 

FS 모듈에는 현재 디렉터리 파일을 살펴볼 수 있는 readdirSync 함수와 readdir 함수가 있습니다. 두 함수의 차이는 파일을 살펴볼 때 동기 처리, 비동기 처리입니다.

  • 동기처리로 디렉터리 읽기: fs. readdirSync(경로[,옵션]) 지정한 경로를 읽어서 그 경로에 있는 파일 이름을 모두 표시합니다. 이때 옵션은 encoding방식을 지정합니다.
const fs = require("fs");

let files = fs.readdirSync("./"); // './'로 지정했으므로 현재 디렉터리이고 옵션을 지정하지 않아서 기본값인 utf8
console.log(files);
  • 비동기 처리로 디렉터리 읽기: fs.readdir(경로[,옵션],콜백) 디렉티리 읽기가끝나면 콜백 함수에서 지저한 일을 수행 

3. 버퍼와 스트림 

버퍼와 스트림은 파일을 읽거나 쓸 때 한 덩어리로 처리하지 않고 작은 단위로 나눠서 시간을 절약하는 방법입니다.

버퍼란

컴퓨터 공학에서 버퍼는 임시 데이터를 저장하는 물리적인 메모리 공간을 가리킵니다. 파일을 읽어 올 때 버퍼 하나 크기 만큼 가져오고, 버퍼가 가득 파면 그 내용을 전달해준다고 생각하면 이해하기 쉽습니다. 노드의 버퍼 크기는 고정되어 있고 이진값으로 저장됩니다.

스트림이란

한 곳에서 다른 곳으로 데이터가 이동하는 것, 즉 데이터의 흐름을 가리킵니다. 백엔드 프로그래밍에서 스티림은 서버에서 클라이언트로 혹은 클라이언트에서 서버로 데이터를 보낼 때 사용하는 방식입니다. 스트림을 사용하면 파일 전체를 내려받지 않고도 차례로 처리할 수 있어서 시간을 절약할 수 있고, 메모리 사용도 최소화할 수 있는 만큼 프로그램의 성능도 향상할 수 있습니다.

버퍼는 데이터를 메모리에 저장하고 직접 다룰 때 사용하고 스트림은 데이터를 효율적으로 읽고 쓸 때 사용하는 개념입니다. 스트림은 버퍼를 사용해서 데이터를 처리하거나 전달하게 되죠.

 

3.1 노드에서의 스트림(리더블 스트림, 라이터블 스트림, 듀플렉스 스트림)

  • 리더블 스트림 (readable stream)

데이터를 읽기 위한 스트림입니다. 네트워크로 연결해서 데이터를 읽어 오거나 파일에서 데이터를 읽어 올 때 사용합니다.

리더블 스트림을 사용하려면 FS 모듈에 있는 createReaadStream 함수를 사용하는데, 이함수는 데이터를 작은 크기로 나눠서 읽어 옵니다.

*데이터를 읽어 오는 작은 단위를 흔히 청크(chunk)라고 합니다.

리버블 스트림이 사용할 수 있는 주요 이벤트
이벤트 설명
data  데이터를 읽을 수 있을 때마다 발생하는 이벤트입니다. 스트림에서 읽은 데이터를 처리할 때 data 이벤트를 사용합니다.
end 스트림에서 데이터를 모두 읽었을 때 발생하는 이벤트입니다. 데이터를 모두 읽었다는 사실을 인지하고 이후 작업이 필요할 댸 사용합니다.
error 스트림에서 오류가 생겼을 때 발생하는 이벤트입니다.
const fs = require("fs");

const readStream = fs.createReadStream("./readMe.txt"); // 파일을 읽어옴

readStream.on("data",(chunk)=>{ // data 이벤트가 발생할 때 마다 버퍼 크기만큼 가져옴
  console.log("new chunk recevied");
  console.log(chunk);
});
readStream.on("end",()=>{
  console.log("finished readinf data");
});
readStream.on("error",(err)=>{
  console.log(`Error reading the files:${err}`);
});

 

  • 라이터블 스트림 (writable stream)

데이터를 쓰기 위한 스트림입니다. 네트워크에 연결한 상태에서 데이터를 기록하거나 파일에 데이터를 기록할 때 사용합니다. 라이터블 스트림을 만들려면 FS 모듈의 createWriteStream 함수를 사용합니다.

const fs = require("fs");

const readStream = fs.createReadStream("./readMe.txt", "utf8");
const writeStream = fs.createWriteStream("./writeMe.txt");

readStream.pipe(writeStream); // 파이프를 사용하면 data 이벤트가 발생했을 때 따로 가져오고 기록하던 것을 한꺼번에 처리할 수 있습니다.

/* pipe함수
fs.readStream.pipe(writeStream) 
리더블 스트림에서 데이터 읽기 -> 
읽은 데이터를 라이터블 스트림으로 기록 ->
라이터블 스트림에 다 기록할 때까지 리더블 스트림에서 읽고 쓰기를 반복->
리더블 스트림에서 더 이상 읽을 데이터가 없거나, 라이터블 스트림에 더 이상 쓸 데이터가 없으면 pipe 함수가 자동 종료
*/
  • 듀플렉스 스트림: 읽기와 쓰기 모두 가능한 스트림입니다. 리더블 스트림과 라이터블 스트림을 결합한 형태라서 실시간 양방향 통신에 사용합니다.

일반적인 백엔드 프로그래밍에서 자주 사용하는 것은 리더블 스트림과 라이터블 스트림입니다.