경일게임아카데미 멀티 디바이스 메타버스 플랫폼 개발자 양성과정 20220913 2022/04/04~2022/12/14

Jinho Lee·2022년 9월 13일
0

2022.09.13 경일 메타버스 24주차 1일 수업내용. Node.js - 노드 기능, http 모듈

노드 기능

  • 자료 : 교과서 “Node.js 교과서” ch. 3 노드 기능 알아보기 p. 89

process

  • p. 108 ~ 112

  • process 객체현재 실행되고 있는 노드 프로세스에 대한 정보를 담고 있다.

  • 속성

    • version : 노드의 버전

    • arch : 프로세서 아키텍처 정보

    • platform : 운영체제 플랫폼 정보

    • pid : 현재 프로세스의 아이디

    • uptime() : 프로세스가 시작된 후 흐른 시간, 단위는 초

    • execpath : 노드의 경로

    • cwd() : 현재 프로세스가 실행되는 위치

    • cpuUsage() : 현재 cpu 사용량

process.env

  • 시스템의 환경 변수

  • 환경 변수는 노드에 직접 영향을 미치기도 한다.

    • UV_THREADPOOL_SIZE

      • 스레드풀스레드 개수를 조절
    • NODE_OPTIONS

      • 노드를 실행할 때의 옵션을 입력 받는 환경 변수
  • 서비스의 중요한 키를 저장하는 공간

    • 중요한 비밀번호 (키)는 process.env의 속성으로 대체할 수 있다.

      const secretId = process.env.SECRET_ID;
      const secretCode = process.env.SECRET_CODE;

process.nextTick(콜백)

  • 이벤트 루프가 다른 콜백 함수보다 nextTick의 콜백 함수를 우선으로 처리하도록 만든다.

  • process.nextTicksetImmediatesetTimeout보다 먼저 실행된다.

    • resolve된 Promise도 다른 콜백들보다 우선시된다.

    • 우선되는 이 둘을 마이크로태스크(Microtask)로 구분짓는다.

  • 예시 코드

setImmediate(() => {
    console.log('immediate');
});
process.nextTick(() => {
    console.log('nextTick');
});
setTimeout(() => {
    console.log('timeout');
}, 0);
Promise.resolve().then(() => console.log('promise'));

/*
console
> node 3_4_6_2
nextTick
promise
timeout
immediate
*/
  • 주의 : 마이크로태스크를 재귀 호출하면 이벤트 루프는 다른 콜백 함수보다 마이크로태스크를 우선 처리하므로 콜백 함수들이 실행되지 않을 수 있다.

process.exit(코드)

  • 실행 중인 노드 프로세스를 종료한다.

  • 인수로 종료 코드를 줄 수 있다.

    • 없거나 0 ⇒ 정상 종료

    • 1 ⇒ 비정상 종료

  • 서버 환경에서 사용하면 서버가 멈추므로 잘 사용하지 않는다.

노드 내장 모듈

  • p. 113 ~ 137

os

  • p. 113 ~ 115

  • 자바스크립트는 운영체제의 정보를 가져올 수 없지만, 노드는 os 모듈에 정보가 담겨 있어 정보를 가져올 수 있다.

path

  • p. 115 ~ 119

  • 폴더와 파일의 경로 조작을 돕는 모듈

  • 운영체제별로 경로 구분자가 다르기 때문에 필요

    • 윈도 타입 :
      \로 구분

    • POSIX 타입 :
      유닉스 기반 운영체제. 맥, 리눅스 등. /로 구분

  • 속성

    • path.sep

      • 경로의 구분자

      • 윈도는 \, POSIX는 /

    • path.delimiter

      • 환경 변수의 구분자

      • process.env.PATH를 입력하면 경로가 이 구분자로 구분되어 있다.

      • 윈도는 세미콜론(;), POSIX는 콜론(:)

    • path.join(경로, …)

      • 여러 인수를 넣으면 하나의 경로로 합친다.

      • 상대 경로..(부모 디렉터리)와 .(현 위치)도 알아서 처리한다.

    • path.resolve(경로, …)

      • path.join()과 비슷하지만 동작 방식에 차이가 있다.

      • /를 만나면 path.resolve절대경로로 인식해 앞의 경로를 무시한다.

      • 같은 상황에서 path.join상대경로로 처리한다.

      • 예시 코드

        path.join('/a', '/b', 'c'); /* 결과: /a/b/c */
        path.resolve('/a', '/b', 'c'); /* 결과: /b/c */
  • 상대경로와 절대경로

    • 절대경로

      • 루트 폴더 (윈도의 C:\, POSIX의 /)나 노드 프로세스가 실행되는 위치가 기준
    • 상대경로

      • 현재 파일이 기준

      • 현재 파일과 같은 경로점 하나(.)

      • 현재 파일보다 한 단계 상위 경로점 두 개(..)

    • 예시

      • C:\users\zerocho\path.js에서 C:\로 갈 때

        • 절대경로 → C:\ 입력

        • 상대경로 → ..\.. 입력

  • 자바스크립트 문자열에서 \가 특수 문자이므로 \\처럼 두 개 붙여 경로를 표시해야 할 때도 있다.

    • ex. \nC:\\node

url

  • p. 119 ~ 123

  • 인터넷 주소 조작을 돕는 모듈

  • url 처리 두 가지 방식

    1. WHATWG (웹 표준을 정하는 단체) 방식

      • URL 생성자에 주소를 넣어 객체로 만들면 주소가 부문별로 정리된다.

      • WHATWG 방식에만 있는 username, password, origin, searchParams 속성이 존재한다.

    2. 기존 노드에서 사용하던 방식

      • 두 메소드를 주로 사용한다.

        • usl.parse(주소)

          • 주소를 분해한다.

          • usernamepassword 대신 auth 속성

          • searchParams 대신 query 속성

        • usl.format(객체)

          • 두 가지 방식 url모두 사용 가능하다.

          • 분해되었던 url 객체원래 상태로 조립한다.

    3. url 구분 방법

      • 위가 기존 노드, 아래가 WHATWG

  • 예시 코드 1

// 3_5_3_url.js
const url = require('url');

const { URL } = url;
const myURL = new URL('http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor');
console.log('new URL():', myURL);
console.log('url.format():', url.format(myURL));
console.log('--------------------------------');
const parsedURL = url.parse('http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor');
console.log('url.parse():', parsedURL);
console.log('url.format():', url.format(parsedURL));
// console
> node 3_5_3_url
new URL(): URL {
  href: 'http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor',
  origin: 'http://www.gilbut.co.kr',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'www.gilbut.co.kr',
  hostname: 'www.gilbut.co.kr',
  port: '',
  pathname: '/book/bookList.aspx',
  search: '?sercate1=001001000',
  searchParams: URLSearchParams { 'sercate1' => '001001000' },
  hash: '#anchor'
}
url.format(): http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor
--------------------------------
url.parse(): Url {
  protocol: 'http:',
  slashes: true,
  auth: null,
  host: 'www.gilbut.co.kr',
  port: null,
  hostname: 'www.gilbut.co.kr',
  hash: '#anchor',
  search: '?sercate1=001001000',
  query: 'sercate1=001001000',
  pathname: '/book/bookList.aspx',
  path: '/book/bookList.aspx?sercate1=001001000',
  href: 'http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor'
}
url.format(): http://www.gilbut.co.kr/book/bookList.aspx?sercate1=001001000#anchor
  • 기존 노드의 url 형식반드시 사용해야 하는 경우

    • host 부분 없이 pathname 부분만 오는 주소

      • WHATWG 방식이 처리할 수 없다
  • WHATWG 방식search 부분을 searchParams라는 특수한 객체로 반환하므로 유용

    • search 부분은 주소를 통해 데이터를 전달할 때 사용

      • ?로 시작

      • 키=값 형식으로 데이터 전달

      • 여러 키가 있을 경우 &로 구분

    • searchParamssearch를 조작하는 메소드

      • getAll(키) : 키에 해당하는 모든 값 반환

      • get(키) : 키에 해당하는 첫 번째 값 반환

      • has(키) : 해당 키가 있는지 없는지 검사

      • keys() : searchParams의 모든 키를 반복기(Iterator) 객체로 반환

      • values() : searchParams의 모든 값을 반복기(Iterator) 객체로 반환

      • append(키, 값) : 해당 키를 추가. 같은 키의 값이 있다면 유지하고 추가.

      • set(키, 값) : 해당 키를 추가. 같은 키의 값이 있다면 모두 삭제하고 추가.

      • delete(키) : 해당 키를 제거

      • toString() : 조작한 searchParams 객체를 다시 문자열로 조립. 이 문자열을 search에 대입하면 주소 객체에 반영

    • 예시 코드 2

      // 3_5_3_searchParams.js
      const { URL } = require('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());
      console.log('searchParams.values():', myURL.searchParams.values());
      
      myURL.searchParams.append('filter', 'es3');
      myURL.searchParams.append('filter', 'es5');
      console.log(myURL.searchParams.getAll('filter'));
      
      myURL.searchParams.append('filter', 'es6');
      console.log(myURL.searchParams.getAll('filter'));
      
      myURL.searchParams.delete('filter');
      console.log(myURL.searchParams.getAll('filter'));
      
      console.log('searchParams.toString():', myURL.searchParams.toString());
      myURL.search = myURL.searchParams.toString();
      // console
      > node 3_5_3_searchParams.js
      searchParams: URLSearchParams {
        'page' => '3',
        'limit' => '10',
        'category' => 'nodejs',
        'category' => 'javascript' }
      searchParams.getAll(): [ 'nodejs', 'javascript' ]
      searchParams.get(): 10
      searchParams.has(): true
      searchParams.keys(): URLSearchParams Iterator { 'page', 'limit', 'category', 'category' }
      searchParams.values(): URLSearchParams Iterator { '3', '10', 'nodejs', 'javascript' }
      [ 'es3', 'es5' ]
      [ 'es3', 'es5', 'es6' ]
      []
      searchParams.toString(): page=3&limit=10&category=nodejs&category=javascript

querystring

  • 기존 노드의 url을 사용할 때, search 부분을 사용하기 쉽게 객체로 만드는 모듈

  • 메소드

    • querystring.parse(쿼리) : url의 query 부분을 자바스크립트 객체로 분해

    • querystring.stringify(객체) : 분해된 query 객체를 문자열로 조립

  • 예시 코드

const url = require('url');
const querystring = require('querystring');

const parsedURL = url.parse('http://www.gilbut.co.kr/?page=3&limit=10&category=nodejs&category=javascript');
const query = querystring.parse(parsedURL.query);
console.log('querystring.parse():', query);
console.log('querystring.stringify():', querystring.stringify(query));
// console
> node 3_5_4.js
querystring.parse(): [Object: null prototype] {
  page: '3',
  limit: '10',
  category: [ 'nodejs', 'javascript' ]
}
querystring.stringify(): page=3&limit=10&category=nodejs&category=javascript

crypto

  • 암호화를 돕는 모듈

단방향 암호화

  • 복호화할 수 없는 암호화 방식

    • 복호화 : 암호화된 문자열을 원래 문자열로 되돌려놓는 것
  • 비밀번호를 암호화할 때 주로 사용

    • 고객의 비밀번호는 복호화할 필요가 없기 때문.
      로그인할 때마다 입력받은 비밀번호를 같은 암호화 알고리즘으로 암호화 한 후, 데이터 베이스의 비밀번호와 비교하면 된다.
  • 해시 함수라고도 한다.

  • 해시 기법을 주로 사용한다.

    • 해시 기법 : 어떠한 문자열을 고정된 길이의 다른 문자열로 바꾸는 방식
  • 메소드

    • createHash(알고리즘)

      • 사용할 해시 알고리즘 입력

      • md5, sha1, sha256, sha512 등

      • md5, sha1은 취약점이 발견됨

    • update(문자열)

      • 변환할 문자열 입력
    • digest(인코딩)

      • 인코딩할 알고리즘 입력. 결과물로 변환된 문자열을 반환한다.

      • base64, hex, latin1 등

  • 예시 코드 1

// 3_5_5_1_hash.js
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'));
// console
> node 3_5_5_1_hash.js
base64: dvfV6nyLRRt3NxKSlTHOkkEGgqW2HRtfu19Ou/psUXvwlebbXCboxIPmDYOFRIpqav2eUTBFuHaZri5x+usy1g==
hex: 76f7d5ea7c8b451b773712929531ce92410682a5b61d1b5fbb5f4ebbfa6c517bf095e6db5c26e8c483e60d8385448a6a6afd9e513045b87699ae2e71faeb32d6
base64: cx49cjC8ctKtMzwJGBY853itZeb6qxzXGvuUJkbWTGn5VXAFbAwXGEOxU2Qksoj+aM2GWPhc1O7mmkyohXMsQw==
  • 알고리즘

    • pbkdf2, bcrypt, scrypt

    • pbkdf2 설명

      • 기존 문자열에 salt라고 불리는 문자열을 붙인 후 해시 알고리즘을 반복해서 적용하는 알고리즘이다.

      • 먼저 randomBytes() 메소드로 64바이트 길이 문자열을 만든다. ⇒ salt

      • pbkdf2() 메소드에 순서대로 비밀번호 / salt / 반복 횟수 / 출력 바이트 / 해시 알고리즘을 인수로 입력한다.

    • 예시 코드 2

      // 3_5_5_1_pbkdf2.js
      const crypto = 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'));
          });
      });
      // console
      > node 3_5_5_1_pbkdf2.js
      salt: pt0VZ18HqnwDIw9iZA5VflI+/pCgi23s6StPq7qC+BxvFrwDlFguqV3humZoJxGnyCFdW+K0GC4juTaypfC46A==
      password: IxPa382uro2oBuA6MsfNasGg0W1vCd4bLGVR8l24bKX4hWvnJSocDbn9woQUtdn5OYptwL/HqQfQ2Zg0dY1Y0g==

양방향 암호화

  • 암호화된 문자열을 복호화할 수 있으며, 키(열쇠)라는 것이 사용된다.

  • 메소드

    • crypto.createCipheriv(알고리즘, 키, iv)

      • 암호화 알고리즘과 키, iv를 넣는다.

      • iv : 암호화할 때 사용하는 초기화 벡터

      • AES 암호화에 대한 공부가 필요하다.

    • cipher.update(문자열, 인코딩, 출력 인코딩)

      • 암호화할 대상과 대상의 인코딩, 출력 결과물의 인코딩을 넣는다.
    • cipher.final(출력 인코딩)

      • 출력 결과물의 인코딩을 넣어 암호화를 완료한다.
    • crypto.createDecipheriv(알고리즘, 키, iv)

      • 복호화할 때 사용한다.

      • 암호화할 때 사용한 정보를 그대로 넣어야한다.

    • decipher.update(문자열, 인코딩, 출력 인코딩)

      • 암호화된 문장, 문장의 인코딩, 복호화할 인코딩을 넣는다.
    • decipher.final(출력 인코딩)

      • 복호화 결과물의 인코딩을 넣어 복호화를 완료한다.
  • 예시 코드

// 3_5_5_2_cipher.js
const crypto = require('crypto');

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

const cipher = crypto.createCipheriv(algorithm, key, iv);
let result = cipher.update('암호화할 문장', 'utf8', 'base64');
result += cipher.final('base64');
console.log('암호화:', result);

const decipher = crypto.createDecipheriv(algorithm, key, iv);
let result2 = decipher.update(result, 'base64', 'utf8');
result2 += decipher.final('utf8');
console.log('복호화:', result2);
// console
> node 3_5_5_2_cipher.js
암호화: iiopeG2GsYlk6ccoBoFvEH2EBDMWv1kK9bNuDjYxiN0=
복호화: 암호화할 문장

util

  • 각종 편의 기능을 모아둔 모듈

  • 메소드

    • util.deprecate

      • 함수가 deprecated 처리되었음을 알린다.
    • util.promisify

      • 콜백 패턴을 프로미스 패턴으로 바꾼다.
  • deprecated

    • 중요도가 떨어져 더 이상 사용되지 않고 앞으로는 사라지게 될 것이라는 의미

    • 새로운 기능이 기존 기능보다 더 좋을 때, 레거시 기능인 기존 기능을 보통 이 처리를 한다.

http 모듈로 서버 만들기

  • 자료 : 교과서 “Node.js 교과서” ch. 4 http 모듈로 서버 만들기 p. 169

  • 실제 서버 동작에 필요한 쿠키와 세션 처리, 요청 주소별 라우팅 방법

요청과 응답 이해하기

  • p. 170 ~ 178

  • 클라이언트에서 서버요청(request)

  • 서버는 요청의 내용을 읽고 처리, 클라이언트응답(response)

    • 서버에는 요청을 받는 부분응답을 보내는 부분이 필요

    • 요청과 응답 ⇒ 이벤트 방식

    • 이벤트 리스너 등록

  • http 서버가 있어야 웹 브라우저의 요청을 처리 가능
    ⇒ http 모듈 사용

  • http 모듈 createServer 메소드

    • 인수로 콜백 함수 → 요청이 들어올 때마다 콜백 함수 실행 → 콜백 함수에 응답을 작성

    • 콜백 부분의 req, res 매개변수

      • req

        • 요청, request의 준말 - 물론 변수이므로 이름 변경 가능

        • 요청에 관한 정보

      • res

        • 응답, response의 준말 - 물론 변수이므로 이름 변경 가능

        • 응답에 관한 정보

        • 메소드

          • res.writeHead

            • 응답에 대한 정보를 기록

            • 인수

              1. HTTP 상태

                • 아래 예시 코드1의 경우, 200 (성공적인 요청)
              2. 콘텐츠의 형식

                • 아래 예시 코드1의 경우, HTML 형식 / 유니코드 인코딩 utf-8
            • 이 정보가 기록되는 부분 : 헤더(Header)

          • res.write

            • 데이터 전송

            • 인수

              1. 클라이언트로 보낼 데이터

                • 문자열 혹은 버퍼
            • 여러 번 호출해 데이터를 여러 개 전송하는 것도 가능

            • 데이터가 기록되는 부분 : 본문(Body)

          • res.end

            • 응답을 종료

            • 인수가 있다면 클라이언트로 보내고 종료

  • 예시 코드 1

// 4_1_server1.js
const http = require('http');

http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  res.write('<h1>Hello Node!</h1>');
  res.end('<p>Hello Server!</p>');
})
  .listen(8080, () => { // 서버 연결
  console.log('8080번 포트에서 서버 대기 중입니다!')
})

http://localhost:8080 혹은 http://127.0.0.1:8080에 접속

localhost, IP 주소, 포트

localhost현재 컴퓨터의 내부 주소. 127.0.0.1도 동일.
위와 같은 숫자 주소를 IP(Internet Protocol)이라 한다.

포트 : 서버 내의 프로세스 구분 번호. IP 주소 뒤에 콜론(:)과 함께 붙여 사용

  • 유명한 포트 번호
    1. 21 : FTP
    2. 80 : HTTP
    3. 443 : HTTPS
    4. 3306 : MYSQL
  • 보통 사이트들은 포트 번호를 따로 표시하지 않는다. http, https의 포트 80, 443을 생략
  • 한 포트에 한 서비스 - 두 서비스가 같은 포트를 사용하면 에러 발생
  • 리눅스와 맥은 1024번 이하 포트에 연결할 때 관리자 권한 필요 → 명령어 앞에 sudo를 붙인다.
    node server1 → sudo node server1
  • 예시 코드 2

    • listen 메소드에 콜백 함수를 넣는 대신 서버에 listening 이벤트 리스너를 붙이고 error 이벤트 리스너 추가
// 4_1_server1-1.js
const http = require('http');

const server = http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    res.write('<h1>Hello Node!</h1>');
    res.end('<p>Hello Server!</p>');
})
server.listen(8080); // 서버 연결

server.on('listening', () => {
    console.log('8080번 포트에서 서버 대기 중입니다!');
});
server.on('error', (error) => {
    console.error(error);
});
  • 예시 코드 3

    • 한 번에 여러 서버도 실행 가능

    • 서로 다른 포트를 사용해 포트 충돌 회피

// 4_1_server1-2.js
const http = require('http');

http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    res.write('<h1>Hello Node!</h1>');
    res.end('<p>Hello Server1!</p>');
})
    .listen(8080, () => { // 서버 연결
        console.log('8080번 포트에서 서버 대기 중입니다!')
    })

http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    res.write('<h1>Hello Node!</h1>');
    res.end('<p>Hello Server2!</p>');
})
    .listen(8081, () => { // 서버 연결
        console.log('8081번 포트에서 서버 대기 중입니다!')
    })
  • 예시 코드 4

    • HTML 파일을 fs 모듈로 읽어 전송

      • data 변수에 저장된 버퍼로 전송
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Node.js 웹 서버</title>
    </head>
    <body>
        <h1>Node.js 웹 서버</h1>
        <p>만들 준비되셨나요?</p>
    </body>
</html>
const http = require('http');
const fs = require('fs').promises

http.createServer(async (req, res) => {
    try {
        const data = await fs.readFile('./4_1_server2.html');
        res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
        res.end(data);
    } catch (err) {
        console.error(err);
        res.writeHead(500, {'Content-Type': 'text/plain; charset=utf-8' });
        res.end(err.message);
    }
})
    .listen(8081, () => { // 서버 연결
        console.log('8081번 포트에서 서버 대기 중입니다!')
    })

HTTP 상태 코드

  • 브라우저는 서버에서 보내주는 상태 코드로 요청이 성공 여부를 판단
  • 2XX : 성공
    • 200 성공
    • 201 작성됨
  • 3XX : 리다이렉션(다른 페이지로 이동). 어떤 주소를 입력했는데 다른 주소의 페이지로 넘어갈 때
    • 301 (영구 이동)
    • 302 (임시 이동)
    • 304 (수정되지 않음)
  • 4XX : 요청 오류
    • 400 (잘못된 요청)
    • 401 (권한 없음)
    • 403 (금지됨)
    • 404 (찾을 수 없음)
  • 5XX : 서버 오류
    • 500 (내부 서버 오류)
    • 502 (불량 게이트웨이)
    • 503 (서비스를 사용할 수 없음)
  • 응답은 반드시 이루어져야 한다

    • 응답요청이 마무리 되었음을 알린다

    • 응답이 없으면 클라이언트는 서버로부터의 응답만 기다리다가 Timeout(시간 초과) 처리한다.

0개의 댓글