Dandy Now!
  • [개발자의품격][부트캠프][1기][30차시] Node.js #1 | 정의, 특징 | 내장 객체
    2022년 04월 06일 19시 00분 28초에 업로드 된 글입니다.
    작성자: DandyNow
    728x90
    반응형

    | Node.js 정의

    Node.js는 Chrome V8 javaScript 엔진으로 빌드된 JavaScript 런타임 환경이다.

     

    | Node.js 특징

    논블로킹(Non-blocking) I/O

    기본적으로 JavaScript는 블로킹 I/O이다. 예를 들어 블로킹 I/O로 파일 쓰기를 한다면 "파일 쓰기 요청 > (파일 쓰기 함수 호출) 파일 쓰기 실행> 파일 쓰기 완료 > 다음 코드 실행"과 같이 진행된다. 반면 논블로킹 I/O로 파일 쓰기를 한다면 "파일 쓰기 요청 > (파일 쓰기 함수 호출) 파일 쓰기 실행> 다음 코드 실행"으로 진행되며 이 과정에서 파일 쓰기가 완료되면 Callback 함수를 호출해서 파일 쓰기가 완료되었음을 알려준다. 즉, 논블로킹은 함수를 호출한 후 호출된 함수가 실행되는 중에도 다른 작업을 동시에 진행할 수 있다. 블로킹 I/O를 '동기식 I/O', 논블로킹 I/O를 '비동기식 I/O'라고 부르기도 한다.

     

    싱글 스레드(Single Thread)

    Java는 멀티 스레드로 대용량 서비스에 안정적이나 비용이 많이 든다. 반면 Node.js는 싱글 스레드 방식이며, 싱글 슬레드의 약점을 극복하기 위해 논블로킹을 사용한다.

     

    이벤트 루프(Event Loop)

    논블로킹 I/O를 지원하기 위해서 libuv 라이브러리의 이벤트 루프 기능을 사용한다. 비동기 처리를 위해서 libuv의 이벤트 루프를 통해서 처리해야 할 함수를 스케줄링해서 처리한다.

     


     

    | 내장 객체

    console.error

    console.error(new Error("에러 메시지 출력"));
    console.error("에러 메시지 출력");

     

    [그림 1] console.error

     

    console.table

    const arr = [
      { name: "John Doe", email: "john@gmail.com" },
      { name: "Sewol Han", email: "han@gmail.com" },
    ];
    
    console.table(arr);

     

    [그림 2] console.table

     

    console.dir

    실무에서 depth가 깊은 많은 양의 객체(데이터)를 볼 때 가독성을 높여준다.

    const obj = {
      student: {
        grade1: { class1: {}, class2: {} },
        grade2: { class1: {}, class2: {} },
        teachers: ["John Doe", "Sewol Han"],
      },
    };
    
    console.log(obj);
    console.dir(obj, { depth: 1, colors: true });

     

    [그림 3] console.dir

     

    console.time

    코드의 성능이 개선되었는지 확인할 때 유용하게 사용할 수 있다.

    console.time("func 1");
    for (let i = 0; i < 99999; i++) {}
    console.timeEnd("func 1");

     

    [그림 4] console.time

     

    module

    // 03_calculator.js
    function add(n1, n2) {
      return n1 + n2;
    }
    
    function minus(n1, n2) {
      return n1 - n2;
    }
    
    function mul(n1, n2) {
      return n1 * n2;
    }
    
    function divide(n1, n2) {
      return n1 / n2;
    }
    
    const defaultNum = 1;
    
    module.exports = {
      add,
      minus,
      mul,
      divide,
      defaultNum,
    };

     

    // 03_module.js
    const { add, minus, defaultNum } = require("./03_calculator");
    
    console.log(add(7, 2));
    console.log(minus(7, 2));
    console.log(defaultNum);

     

    [그림 5] module

     

    setImmediate

    const timeout = setTimeout(() => {
      console.log("1초 후에 실행됩니다.");
    }, 1000);
    
    const interval = setInterval(() => {
      console.log("1초 마다 실행됩니다.");
    }, 1000);
    
    const immediate = setImmediate(() => {
      console.log(
        "setImmediate 함수를 호출하고 난 뒤에 오는 모든 코드를 먼저 실행하고 그 다음 실행합니다."
      );
    });
    
    console.log("1");
    console.log("2");
    console.log("3");

     

    [그림 6] setImmediate

     

    process.on

    process event를 등록해서 사용할 수 있다. 마치 Vue.js의 lifecycle-hooks과 같이 프로세스의 시점을 제어한다.

    const process = require("process");
    
    // 노드 프로세스가 종료될 때
    process.on("exit", (code) => {
      console.log("exit 이벤트 리스너", code);
    });
    
    // 이벤트 루프에 등록된 작업이 모두 종료되고, 노드 프로세스가 종료되기 직전
    process.on("beforeExit", (code) => {
      console.log("beforeExit 이벤트 리스너", code);
    });
    
    console.log("출력");

     

    [그림 7] process.on

     

    process.exit

    멘토님은 실무에서 한번 사용해본 적이 있는 기능이라고 한다. 최초의 환경설정에 사용했다고 한다. 환경설정이 완료되면 다시 환경설정 화면을 띄우는 것이 불필요하기 때문이다. 아래 코드가 실행된 콘솔에서 "exit 이벤트 리스너 0"만 출력된 것에 유의하자!

    const process = require("process");
    
    // 노드 프로세스가 종료될 때
    process.on("exit", (code) => {
      console.log("exit 이벤트 리스너", code);
    });
    
    // 이벤트 루프에 등록된 작업이 모두 종료되고, 노드 프로세스가 종료되기 직전
    process.on("beforeExit", (code) => {
      console.log("beforeExit 이벤트 리스너", code);
    });
    
    // 이후의 코드가 실행되지 않게 한다.
    process.exit();
    
    console.log("출력");

     

    [그림 8] process.exit

     

    path

    path는 파일 경로를 다룬다.

    아래 코드는 path를 사용하지 않고 __filename, __dirname 만 사용한 경우이다. 실무에서 자주 사용한다.

    console.log(__filename);
    console.log(__dirname);

     

    [그림 9] __filename, __dirname

     

    path.basename

    const path = require("path");
    
    console.log(path.basename(__filename)); 
    console.log(path.basename(__filename, ".js"));

     

    [그림 10] path.basename

     

    path.extname

    path.extname을 사용하면 코드가 간결해진다.

    const path = require("path");
    
    const filename = "index.html";
    console.log(filename.substring(filename.indexOf("."))); // .html
    
    console.log(path.extname("index.html")); // .html

     

    [그림 11] path.extname

     

    path.parse

    const path = require("path");
    
    console.log(path.parse(__filename));

     

    [그림 12] path.parse

     

    URL

    url을 객체로 parsing 해준다.

    const myURL = new URL(
      // url최대치
      "https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash"
    );
    
    console.log(myURL);

     

    [그림 13] URL

     

    crypto

    "pw1234"를 암호화하는 코드이다.

    const crypto = require("crypto");
    // const { resolve } = require("path");
    
    console.log(crypto.createHash("sha512").update("pw1234").digest("base64")); // 암호화 알고리즘: sha512, 인코딩 방식: base64
    // 9iSeOd1vv2qinR2UM5Aog5LmqBncF/oFeTTsPUjqwGoG3lG232280LqAScE7FR7HHe4K0gyedCN7iZDZl+NZaA==
    console.log(crypto.createHash("sha512").update("pw1234").digest("hex")); // 암호화 알고리즘: sha512, 인코딩 방식: hex
    // f6249e39dd6fbf6aa29d1d943390288392e6a819dc17fa057934ec3d48eac06a06de51b6df6dbcd0ba8049c13b151ec71dee0ad20c9e74237b8990d997e35968

     

    base64 보다 hex 가 더 안전하지만 salting 암호화를 하여야 더 안전하다. 데이터 베이스에는 "사용자 아이디, 암호화된 비밀번호, salt 값"이 저장되어 있어야 한다.

    // salting 암호화를 위한 랜덤한 64비트 문자열을 만드는 함수
    const createSalt = () => {
      return new Promise((resolve, reject) => {
        crypto.randomBytes(64, (err, buf) => {
          if (err) reject(err);
          resolve(buf.toString("base64"));
        });
      });
    };
    
    const createCryptPassword = async (plainPassword) => {
      const salt = createSalt();
      return new Promise((resolve, reject) => {
        // 암호화할 문자열, salt문자열, 반복횟수, 출력할 바이트수, 해시 알고리즘
        crypto.pbkdf2(plainPassword, salt, 100000, 64, "sha512", (err, key) => {
          if (err) reject;
          resolve({ password: key.toString("base64"), salt });
        });
      });
    };

     

    salting 암호화된 경우 로그인할 때 아래의 코드를 이용한다.

    const getCryptPassword = async (plainPassword, salt) => {
      return new Promise((resolve, reject) => {
        // 암호화할 문자열, salt문자열, 반복횟수, 출력할 바이트수, 해시 알고리즘
        crypto.pbkdf2(plainPassword, salt, 100000, 64, "sha512", (err, key) => {
          if (err) reject;
          resolve({ password: key.toString("base64"), salt });
        });
      });
    };

     

    fs.readFile

    text.txt 파일의 내용을 비동기 형식으로 읽어서 콘솔에 출력하는 코드이다. 콜백 함수로 에러가 발생한 경우에는 err을 정상적인 경우에는 data를 출력한다.

    const fs = require("fs");
    
    fs.readFile("./sample/text.txt", "utf8", (err, data) => {
      if (err) {
        throw err;
      }
      console.log(data);
    });

     

    [그림 14] fs.readFile

     

    fs.readFileSync

    text.txt 파일의 내용을 동기 형식으로 읽어서 콘솔에 출력하는 코드이다. 

    const fs = require("fs");
    
    const data = fs.readFileSync("./sample/text.txt", "utf8");
    console.log(data);

     

    fs.writeFile

    "파일 쓰기 테스트"라는 내용의 text_w.txt 파일을 생성한 후 콘솔에 txt 파일의 내용을 출력한다.

    const fs = require("fs");
    
    const txt = "파일 쓰기 테스트";
    fs.writeFile("./sample/text_w.txt", txt, "utf-8", (err) => {
      if (err) {
        throw err;
      }
      const data = fs.readFileSync("./sample/text_w.txt", "utf8");
      console.log(data);
    });
    728x90
    반응형
    댓글