2023.04.27 node start

이무헌·2023년 7월 21일
0

node.JS

목록 보기
2/10
post-thumbnail

1.Node.js 입문

  • node.js란?
    # Nodejs란?
    
    ## nodeJS란 서버???
    
    - nodejs 자체가 웹서버가 아니고 <br>
      nodejs는 js를 사용해서 사용해서 서버 측에 코드들을 <br>작성하고 서버를 구축할 수 있게 도와주고 <br>
      개발 생산성을 위해 npm을 통해 모듈을 제공한다.
    
    ## npm(node pacakage manager)란?
    
    - 개발자들이 작성한 자신의 모듈을 공유할 수 있는 <br>패키지 저장소 npm을 사용하면 모듈을 쉽게 설치받아서 사용할 수 있다.<br>
      node.js가 개발된 이유는 웹서버 개발을 하기위해서도 있지만 <br>방대한 오픈소스 생태계를 구축하기 위해서 개발자들이 편리하게 <br>
      개발할 수 있게 개발 생산성을 향상 시켜주기 위해서.
    
    ## chrome v8 javascript
    
    - chrome V8 javascript 엔진으로 `build`된
      <br>서버 측 자바스크립트 런타임 환경 <br>
      브라우저와 런타임환경이 다릅니다.
    
    - build가 되었다는 건 구글에서 개발한 v8 javascript 엔진을 사용해서 build를 한다. <br>
      build라는건 코드를 컴파일러를 통해 실행파일로 변환하는 작업이라고 보면됨
    
    - 구글에서 개발한 v8 javascript 엔진은 2008년도 <br> 오픈소스로 공개하고 구글에서 개발한 <br>
      js엔진을 사용하고 `비동기 I/O` 와 `이벤트 기반의 아키텍쳐`를 채택해서 속도가 빠르고 성능이 좋다.
    
    ## nodeJs의 블로킹과 논블로킹
    
    - nodejs에서 `비동기 I/O` 작업을 진행하는 동안 또 다른 작업을 실행 할 수 있다. (nodeJS의 장점)
    - I/O 작업이 완료될 떄 까지 기다리면서 다른 코드를 또 실행 시킬 수 있다. <br>
      Input/Output:파일 시스템 (브라우저에서 파일을 조작할 순 없다.) 네트워크,디스크 등 <br>
      데이터를 읽거나 쓰거나 하는 작업을(무거운 작업들)
    - 블로킹이란 한 작업이 끝날 때 까지 다른 작업을 수행하지 않는것 <br>
    블로킹 I/O 작업은 수행하는 동안 다른 코드의 실행이  중단된다.
    - 논블로킹이란 I/O 작업을 기다리는 동안 다른 코드들을
    실행할 수 있게 하는것 <br>
    
    nodejs는 모든 I/O 작업은 비동기적으로 실행되고<br>
    블로킹을 하지 않는다.
    
    ## nodejs의 이벤트 기반의 아키텍쳐
    - nodejs 가장 큰 특징으로 이벤트 기반의 아키텍처가 있다. <br>이벤트 기반의 프로그래밍은 이벤트가 발생하면 콜백 함수를 실행시키는 방식 <br>
    이벤트 기반의 프로그래밍을 작성하면 비동기식 처리가 가능하고 <br>좋은 성능과 확장성을 가질 수 있다.
    
    - 이벤트 기반
    <br>이벤트를 실행시키면 이벤트로 등록한 작업을 수행하는것
    <br>우리가 자바스크립트에서 클릭 같은 이벤트에 콜백함수를 작성해놓고 <br>이벤트 기반의 특정 이벤트가 발생하면 등록시킨 콜백함수를 호출해서 실행 시킨다.
    <br> 이런 내용을 `이벤트 리스너에 콜백함수를 등록한다.`
    
    - nodejs는 이벤트가 발생하면 이벤트에 등록된 콜백 함수를 호출하고 이벤트를 다 처리하면 <br>
    다음 이벤트 발생까지 대기한다.
    
    ```mermaid
    graph TD;
      A[nodejs]-- 이벤트 리스너와 콜백 함수 등록 -->D((이벤트 리스너));
      D -- 이벤트를 수신 --> B[이벤트]
      B -- 이벤트 리스터 호출 --> D
      D -- 콜백 함수 호출 --> C[콜백함수]
      C -- 작업을 수행 --> A
    
  • nodejs의 REPL
    // nodejs설치 해봅시다
    // 설치 버전 확인
    // node -v
    
    // nodejs의 Repl
    // 읽기 -해석(실행) - 출력 -반복
    //REPL(read-eval-print-loop)은 콘솔 환경에서
    // 코드를 입력하면 즉시 실행해서 결과를 반환해주는 인터페이스
    // node.js의 코드를 test하고 실행할 수 있도록 해주는 대화형 콘솔
    
    // 레포라는 모드에 들어가 보자
    // 터미널 창을 열고
    // node만 써주면 됩니다.
    // 레포 모드에서 test코드를 작성해보자
    const str='hello world';
    console.log(str)
    // 출력된 값은 'hello nodejs'이렇게 나오고
    // 함수의 반환값이 다음으로 출력되는데 undefined
    // console.log(str)함수의 반환값이 undefined이기 때문!
    // 그래서 helloworld 나오고 undefined나오는거
    // 자바 스크립트로 브라우저 창을 만들고 볼 때는 런타임 환경
    // node의 런타임 환경
    // 실행되고 있는 환경이 다르다
    // 브라우저에서 실행하는 console.log와 node.js에서 실행하는 console.log는
    // 내용이 비슷한거지 같은 친구가 아니다.
    // 브라우저에서는 this 전역객체가 window였고 
    // node 런타임 환경에서 this의 전역객체가 global
    
    // node로 파일을 실행해서 응용프로그램으로 만들어보자
    
    // 파일 실행 모드
    // node 구문 뒤에 파일의 경로를 작성해 주면 된다.
    • 리눅스의 vi와 비슷하게 node의 환경에서 js파일을 작성할 수 있다!
  • module화 시키기 💡 // 모듈이란? // 하나의 파일에 기능을 모두 작성해버리면 // 렵럽이 힘들고... // 수정도 힘들고 유지보수 힘들고.. // 파일을 규칙에 맞게 나눌 수 있으면 나누는 것이 좋다. // 하나의 큰 프로그램의 작은 단위 // 모듈은 가각의 파일단위로 구분되어있고, // 파일의 내용은 필요한 기능들의 함수나 변수들이 포함되어 있는것. // 작은 모듈을 하나 만들어 보자 - 모듈화시 장점은 다음과 같다. 1. 재사용성: 큰 코드베이스를 더 작은 모듈로 나누면 각 모듈을 코드베이스의 다른 부분이나 다른 프로젝트에서 재사용할 수 있습니다. 이를 통해 개발 시간을 절약하고 버그 위험을 줄일 수 있습니다. 2. 유지보수성: 모듈화를 통해 시간이 지남에 따라 코드베이스를 보다 쉽게 유지관리할 수 있습니다. 각 모듈은 독립적으로 개발, 테스트 및 유지 관리할 수 있으므로 버그를 쉽게 추적하고 코드베이스를 변경할 수 있습니다. 3. 캡슐화: 모듈은 데이터와 기능을 캡슐화하여 코드베이스의 다른 부분 간의 충돌을 방지할 수 있습니다. 이렇게 하면 코드베이스를 보다 체계적이고 이해하기 쉽게 유지할 수 있습니다. 4. 확장성: 코드베이스가 커짐에 따라 관리 및 유지 관리가 점점 더 어려워질 수 있습니다. 모듈성은 큰 코드베이스를 더 작고 관리하기 쉬운 조각으로 분해하여 시간이 지남에 따라 코드베이스를 더 쉽게 확장할 수 있도록 합니다. 5. 협업: 여러 개발자와 함께 대규모 코드베이스에서 작업할 때 모듈성은 충돌을 줄이고 협업을 개선하는 데 도움이 될 수 있습니다. 각 개발자는 서로 다른 모듈에서 작업할 수 있으며 변경 사항을 보다 쉽게 통합할 수 있습니다. ```jsx const BlockCalss = [ { name: "박덕배", age: 1, comment: function () { console.log(this.name + "이야"); }, }, { name: "김창춘", age: 1, comment: function () { console.log(this.name + "이야"); }, }, ]; ``` - BlockClass를 만들고 다른 파일에서 쓰기위해 내보내보자 - exports.module을 이용하여 모듈을 내보낼 수 있다. ```jsx module.exports = BlockClass ; ``` - 이렇게 보내면 다음 파일에선 아래와 같이 사용할 수 있다. ```jsx // 모듈을 가져오는 방법 const BlockClass = require("./index2"); console.log(BlockClass); // 현재 내용에 외부의 모듈을 모듈화 할 수 있는 메소드가 // require메소드를 사용해서 외부의 모듈을 현재 내용에 모듈화 // 시켜줄 수 있다. ``` [ { name: '박덕배', age: 1, comment: [Function: comment] }, { name: '김창춘', age: 1, comment: [Function: comment] } ] - 정상적으로 module을 가져오는 것을 확인 할 수 있다.
  • node의 내장 모듈
    // nodejs의 내장 모듈
    // nodejs에서 미리 만들어 놓은 모듈을 내장 모듈이라고 해요.
    // require에서 모듈경로와 파일명을 적는게 아니라
    // 모듈의 이름만 필요
    
    // 운영체제
    
     const os = require("os");
    
     console.log(os);
    
    //nodejs의 내장 객체
    // node의 전역객체에는 global 객체 우리가 js에서 보던 window 처럼
    // 전역 객체가 있어요
    // 런타임 환경이 다르기 때문에 전역 객체도 다르다.
    
    // 자바스크립트에 this를 작성하면 module.exports가 나옴
    // 참조가 들어가기 때문에 다르지 않을까?
    
    // nodejs의 모듈의 실행 컨텍스트와 전역 컨텍스트가 다르기 때문에
    // nodejs에서는 모듈은 각각의 파일 스코프를 가지고 있기 때문에 this는 모듈 자체를 가리킨다.
    • 다음과 같이 os에 대한 정보가 나온다.

      💡

      {
      arch: [Function: arch] {
      [Symbol(Symbol.toPrimitive)]: [Function (anonymous)]
      },
      cpus: [Function: cpus],
      endianness: [Function: endianness] {
      [Symbol(Symbol.toPrimitive)]: [Function (anonymous)]
      },
      freemem: [Function: getFreeMem] {
      [Symbol(Symbol.toPrimitive)]: [Function (anonymous)]
      },
      getPriority: [Function: getPriority],
      homedir: [Function: __node_internal_checkError] {
      [Symbol(Symbol.toPrimitive)]: [Function (anonymous)]
      },
      hostname: [Function: __node_internal_checkError] {
      [Symbol(Symbol.toPrimitive)]: [Function (anonymous)]
      },
      loadavg: [Function: loadavg],
      networkInterfaces: [Function: networkInterfaces],
      platform: [Function: platform] {
      [Symbol(Symbol.toPrimitive)]: [Function (anonymous)]
      },
      release: [Function: getOSRelease] {
      [Symbol(Symbol.toPrimitive)]: [Function (anonymous)]
      },
      setPriority: [Function: setPriority],
      tmpdir: [Function: tmpdir] {
      [Symbol(Symbol.toPrimitive)]: [Function (anonymous)]
      },
      totalmem: [Function: getTotalMem] {
      [Symbol(Symbol.toPrimitive)]: [Function (anonymous)]
      },
      type: [Function: getOSType] {
      [Symbol(Symbol.toPrimitive)]: [Function (anonymous)]
      },
      userInfo: [Function: userInfo],
      uptime: [Function: getUptime] {
      [Symbol(Symbol.toPrimitive)]: [Function (anonymous)]
      },
      version: [Function: getOSVersion] {
      [Symbol(Symbol.toPrimitive)]: [Function (anonymous)]
      },
      constants: [Object: null prototype] {
      UV_UDP_REUSEADDR: 4,
      dlopen: [Object: null prototype] {
      RTLD_LAZY: 1,
      RTLD_NOW: 2,
      RTLD_GLOBAL: 8,
      RTLD_LOCAL: 4
      },
      errno: [Object: null prototype] {
      E2BIG: 7,
      EACCES: 13,
      EADDRINUSE: 48,
      EADDRNOTAVAIL: 49,
      EAFNOSUPPORT: 47,
      EAGAIN: 35,
      EALREADY: 37,
      EBADF: 9,
      EBADMSG: 94,
      EBUSY: 16,
      ECANCELED: 89,
      ECHILD: 10,
      ECONNABORTED: 53,
      ECONNREFUSED: 61,
      ECONNRESET: 54,
      EDEADLK: 11,
      EDESTADDRREQ: 39,
      EDOM: 33,
      EDQUOT: 69,
      EEXIST: 17,
      EFAULT: 14,
      EFBIG: 27,
      EHOSTUNREACH: 65,
      EIDRM: 90,
      EILSEQ: 92,
      EINPROGRESS: 36,
      EINTR: 4,
      EINVAL: 22,
      EIO: 5,
      EISCONN: 56,
      EISDIR: 21,
      ELOOP: 62,
      EMFILE: 24,
      EMLINK: 31,
      EMSGSIZE: 40,
      EMULTIHOP: 95,
      ENAMETOOLONG: 63,
      ENETDOWN: 50,
      ENETRESET: 52,
      ENETUNREACH: 51,
      ENFILE: 23,
      ENOBUFS: 55,
      ENODATA: 96,
      ENODEV: 19,
      ENOENT: 2,
      ENOEXEC: 8,
      ENOLCK: 77,
      ENOLINK: 97,
      ENOMEM: 12,
      ENOMSG: 91,
      ENOPROTOOPT: 42,
      ENOSPC: 28,
      ENOSR: 98,
      ENOSTR: 99,
      ENOSYS: 78,
      ENOTCONN: 57,
      ENOTDIR: 20,
      ENOTEMPTY: 66,
      ENOTSOCK: 38,
      ENOTSUP: 45,
      ENOTTY: 25,
      ENXIO: 6,
      EOPNOTSUPP: 102,
      EOVERFLOW: 84,
      EPERM: 1,
      EPIPE: 32,
      EPROTO: 100,
      EPROTONOSUPPORT: 43,
      EPROTOTYPE: 41,
      ERANGE: 34,
      EROFS: 30,
      ESPIPE: 29,
      ESRCH: 3,
      ESTALE: 70,
      ETIME: 101,
      ETIMEDOUT: 60,
      ETXTBSY: 26,
      EWOULDBLOCK: 35,
      EXDEV: 18
      },
      signals: [Object: null prototype] {
      SIGHUP: 1,
      SIGINT: 2,
      SIGQUIT: 3,
      SIGILL: 4,
      SIGTRAP: 5,
      SIGABRT: 6,
      SIGIOT: 6,
      SIGBUS: 10,
      SIGFPE: 8,
      SIGKILL: 9,
      SIGUSR1: 30,
      SIGSEGV: 11,
      SIGUSR2: 31,
      SIGPIPE: 13,
      SIGALRM: 14,
      SIGTERM: 15,
      SIGCHLD: 20,
      SIGCONT: 19,
      SIGSTOP: 17,
      SIGTSTP: 18,
      SIGTTIN: 21,
      SIGTTOU: 22,
      SIGURG: 16,
      SIGXCPU: 24,
      SIGXFSZ: 25,
      SIGVTALRM: 26,
      SIGPROF: 27,
      SIGWINCH: 28,
      SIGIO: 23,
      SIGINFO: 29,
      SIGSYS: 12
      },
      priority: [Object: null prototype] {
      PRIORITY_LOW: 19,
      PRIORITY_BELOW_NORMAL: 10,
      PRIORITY_NORMAL: 0,
      PRIORITY_ABOVE_NORMAL: -7,
      PRIORITY_HIGH: -14,
      PRIORITY_HIGHEST: -20
      }
      },
      EOL: '\n',
      devNull: '/dev/null'
      }

    • 무슨 소리야..

    • 잘은 모르겠지만 노트북의 운영체제 정보가 나온 것 같다.

    • 이와 같이 node에는 내장모듈이 있으며 이를 효율적으로 사용하는 것이 중요하다.

    • 서버 또한 이 내장모듈로 열고 관리한다.

      global.console.log();
      global.console.time(); //코드 시작 시간 매개변수로 해당 테스트 이름을 문자열 작성
      global.console.timeEnd(); //코드 종료후 시간 출력
      
      // 전달된 객체를 표형태로 보여주는 메소드
      // global.console.table({a: { name: "이무헌" },b: { name: "이무헌2" },c: { name: "이무헌3" },});
      
      // 실행시키면 실행한 파일의 이름까지 보여줌
      console.log(__filename);
      // 실행한 파일의 directory까지 보여줌
      console.log(__dirname);
      
      // 나중에 필요할 때가 생기니 잘 알아둘것~
    • a서버에서 코드를 작성할 때 클라이언트에게 데이터가 몇 초후의 전달되며 얼마나 빨리 처리되는지 알기 위해선 위와같은 time메소드가 매우 필요하다.

    • 실행파일 및 directory까지 편리하게 확인 가능하다.
      이제 vsc에서 우클릭+상대경로 안해도 된다….

      // process 객체
      process.env
      // console.log(process.env);
      console.log(process.version) //노드의 설치 버전
      console.log(process.execPath); //노드의 경로
      console.log(process.cpuUsage()); //cpu 사용량
    • process라는 객체는 환경변수 env를 가진다. 그 외에도 다양한 객체를 가진다.

    1. process.argv: 이 속성에는 Node.js 프로세스에 전달되는 명령줄 인수 배열이 포함되어 있습니다. 배열의 첫 번째 요소는 Node.js 실행 파일의 경로이고 두 번째 요소는 실행 중인 스크립트의 경로입니다. 나머지 요소는 스크립트에 전달된 명령줄 인수입니다.
    2. process.env: 이 속성에는 Node.js 프로세스에서 사용할 수 있는 환경 변수를 나타내는 개체가 포함되어 있습니다. process.env.SOME_VARIABLE과 같이 변수 이름을 키로 사용하여 특정 환경 변수에 액세스할 수 있습니다.
    3. process.exit([code]): 이 메서드는 지정된 종료 코드로 Node.js 프로세스를 종료합니다. 코드가 제공되지 않으면 프로세스는 코드 0(성공)으로 종료됩니다.
    4. process.on(event, listener): 이 메서드를 사용하면 프로세스 수준 이벤트에 대한 수신기를 등록할 수 있습니다. 수신 대기할 수 있는 일부 이벤트에는 프로세스가 종료하려고 할 때 발생하는 exit와 처리되지 않은 예외가 발생할 때 발생하는 uncaughtException이 포함됩니다.
    5. process.cwd(): 이 메서드는 Node.js 프로세스의 현재 작업 디렉터리를 반환합니다.
    6. process.pid: 이 속성에는 Node.js 프로세스의 프로세스 ID가 포함됩니다.
    7. process.platform: 이 속성에는 win32, darwin 또는 linux와 같이 Node.js 프로세스가 실행 중인 운영 체제 플랫폼을 나타내는 문자열이 포함됩니다.
    • 위 코드에서 cpu할당량을 나타내는 함수도 매우 유용할 것 같다.
  • exports오브젝트처럼 쓰기
    // 우리가 window를 생략해서 작성하던 것과 같이
    // window.console.log()
    // console.log()
    // global과 module을 생략해서 작성할 수 있다.
    
    // console.log(module.exports===exports);
    // true 생략을 해도 된다.
    • a 우리가 일반 js에서 console.log에서 window를 생략했던 것 처럼 node도 생략이 가능하다.

    • 바닐라 js에선 this가 window를 가리키지만 node에선 this가 global을 가리킨다.

    • 하지만 역시 global을 생략이 가능하다.

    • 이처럼 우리는 module을 생략 할 수도 있다!

      exports.objs = { a: 1 };
      
      exports.add = () => {
        return 2;
      };
    • a이렇게 하면 exports에서 objs와 add함수를 내보내게 된다.

    • 이제 받아보자

      const BlockClass = require("./index5");
      
      // const require=(path)=>{
      //     // 1.해당 파일의 (path)를 가져오고
      //     // 2.해당 소스코드를 실행시킨다.
      //     // 3. return module.exports
      // }
      
      // ES 6
      // nodejs가 자체적으로 모듈의 문법을 만든 것
      // 모듈 시스템을 구축한 것.
      console.log(BlockClass);//{ objs: { a: 1 }, add: [Function (anonymous)] }
      
    • a 정상적으로 출력이 됨을 알 수 있다. 그렇다면 module.exports로는 어떻게 내보낼까? 즉,변수를 객체에 담아 보내려면?

      const objs = { a: 1 };
      const add = () => {
            return 2;
          };
      
      const objss = {
        objs: objs,
        add: add,
      };
      
      // exports.objs = { a: 1 };
      
      // exports.add = () => {
      //   return 2;
      // };
      
      module.exports=objss
    • 객체와 함수를 하나의 object에 담아 보냈다.

      const BlockClass = require("./index5");
      
      // const require=(path)=>{
      //     // 1.해당 파일의 (path)를 가져오고
      //     // 2.해당 소스코드를 실행시킨다.
      //     // 3. return module.exports
      // }
      
      // ES 6
      // nodejs가 자체적으로 모듈의 문법을 만든 것
      // 모듈 시스템을 구축한 것.
      console.log(BlockClass); //{ objs: { a: 1 }, add: [Function: add] }
    • 똑같이 나온다. 그렇다면 구조분해할당을 이용해보자

      const objs = { a: 1 };
      const add = () => {
            return 2;
          };
      
      // exports.objs = { a: 1 };
      
      // exports.add = () => {
      //   return 2;
      // };
      
      module.exports={objs: objs,
        add: add}
    • 똑같이 나온다. 받는쪽에서 마찬가지로 구조분해로 받을 수 있다.

      const {objs,add} = require("./index5");
      
      // const require=(path)=>{
      //     // 1.해당 파일의 (path)를 가져오고
      //     // 2.해당 소스코드를 실행시킨다.
      //     // 3. return module.exports
      // }
      
      // ES 6
      // nodejs가 자체적으로 모듈의 문법을 만든 것
      // 모듈 시스템을 구축한 것.
      console.log(objs,add); //{ a: 1 } [Function: add]
    • 개인적으로 구조분해 할당으로 가져오는 방법이 더 깔끔하고 좋은 것 같다.

    • 변수를 외우지 않아도 되고

  • 간단하게 웹 서버 만들기
    // 웹서버 ㄱ
    
    // 내장모듈 가져오자
    const http = require("http");
    
    // createServer 메소드는 서버 객체를 만들어 주고
    // 클라이언트의 요청을 받으면 호출이 된다.
    // 전달된 콜백 함수는 클라이언트의 요청을 받아서 처리 후
    // 클라이언트에 응답해준다.
    
    const server = http.createServer((req, res) => {
    //   res.setHeader("Content-Type", "text/plain; charset=utf-8");
      res.end("serverOn");
    })
    • node 내장함수를 이용하여 서버를 만들었다.

      // 포트
      // 3000번 포트 같은... 포트를 지정하는 이유
      // 네트워크 프로세스를 나눠 주기 위해서
      // http 80 https 433
      // 시스템 예약 포트이외의
      // 사용하지 않을 것 같은 포트들을 사용하면 된다.
      // 1025~65535까지 안에서
      // 거의 많이 사용되는건 8000,8080
      
      const PORT = 4000;
      
      // 서버 객체의 listen 메소드를 호출해서
      // 클라이언트의 요청을 대기상태로 만들어 줄 수 있다.
      // 이벤트 루프를 돌면서 요청이 오기까지 대기를 하다가
      // 요청이 오면 응답해준다.
      // listen매개 변수로 첫번째로 port
      server.listen(PORT, () => {
        console.log(+PORT + "=>  이녀석..성공했구나");
      });
    • 그 후, 포트를 설정하여 서버 생성 이벤트를 만들어 주면 된다. 요청이 온다면 listen이 응답해준다. 응답이 성공적으로 끝난다면 res로 end에 정의한 매개변수를 전달한다!

profile
개발당시에 직면한 이슈를 정리하는 곳

0개의 댓글