상세 컨텐츠

본문 제목

[노드 1팀] 3장. 노드 기능 알아보기 (2)

24-25/Node.js 1

by gooroominuna 2024. 11. 8. 10:00

본문

728x90

3.4. 노드 내장 객체 알아보기

GLOBAL

  • 전역 객체로 모든 파일에서 접근 가능하다.
module.exports = () => global.message;
const A = require("./globalA");

globalThis.message = "hello";
console.log(A());

console

  • 디버깅을 위해 사용한다.
  • 개발 중 변수에 값이 잘 들어있는지 확인하거나 에러 내용을 표시하기 위해, 또 코드 실행 시간을 알기 위해 사용한다. 
  • 대표적으로 console.log()
const string = "abc";
const number = 1;
const boolean = true;
const obj = {
  outside: {
    inside: {
      key: "value",
    },
  },
};

console.time("전체 시간"); //console.timeEnd(레이블) 과 대응되어 같은 레이블을 가진 time과 timeEnd 사이의 시간 측정한다.
console.log("평범한 로그입니다 쉼표로 구분해 여러 값을 찍을 수 있습니다"); //평범한 로그를 콘솔에 표시한다.
console.log(string, number, boolean); //여러 내용을 동시에 표시할 수 있다.
console.error("에러 메시지는 console.error에 담아주세요"); //에러를 콘솔에 표시한다.

console.table([
  //배열의 요소로 객체 리터럴을 넣으면, 객체의 속성들이 테이블 형식으로 표현된다.
  { name: "제로", birth: 1994 },
  { name: "hero", birth: 1998 },
]);

console.dir(obj, { colors: false, depth: 2 });
console.dir(obj, { colors: true, depth: 1 });
//객체를 콘솔에 표시할 때 사용한다. colors 를 true로 하면 색이 추가되고, depth 는 객체를 몇 단계까지 보여줄 지 결정한다. (기본값 : 2)

console.time("시간 측정");

for (let i = 0; i < 100000; i++) {}
console.timeEnd("시간 측정");

function b() {
  console.trace("에러 위치 추적"); //에러가 어디서 발생했는지 추적한다.
}
function a() {
  b();
}
a();

console.timeEnd("전체 시간");

timer

  • 타이머 기능을 제공한다.
const timeout = setTimeout(() => { //주어진 밀리초(1000분의 1초) 이후에 콜백 함수를 실행한다.
  console.log("1.5초 후 실행");
}, 1500);

const interval = setInterval(() => { //주어진 밀리초마다 콜백 함수를 반복 실행한다.
  console.log("1초마다 실행");
}, 1000);

const timeout2 = setTimeout(() => {
  console.log("실행되지 않습니다");
}, 3000);

setTimeout(() => {
  clearTimeout(timeout2); //setTimeout을 취소한다.
  clearInterval(interval); //setInterval을 취소한다.
}, 2500);

const immediate = setImmediate(() => { //콜백 함수를 즉시 실행한다.
  console.log("즉시 실행"); 
});

const immediate2 = setImmediate(() => {
  console.log("실행되지 않습니다");
});

clearImmediate(immediate2); // setImmediate을 취소한다.

process

  • 현재 실행되고 있는 노드 프로세스에 대한 정보를 담고 있다.
  • process.version : 설치된 노드 버전이다.
  • process.arch : 프로세서 아키텍처 정보이다.
  • process.platform : 운영체제 플랫폼 정보이다.
  • process.pid : 현재 프로세스의 아이디이다.
  • process.uptime() : 프로세스가 시작된 후 흐른 시간이다. 단위는 초이다.
  • process.execPath : 노드의 경로이다.
  • process.cwd() : 현재 프로세스가 실행되는 위치이다.
  • process.cpuUsage() : 현재 cpu 사용량이다.
  • process.env 
    • 시스템의 환경 변수를 나타낸다.
    • NODE_OPTIONS : 노드를 실행할 때의 옵션들을 입력받는 환경 변수이다.
    • UV_THREADPOOL_SIZE : 노드에서 기본적으로 사용하는 스레드 풀의 스레드 개수를 조절할 수 있게 한다. 
    • 서비스의 중요한 키를 저장하는 공간으로도 사용된다.
    • 중요한 비밀번호는 process.env.SECRET_ID(SECRET_CODE) 로 대체한다.
  • process.nextTick(콜백)
    • 이벤트 루프가 다른 콜백 함수들보다 우선으로 처리하도록 만든다.
    • setImmediate나 setTimeout보다 먼저 실행된다.
    • resolve된 Promise도 nextTick처럼 다른 콜백들보다 우선시 된다.
    • process.nextTick과 Promise를 마이크로태스크라고 따로 구분해서 부른다.
  • process.exit(코드)
    • 실행 중인 노드 프로세스를 종료한다.
    • 서버 환경에서 이 함수를 사용하면 서버가 멈춰 특수한 경우를 제외하고는 서버에서 잘 사용하지 않는다.
    • 인수를 코드 번호를 줄 수 있는데 인수를 주지 않거나 0을 주면 정상 종료, 1을 주면 비정상 종료를 뜻한다.
    • 에러가 발생해서 종료하는 경우에는 1을 넣으면 된다.

기타 내장 객체

  • URL, URLSearchParams
  • AbortController, FormData, fetch, Headers, Request, Response, Event, EventTarget : 브라우저에서 사용하던 API가 노드에도 동일하게 생성되었다.
  • TextDecoder : Buffer를 문자열로 바꾼다.
  • TextEncoder : 문자열을 Buffer로 바꾼다.
  • WebAssembly : 웹어셈블리 처리를 담당한다. 

3.5. 노드 내장 모듈 사용하기

os

  • 노드는 os 모듈에 정보가 담겨있어 정보를 가져올 수 있다.
  • require('os') 또는 require('node: os')를 하면 불러올 수 있다.
  • os라는 파일이 존재하진 않지만 노드가 알아서 내장 모듈임을 파악하여 불러온다.
const os = require("os");

console.log("운영체제 정보 --------------------------------------");
console.log("os.arch(): ", os.arch()); //process.arch와 동일하다. 프로세서 아키텍처 정보이다.
console.log("os.platform(): ", os.platform()); //process.platform과 동일하다. 운영체제 플랫폼 정보이다.
console.log("os.type(): ", os.type()); //운영체제의 종류를 보여준다.
console.log("os.uptime(): ", os.uptime()); //운영체제 부팅 이후 흐른 시간(초)를 보여준다.
console.log("os.hostname(): ", os.hostname()); //컴퓨터의 이름을 보여준다.
console.log("os.release(): ", os.release()); //운영체제의 버전을 보여준다.

console.log("경로----------------------------------------------");
console.log("os.homedir(): ", os.homedir()); //홈 디렉터리 경로를 보여준다.
console.log("os.tmpdir(): ", os.tmpdir()); //임시 파일 저장 경로를 보여준다.

console.log("cpu 정보-------------------------------------------");
console.log("os.cpus(): ", os.cpus()); //컴퓨터의 코어 정보를 보여준다.
console.log("os.cpus().length: ", os.cpus().length); //사용 가능한 메모리(RAM)를 보여준다.

console.log("메모리 정보------------------------------------------");
console.log("os.freemem(): ", os.freemem()); //전체 메모리 용량을 보여준다.
console.log("os.totalmem(): ", os.totalmem()); //각종 에러와 신호에 대한 정보가 담겨있고, 에러 코드를 함께 보여줘서 발생할 때마다 검색하는 것이 좋다.

path

  • 폴더와 파일의 경로를 쉽게 조작하도록 도와주는 모듈이다.
const path = require("path");

const string = __filename;

console.log("path.sep:", path.sep); //경로의 구분자이다.
console.log("path.delimiter:", path.delimiter); //환경 변수의 구분자이다. process.enc.PATH를 입력하면 여러 개의 경로가 구분되어있다.
console.log("------------------------------");
console.log("path.dirname():", path.dirname(string)); //파일이 위치한 폴더 경로를 보여준다.
console.log("path.extname():", path.extname(string)); //파일의 확장자를 보여준다.
console.log("path.basename():", path.basename(string)); //파일의 이름(확장자 포함)을 표시한다. 
console.log("path.basename - extname:", path.basename(string, path.extname(string)));
console.log("------------------------------");
console.log("path.parse()", path.parse(string)); // 파일 경로를 root, dir, base, ext, name으로 분리한다.
console.log(
  "path.format():",
  path.format({ //path.parse()한 객체를 파일 경로로 합친다.
    dir: "C:\\users\\zerocho",
    name: "path",
    ext: ".js",
  })
);

console.log("path.normalize():", path.normalize("C://users\\\\zerocho\\path.js")); ///나 \를 실수로 여러 번 사용했거나 혼용했을 때 정상적인 경로로 변환한다.
console.log("------------------------------");
console.log("path.isAbsolute(C:\\):", path.isAbsolute("C:\\")); //파일의 경로가 절대경로인지 상대경로인지를 true나 false로 알린다.
console.log("path.isAbsolute(./home):", path.isAbsolute("./home"));
console.log("------------------------------");
console.log("path.relative():", path.relative("C:\\users\\zerocho\\path.js", "C:\\")); //경로를 두 개 넣으면 첫 번째 경로에서 두번째 경로로 가는 방법을알린다.
console.log("path.join():", path.join(__dirname, "..", "..", "/users", ".", "/zerocho")); // 여러 인수를 넣으면 하나의 경로로 합친다.
console.log("path.resolve():", path.resolve(__dirname, "..", "users", ".", "/zerocho")); //path.join()과 비슷하다. 동작방식이 다르다.

url

  • 인터넷 주소를 쉽게 조작하도록 도와주는 모듈이다.

WHATWG 방식의 url

const url = require("url");

const { URL } = url;
const myURL = new URL("http://www.github.co.kr/book/bookList.aspx?sercate1=001001000#anchor");
console.log("new URL(): ", myURL);
console.log("url.format(): ", url.format(myURL)); //분해되었던 url 객체를 다시 원래 상태로 조립한다.
const myURL = new URL("http://www.gilbut.co.kr/?page=3&limit=10&category=nodejs&category=javascript");

console.log("searchParams:", myURL.searchParams);
console.log("searchParams.getAll(): ", myURL.searchParams.getAll("category")); //키에 해당하는 모든 값을 가져온다.
console.log("searchParams.get(): ", myURL.searchParams.get("limit")); //키에 해당하는 첫 번째 값만 가져온다.
console.log("searchParams.has(): ", myURL.searchParams.has("page")); //해당 키가 있는지 없는지 검사한다.

console.log("searchParams.keys(): ", myURL.searchParams.keys()); //searchParams의 모든 키를 반복기 객체로 가져온다.
console.log("searchParams.values(): ", myURL.searchParams.values()); //searchParams의 모든 값을 반복기 객체로 가져온다.

myURL.searchParams.append("filter", "es3"); //해당 키를 추가한다. 같은 키의 값이 있다면 유지하고 하나 더 추가한다.
myURL.searchParams.append("filter", "es5");
console.log(myURL.searchParams.getAll("filter"));

myURL.searchParams.set("filter", "es6"); //append와 비슷하지만 같은 키의 값들을 모두 지우고 새로 추가한다.
console.log(myURL.searchParams.getAll("filter"));

myURL.searchParams.delete("filter"); //해당 키를 제거한다.
console.log(myURL.searchParams.getAll("filter"));

console.log("searchParams.toString(): ", myURL.searchParams.toString()); //조작한 searchParams 객체를 다시 문자열로 만든다.
myURL.search = myURL.searchParams.toString();

dns

  • DNS를 다룰 때 사용하는 모듈
  • 주로 도메인을 통해 IP나 기타 DNS정보를 얻고자 할 때 사용한다.
import dns from "dns/promises";

const ip = await dns.lookup("gilbut.co.kr"); //ip 주소 얻기
console.log("IP", ip);

const a = await dns.resolve("gilbut.co.kr", "A"); //ipv4 주소
console.log("A", a);
// AAAA: ipv6주소, NS: 네임서버, SOA: 도메인 정보

const mx = await dns.resolve("gilbut.co.kr", "MX");  //메일 서버
console.log("MX", mx);

const cname = await dns.resolve("www.gilbut.co.kr", "CNAME"); //별칭, www가 붙은 주소
console.log("CNAME", cname);

const any = await dns.resolve("gilbut.co.kr", "ANY");
console.log("ANY", any);

crypto

  • 다양한 방식의 암호화를 도와주는 모듈
  • 단방향 암호화
    • 복호화할 수 없는 암호화 방식 (암호화된 문자열을 원래 문자열로 되돌려놓는 것을 복호화라 한다.)
    • 한 번 암호화하면 원래 문자열을 찾을 수 없고 해시 함수라고 부르기도 한다.
    • 해시 기법 : 어떠한 문자열을 고정된 길이의 다른 문자열로 바꿔버리는 방식
const crypto = require("crypto");

console.log("base64: ", crypto.createHash("sha512").update("비밀번호").digest("base64"));
console.log("hex: ", crypto.createHash("sha512").update("비밀번호").digest("hex"));
console.log("base64: ", crypto.createHash("sha512").update("다른 비밀번호").digest("base64"));
  • createHash(알고리즘) : 사용할 해시 알고리즘을 넣는다.
  • update(문자열) : 변환할 문자열을 넣는다.
  • digest(인코딩) : 인코딩할 알고리즘을 넣는다. 
const cryto = require("crypto");

crypto.randomBytes(64, (err, buf) => {
  const salt = buf.toString("base64");
  console.log("salt: ", salt);
  crypto.pbkdf2("비밀번호", salt, 100000, 64, "sha512", (err, key) => {
    console.log("password: ", key.toString("base64"));
  });
});
  • pbkdf2 : 기존 문자열에 salt라고 불리는 문자열을 붙인 후 해시 알고리즘을 반복해서 적용하는 것이다.
  • 양방향 암호화
    • 암호화된 문자열을 복호화할 수 있으며, 키를 사용한다.
const crypto = require("crypto");

const algorithm = "aes-256-cbc";
const key = "abcdefghijklmnopqrstuvwxyz123456";
const iv = "1234567890123456";

const cipher = crypto.createCipheriv(algorithm, key, iv); // 알고리즘에 따라 바이트수가 다름 이 경우 키는 32바이트, iv는 16바이트
let result = cipher.update("암호화할 문장", "utf8", "base64"); // 암호화할 대상과 대상의 인코딩, 출력 결과물의 인코딩을 넣는다.
result += cipher.final("base64");// 출력 결과물의 인코딩을 넣으면 암호화가 완료된다.
console.log("암호화: ", result);

const decipher = crypto.createDecipheriv(algorithm, key, iv); // 복호화할 때 사용한다. 암호화할 때 사용한 알코리즘과 키, iv를 그대로 넣어야 한다.
let result2 = decipher.update(result, "base64", "utf8"); // 암호화된 문장, 그 문장의 인코딩, 복호화할 인코딩을 넣는다.
result2 += decipher.final("utf8");// 복호화 결과물의 인코딩을 넣는다.
console.log("복호화: ", result2);

util

  • 각종 편의 기능을 모아둔 모듈
const util = require("util");
const crypto = require("crypto");

const dontUseMe = util.deprecate((x, y) => { //함수가 deprecated(없어짐) 처리 되었음을 알린다.
  console.log(x + y);
}, "dontUseMe 함수는 deprecated되었으니 더 이상 사용하지 마세요!");
dontUseMe(1, 2);
//첫 번째 인수로 넣은 함수를 사용했을 때 경고 메시지가 출력된다.
//두 번째 인수로 경고 메시지 내용을 넣으면 된다.

const randomBytesPromise = util.promisify(crypto.randomBytes); //콜백 패턴을 프로미스 패턴으로 바꾼다.
randomBytesPromise(64)
  .then((buf) => {
    console.log(buf.toString("base64"));
  })
  .catch((error) => {
    console.error(error);
  });

worker_threads

  • 멀티 스레드 방식으로 작업하는 방법
const { Worker, isMainThread, parentPort } = require("worker_threads");

if (isMainThread) {
  //부모일 때
  const worker = new Worker(__filename);
  worker.on("message", (message) => console.log("from worker", message));
  worker.on("exit", () => console.log("worker exit"));
  worker.postMessage("ping");
} else {
  //워커일 때
  parentPort.on("message", (value) => {
    console.log("from parent", value);
    parentPort.postMessage("pong");
    parentPort.close();
  });
}
const { Worker, isMainThread, parentPort, workerData } = require("worker_threads");

if (isMainThread) {
  //부모일 때
  const threads = new Set();
  threads.add(
    new Worker(__filename, {
      workerData: { start: 1 },
    })
  );
  threads.add(
    new Worker(__filename, {
      workerData: { start: 2 },
    })
  );
  for (let worker of threads) {
    worker.on("message", (message) => console.log("from worker", message));
    worker.on("exit", () => {
      threads.delete(worker);
      if (threads.size === 0) {
        console.log("job done");
      }
    });
  }
} else {
  // 워커일 때
  const data = workerData;
  parentPort.postMessage(data.start + 100);
}
const min = 2;
const max = 10000000;
const primes = [];

function findPrimes(start, end) {
  let isPrime = true;
  for (let i = start; i <= end; i++) {
    for (let j = min; j < Math.sqrt(end); j++) {
      if (i !== j && i % j === 0) {
        isPrime = false;
        break;
      }
    }
    if (isPrime) {
      primes.push(i);
    }
    isPrime = true;
  }
}

console.time("prime");
findPrimes(min, max);
console.timeEnd("prime");
console.log(primes.length);
const { Worker, isMainThread, parentPort, workerData } = require("worker_threads");

const min = 2;
let primes = [];

function findPrimes(start, end) {
  let isPrime = true;
  for (let i = start; i <= end; i++) {
    for (let j = min; j < Math.sqrt(end); j++) {
      if (i !== j && i % j === 0) {
        isPrime = false;
        break;
      }
    }
    if (isPrime) {
      primes.push(i);
    }
    isPrime = true;
  }
}

if (isMainThread) {
  const max = 10000000;
  const threadCount = 8;
  const threads = new Set();
  const range = Math.floor((max - min) / threadCount);
  let start = min;
  console.time("prime");
  for (let i = 0; i < threadCount - 1; i++) {
    const end = start + range - 1;
    threads.add(new Worker(__filename, { workerData: { start, range: end } }));
    start += range;
  }
  threads.add(new Worker(__filename, { workerData: { start, range: max } }));
  for (let worker of threads) {
    worker.on("error", (err) => {
      throw err;
    });
    worker.on("exit", () => {
      threads.delete(worker);
      if (threads.size === 0) {
        console.timeEnd("prime");
        console.log(primes.length);
      }
    });
    worker.on("message", (msg) => {
      primes = primes.concat(msg);
    });
  }
} else {
  findPrimes(workerData.start, workerData.range);
  parentPort.postMessage(primes);
}

child_process

  • 다른 프로그램을 실행하고 싶거나 명령어를 수행하고 싶을 때 사용하는 모듈
const exec = require("child_process").exec;

const process = exec("ls");

process.stdout.on("data", function (data) {
  console.log(data.toString());
});

process.stderr.on("data", function (data) {
  console.error(data.toString());
});
const spawn = require("child_process").spawn;

const process = spawn("python", ["test.py"]);

process.stdout.on("data", function (data) {
  console.log(data.toString());
});

process.stderr.on("data", function (data) {
  console.error(data.toString());
});

3.6. 파일 시스템 접근하기

const fs = require("fs");

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

fs.readFile("./readme.txt")
  .then((data) => {
    console.log(data);
    console.log(data.toString());
  })
  .catch((err) => {
    console.error(err);
  });
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());
  });
});

동기 메서드와 비동기 메서드

//비동기 메서드
const fs = require("fs");

console.log("시작");
fs.readFile("./readme2.txt", (err, data) => {
  if (err) {
    throw err;
  }
  console.log("1번", data.toString());
});
fs.readFile("./readme2.txt", (err, data) => {
  if (err) {
    throw err;
  }
  console.log("2번", data.toString());
});
fs.readFile("./readme2.txt", (err, data) => {
  if (err) {
    throw err;
  }
  console.log("3번", data.toString());
});
console.log("끝");
  • 읽으라고 요청하고 다음 작업으로 넘어감 -> 순서대로 읽히지 않음
//동기 메서드
const fs = require("fs");

console.log("시작");
let data = fs.readFileSync("./readme2.txt");
console.log("1번", data.toString());
data = fs.readFileSync("./readme2.txt");
console.log("2번", data.toString());
data = fs.readFileSync("./readme2.txt");
console.log("3번", data.toString());
console.log("끝");
  • 이전 작업이 완료되어야 다음 작업으로 넘어감 -> 순서대로 읽힘
  • 백그라운드가 작업하는 동안 메인스레드가 일을 하지 않고 노는 시간이 생겨 비효율적
//비동기 방식으로 순서대로 나오게 하기
const fs = require("fs");

console.log("시작");
fs.readFile("./readme2.txt", (err, data) => {
  if (err) {
    throw err;
  }
  console.log("1번", data.toString());
  fs.readFile("./readme2.txt", (err, data) => {
    if (err) {
      throw err;
    }
    console.log("2번", data.toString());
    fs.readFile("./readme2.txt", (err, data) => {
      if (err) {
        throw err;
      }
      console.log("3번", data.toString());
      console.log("끝");
    });
  });
});
const fs = require("fs").promises;

console.log("시작");
fs.readFile("./readme2.txt")
  .then((data) => {
    console.log("1번", data.toString());
    return fs.readFile("./readme2.txt");
  })
  .then((data) => {
    console.log("2번", data.toString());
    return fs.readFile("./readme2.txt");
  })
  .then((data) => {
    console.log("3번", data.toString());
    return fs.readFile("./readme2.txt");
  })
  .catch((err) => {
    console.error(err);
  });

버퍼와 스트림 이해하기

  • 버퍼 : 메모리에 저장된 데이터
const buffer = Buffer.from("저를 버퍼로 바꿔보세요");
console.log("from(): ", buffer); //문자열을 버퍼로 바꾼다.
console.log("length: ", buffer.length); //버퍼의 크기를 알린다. (바이트 단위))
console.log("toString(): ", buffer.toString());

const array = [Buffer.from("띄엄 "), Buffer.from("띄엄 "), Buffer.from("띄어쓰기")];
const buffer2 = Buffer.concat(array); // 배열 안에 든 버퍼들을 하나로 합친다.
console.log("concat(): ", buffer2.toString());

const buffer3 = Buffer.alloc(5);
console.log("alloc(): ", buffer3); // 빈 버퍼를 생성한다.
  • 스트림 : 버퍼의 크기를 작게 만들고 여러 번에 걸쳐 나눠 보내는 방식
const fs = require("fs");

const readStream = fs.createReadStream("./readme3.txt", { highWaterMark: 16 }); //읽기 스트림 만들기
//highWaterMark : 버퍼의 크기(바이트 단위)를 정할 수 있는 옵션
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);
});
const fs = require("fs");

const writeStream = fs.createWriteStream("./writeme2.txt");
writeStream.on("finish", () => {
  console.log("파일 쓰기 완료");
});

writeStream.write("이 글을 씁니다.\n");
writeStream.write("한 번 더 씁니다.");
writeStream.end();
const fs = require("fs");

const readStream = fs.createReadStream("readme4.txt");
const writeStream = fs.createWriteStream("writeme3.txt");
readStream.pipe(writeStream);
  • 파이핑 : 스트림끼리 연결하는 것
const zlib = require("zlib");
const fs = require("fs");

const readStream = fs.createReadStream("./readme4.txt");
const zlibStream = zlib.createGzip();
const writeStream = fs.createWriteStream("./readme4.txt.gz");
readStream.pipe(zlibStream).pipe(writeStream);
//파일을 압축하는 모듈
import { pipeline } from "stream/promises";
import zlib from "zlib";
import fs from "fs";

await pipeline(fs.createReadStream("./readme4.txt"), zlib.createGzip(), fs.createWriteStream("./readme4.txt.gz"));
import { pipeline } from "stream/promises";
import zlib from "zlib";
import fs from "fs";

const ac = new AbortController();
const signal = ac.signal;

setTimeout(() => ac.abort(), 1);
await pipeline(fs.createReadStream("./readme4.txt"), zlib.createGzip(), fs.createWriteStream("./readme4.txt.gz"), { signal });
  • pipeline을 사용하여 여러 개의 파이프를 연결한다. 
  • 중간에 AbortController를 사용하고 마지막에 signal을 추가하면 원하는 시점에 중단 가능하다.
const fs = require("fs");
const file = fs.createWriteStream("./big.txt");

for (let i = 0; i <= 10000000; i++) {
  file.write("안녕하세요. 엄청나게 큰 파일을 만들어 볼 것입니다. 각오 단단히 하세요!\n");
}
file.end();
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);
const fs = require("fs");

console.log("before: ", process.memoryUsage().rss);

const readStream = fs.createReadStream("./big.txt");
const writeStream = fs.createWriteStream("./big3.txt");
readStream.pipe(writeStream);
readStream.on("end", () => {
  console.log("stream: ", process.memoryUsage().rss);
});

기타 fs 메서드 알아보기

const fs = require("fs").promises;
const constants = require("fs").constants;

fs.access("./folder", constants.F_OK | constants.W_OK | constants.R_OK) //폴더나 파일에 접근할 수 있는지 확인함
  // F_OK : 파일 존재 여부, W_OK : 쓰기 권한 여부, R_OK: 읽기 권한 여부 / 파일 없을 때 에러코드 : ENOENT
  .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"); // 파일의 아이디(fd 변수)를 가져오는 메서드
  })
  .then((fd) => {
    console.log("빈 파일 만들기 성공", fd);
    return fs.rename("./folder/file.js", "./folder/newfile.js"); //파일 이름 바꾸는 메서드
  })
  .then(() => {
    console.log("이름 바꾸기 성공");
  })
  .catch((err) => {
    console.error(err);
  });
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);
  });
const fs = require("fs");

fs.watch("./target.txt", (eventType, filename) => {
  console.log(eventType, filename);
});

스레드 풀 알아보기

const crypto = require("crypto");

const pass = "pass";
const salt = "salt";
const start = Date.now();

crypto.pbkdf2(pass, salt, 1000000, 128, "sha512", () => {
  console.log("1: ", Date.now() - start);
});

crypto.pbkdf2(pass, salt, 1000000, 128, "sha512", () => {
  console.log("2: ", Date.now() - start);
});

crypto.pbkdf2(pass, salt, 1000000, 128, "sha512", () => {
  console.log("3: ", Date.now() - start);
});

crypto.pbkdf2(pass, salt, 1000000, 128, "sha512", () => {
  console.log("4: ", Date.now() - start);
});

crypto.pbkdf2(pass, salt, 1000000, 128, "sha512", () => {
  console.log("5: ", Date.now() - start);
});

crypto.pbkdf2(pass, salt, 1000000, 128, "sha512", () => {
  console.log("6: ", Date.now() - start);
});

crypto.pbkdf2(pass, salt, 1000000, 128, "sha512", () => {
  console.log("7: ", Date.now() - start);
});

crypto.pbkdf2(pass, salt, 1000000, 128, "sha512", () => {
  console.log("8: ", Date.now() - start);
});
  • 기본적인 스레드 풀의 개수가 4개라서 4개씩 처리됨

3.7. 이벤트 이해하기

const EventEmitter = require("events");

const myEvent = new EventEmitter();
myEvent.addListener("event1", () => {
  // on과 기능이 같다.
  console.log("이벤트 1");
});
myEvent.on("event2", () => {
  //이벤트 이름과 콜백을 연결한다.
  console.log("이벤트 2");
});
myEvent.on("event2", () => {
  console.log("이벤트 2 추가");
});
myEvent.on("event3", () => {
  console.log("이벤트 3");
});

myEvent.emit("event1"); //한 번만 실행되는 이벤트
myEvent.emit("event2");

myEvent.emit("event3");
myEvent.emit("event3"); //한 번만 실행됨

myEvent.on("event4", () => {
  console.log("이벤트 4");
});
myEvent.removeAllListeners("event4"); // 연결된 모든 이벤트 리스너를 제거함
myEvent.emit("event4");

const listener = () => {
  console.log("이벤트 5");
};
myEvent.on("event5", listener);
myEvent.removeListener("event5", listener); // 이벤트에 연결된 리스너를 하나씩 제거함. off도 같음
myEvent.emit("event5");

console.log(myEvent.listenerCount("event2")); //현재 리스너가 몇 개 연결되어 있는지 확인

3.8. 예외 처리하기

setInterval(() => {
  //프로세스가 멈추는지 여부를 체크하기 위해 사용
  console.log("시작");
  try {
    throw new Error("서버를 고장내주마!");
  } catch (err) {
    console.error(err);
  }
}, 1000);
const fs = require("fs");

setInterval(() => {
  fs.unlink(" /abcdefg.js", (err) => {
    if (err) {
      console.error(err);
    }
  });
}, 1000);
const fs = require("fs").promises;

setInterval(() => {
  fs.unlink(" /abcdefg.js").catch(console.error);
}, 1000);
process.on("uncaughtException", (err) => {
  // 다눈히 에러 내용을 기록하는 정도로 사용하고, 에러를 기록한 후 process.exit()으로 프로세스를 종료하는 것이 좋다.
  console.error("예기치 못한 에러", err);
});

setInterval(() => {
  throw new Error("서버를 고장내주마!");
}, 1000);

setTimeout(() => {
  console.log("실행됩니다.");
}, 2000);

Quiz

1. 폴더와 파일의 경로를 쉽게 조작하도록 도와주는 모듈은 ( path )이다.

2. 암호화된 문자열을 원래 문자열로 되돌려놓는 것을 ( 복호화 )라 한다.

3. 메모리에 저장된 데이터를 ( 버퍼 )라고 한다.

4. 버퍼의 크기를 작게 만들고 여러 번에 걸쳐 나눠 보내는 방식을 ( 스트림 )이라 한다.

 

Programming Quiz

1. 파일 시스템 모듈을 가져오는 코드를 작성하시오.

2. on을 사용하여 같은 이벤트를 호출하시오.

myEvent.addListener("event", () => {
  console.log("이벤트");
});

 

 


Answer

const fs = require("fs");
myEvent.on("event", () => {
  console.log("이벤트");
});

 

 

 

출처 :  조현영,  Node.js 교과서 개정 3판, 길벗(2022)

Corner Node.js 1
Editor : Krong

728x90

관련글 더보기