본문 바로가기

[JS] Node.js 내장 모듈인 fs 모듈로 비동기 실습 - 1/2 (개념)

[JS] Node.js 내장 모듈인 fs 모듈로 비동기 실습 - 1/2 (개념)

브라우저에서 사용할 수 있는 비동기 흐름은 타이머 혹은 DOM 이벤트와 관련된 상황으로 다소 한정적이지만, Node.js의 경우 많은 API가 비동기로 작성되어 있다. Node.js 소개 문서의 첫 단락은 Node.js의 정의부터 시작한다. Node.js는 "비동기 이벤트 기반 JavaScript 런타임"이다.

 

About Node.js®
As an asynchronous event-driven JavaScript runtime, Node.js is designed to build scalable network applications. Many connections can be handled concurrently. Upon each connection, the callback is fired, but if there is no work to be done, Node.js will sleep.
 
Thread-based networking is relatively inefficient and very difficult to use. Furthermore, users of Node.js are free from worries of dead-locking the process, since there are no locks. Almost no function in Node.js directly performs I/O, so the process never blocks except when the I/O is performed using synchronous methods of Node.js standard library. Because nothing blocks, scalable systems are very reasonable to develop in Node.js.

 

* 같이 읽으면 좋을 자료 : 비동기와 동기, 블로킹과 논블로킹

 

👩‍💻 동기 & 비동기 / 블로킹 & 논블로킹 💯 완벽 이해하기

동기 & 비동기 vs 블로킹 & 논블록킹 개념 이 개념들을 처음 접하거나 컴퓨터 공학에 대해 잘 모르는 사람은 이 개념들이 서로 뭔가 연관이 있는 것으로 오해하기 쉽다. 아무래도 동기와 블록킹,

inpa.tistory.com


 

Node.js 내장 모듈을 사용하는 방법

건축으로부터 비롯된 모듈이라는 단어는, 어떤 기능을 조립할 수 있는 형태로 만든 부분이다. 그 중 fs(File System) 모듈은, PC의 파일을 읽거나 저장하는 등의 일을 할 수 있게 도와준다.

 

* Node.js 내장 모듈 목록은 다음 링크에서 찾을 수 있다. : Node.js v16.14.2 Documentation

 

 

Node.js 내장 모듈 (Node.js built-in modules)에는 다양한 모듈이 존재한다. 개발자는 자신이 이해하는 범위만큼 모듈을 사용할 수 있다. 예를 들어, DNS에 대한 지식을 알고 있다면, DNS 모듈 사용법 문서에서 관련 메서드를 사용할 수 있다. (DNS는 파일 시스템 모듈이 파일을 읽거나 저장하는 기능을 구현할 수 있도록 돕는다)

 

파일을 읽거나 저장하는 기능 등을 제공하는 Node.js 내장 모듈은 File System 모듈이다. 파일과 관련하여 파일을 읽을 때에는 readFile, 파일을 저장할 때에는 writeFile을 쓰면 된다.  

 

모듈 불러오기 

모든 모듈은 '모듈을 사용하기 위해 불러오는 과정'이 필요하다. 브라우저에서 다른 파일을 불러올 때에는 다음과 같이 <script> 태그를 이용했다.

<script src="불러오고싶은_스크립트.js"></script>

 

Node.js 에서는 JavaScript 코드 가장 상단에 require 구문을 이용하여 다른 파일을 불러온다.

const fs = require('fs'); // 파일 시스템 모듈을 불러온다.
const dns = require('dns'); // DNS 모듈을 불러온다.

// 이제 fs.readFile 메서드 등을 사용할 수 있다!

 

3rd-party 모듈을 사용하는 방법

서드 파티 모듈(3rd-party module)은 해당 프로그래밍 언어에서 공식적으로 제공하는 빌트인 모듈(built-in module)이 아닌 모든 외부 모듈을 일컫는다. 예를 들어, Node.js에서 underscore는 Node.js 공식 문서에 없는 모듈이기 때문에 서드 파티 모듈이다. underscore 와 같은 서드 파티 모듈을 다운로드하기 위해서는 npm을 사용한다. 터미널에서 다음과 같이 입력해 underscore 를 설치할 수 있다.

npm install underscore

 

이제 node_modules에 underscore가 설치되었다. 이제 Node.js 내장 모듈을 사용하듯 require 구문을 통해 underscore 를 사용할 수 있다.

const _ = require('underscore');

 

 


fs.readFile을 통해 알아보는 Node.js 공식문서 가이드

일단 공식문서를 읽고 시작한다!!!! 

 

fs.readFile(path[, options], callback)#
 

Asynchronously reads the entire contents of a file.

import { readFile } from 'node:fs';

readFile('/etc/passwd', (err, data) => {    // URL 사용
  if (err) throw err;
  console.log(data);
});

 

The callback is passed two arguments (err, data), where data is the contents of the file.

If no encoding is specified, then the raw buffer is returned. (따라서 UTF-8 로 인코딩해줘야 한다.)

If options is a string, then it specifies the encoding:

import { readFile } from 'node:fs';

readFile('/etc/passwd', 'utf8', callback);

 

When the path is a directory, the behavior of fs.readFile() and fs.readFileSync() is platform-specific. On macOS, Linux, and Windows, an error will be returned. On FreeBSD, a representation of the directory's contents will be returned.

import { readFile } from 'node:fs';

// macOS, Linux, and Windows
readFile('<directory>', (err, data) => {
  // => [Error: EISDIR: illegal operation on a directory, read <directory>]
});

//  FreeBSD
readFile('<directory>', (err, data) => {
  // => null, <data>
});

 

It is possible to abort an ongoing request using an AbortSignal. If a request is aborted the callback is called with an AbortError:

import { readFile } from 'node:fs';

const controller = new AbortController();
const signal = controller.signal;
readFile(fileInfo[0].name, { signal }, (err, buf) => {
  // ...
});
// When you want to abort the request
controller.abort();

 

The fs.readFile() function buffers the entire file. To minimize memory costs, when possible prefer streaming via fs.createReadStream().

Aborting an ongoing request does not abort individual operating system requests but rather the internal buffering fs.readFile performs.

 

File descriptors#

  1. Any specified file descriptor has to support reading.
  2. If a file descriptor is specified as the path, it will not be closed automatically.
  3. The reading will begin at the current position. For example, if the file already had 'Hello World' and six bytes are read with the file descriptor, the call to fs.readFile() with the same file descriptor, would give 'World', rather than 'Hello World'.

Performance Considerations#

The fs.readFile() method asynchronously reads the contents of a file into memory one chunk at a time, allowing the event loop to turn between each chunk. This allows the read operation to have less impact on other activity that may be using the underlying libuv thread pool but means that it will take longer to read a complete file into memory. The additional read overhead can vary broadly on different systems and depends on the type of file being read. If the file type is not a regular file (a pipe for instance) and Node.js is unable to determine an actual file size, each read operation will load on 64 KiB of data. For regular files, each read will process 512 KiB of data.

For applications that require as-fast-as-possible reading of file contents, it is better to use fs.read() directly and for application code to manage reading the full contents of the file itself.

 

* The Node.js GitHub issue #25741 provides more information and a detailed analysis on the performance of fs.readFile() for multiple file sizes in different Node.js versions.

 


 

fs모듈 readFile 공식 문서 설명

 

Node.js로 파일을 읽고 저장할 수 있는 fs 모듈로 기본이 되는 파일 읽기 및 쓰기를 알아보자. 메서드 fs.readFile 은 로컬에 존재하는 파일을 읽어온다. 16.x 버전 기준으로, fs.readFile의 공식 API 문서에 안내되어 있는 항목을 설명한다.   

fs.readFile(path[, options], callback)

메서드 fs.readFile 은 비동기적으로 파일 내용 전체를 읽는다. 이 메서드를 실행할 때에는 전달인자 세 개를 받는다. (Asynchronously reads the entire contents of a file.)

  • path \<string> | \<Buffer> | \<URL> | \<integer>

path에는 파일 이름을 전달인자로 받는다. 네 가지 종류의 타입을 넘길 수 있지만 일반적으로 문자열(<string>)의 타입을 받는다. (filename or file descriptor)

 

다음은 `/etc/passwd' 라는 파일을 불러오는 예시이다.

fs.readFile('/etc/passwd', ..., ...)

 

  • options \<Object> | \<string>

대괄호로 감싼 두 번째 전달인자 options는 넣을 수도 있고, 넣지 않을 수도 있다. (대괄호는 선택적 전달인자를 의미) options는 문자열 또는 객체 형태로 받을 수 있다. 문자열로 전달할 경우 인코딩을 받는다.  (If options is a string, then it specifies the encoding:

 

// **두 번째 전달인자 options에 문자열을 전달한 경우
// /etc/passwd 파일을 'utf8'을 사용하여 읽는다.
fs.readFile('/etc/passwd', 'utf8', ...);
// ** 두 번째 전달인자 options에 객체를 전달한 경우
let options = {
  encoding: 'utf8',    // utf8 인코딩 방식으로 연다
  flag: 'r'            // 읽기 위해 연다
}

// /etc/passwd 파일을 options를 사용하여 읽는다.
fs.readFile('/etc/passwd', options, ...)

 

  • callback \<Function>
    • err \<Error> | \<AggregateError>
    • data \<string> | \<Buffer>

콜백 함수를 전달한다. 파일을 읽고 난 후에 비동기적으로 실행되는 함수이다. 콜백 함수에는 두 가지 매개변수가 존재한다. 에러가 발생하지 않으면 err 는 null 이 되며, data 에 문자열이나 Buffer 라는 객체가 전달된다. data 는 파일의 내용이다.

* 인코딩을 하면 문자열, 하지 않으면 Buffer 가 전달된다.  (If no encoding is specified, then the raw bufferis returned.)

* Buffer은 그냥 메모리 뭉치로만 생각하면 된다. (우리가 읽을 수 없는 문자 -> 인코딩이 필요함)


 

직접 해보기

 

JavaScript 파일이 실행되는 디렉토리에, 적당한 텍스트 파일(test.txt)을 새롭게 생성한 후 실행해보자.

//** 예제코드

import { readFile } from 'node:fs';


fs.readFile('test.txt', 'utf8', (err, data) => {
  if (err) {
    throw err; // 에러를 던집니다.
  }
  console.log(data);
});

 

Quokka로 js 파일을 만들어서 현재 폴더 안에 있는 README.md 파일을 읽어보았다. 출력이 잘 되는 것을 볼 수 있다.

오른 쪽 위 (README.md)의 내용이 아래 터미널에 그대로 나온다. (utf-8, UTF-8, Utf8 다 됨)

'utf8'로 인코딩을 안 하면 raw Buffer 이 전달된다.

728x90
⬆︎