Node.js는 크롬 V8 자바스크립트 엔진으로 빌드된 자바스크립트 런타임입니다.

 - 서버의 역할도 수행할 수 있는 자바스크립트 런타임

 - 노드는 자바스크립트로 작성된 서버를 실행할 수 있음.

 - 서버 실행을 위해 필요한 http/https/http2 모듈을 제공

 

노드 : 자바스크립트 런타임

 - 런타임 : 특정 언어로 만든 프로그램을 실행할 수 있게 해주는 가상 머신(크롬의 V8 엔진사용)의 상태

 - 노드 : 자바스크립트로 만든 프로그램들을 실행할 수 있게 해 줌

 - 다른 런타임으로는 웹 브라우저(크롬, 엣지, 사파리, 파이어폭스 등)가 있음

 

노드는 V8과 libuv를 내부적으로 포함

 - V8 엔진 :오픈 소스 자바스크립트 엔진 -> 속도 문제 개선

 - libuv : 노드의 특성인 이벤트 기반, 논블로킹 I/O 모델을 구현한 라이브러리

 

 

'Javascript > Node' 카테고리의 다른 글

서버 노드  (0) 2024.10.28
노드의 특성  (0) 2024.10.27
노드 교과서 섹션0  (0) 2023.09.07
노드 교과서 섹션 1  (0) 2023.09.06
노드 교과서 섹션 2  (0) 2023.09.05

서버와 클라이언트의 관계

  • 클라이언트가 서버로 요청(request)을 보냄
  • 서버는 요청을 처리
  • 처리 후 클라이언트를 응답(response)을 보냄

http 요청에 응답하는 노드 서버

  • createServer로 요청 이벤트에 대기
  • req 객체는 요청에 관한 정보가, res 객체는 응답에 관한 정보가 담겨 있음

포트는 서버 내에서 프로세스를 구분하는 번호

  • https 는 기본 port 443 (생략할 수 있다.)
  • http 는 기본 port 80 (생략할 수 있다.)
  • 예) www.github.com:80www.github.com
  • 다른 포트로 데이터베이스나 다른 서버 동시에 연결 가능
//server1.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.write('<p>Hello server</p>');
    res.end('<p>Hello ZeroCho<p>');
}).listen(8080);
server.on('listening',()=>{
    console.log('8080번 포트에서 서버 대기 중입니다.');
});
server.on('error', (error)=> {
    console.error(error);
})

 

localhost:8080 으로 접속하면 아래와 같은 결과를 얻을 수 있다.

Hello Node!

Hello server

Hello ZeroCho

 

코드를 수정하였으면 한 번 껐다가 켜 주어야 수정이 된다.

포트 다르게 하면 동시에 서버를 추가로 기동할 수 있다.

 

아래와 같이 html 파일을 읽어서 서버를 기동할 수도 있다.

 

//server2.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Node.js 웹서버</title>
</head>
<body>
    <h1>Noe.js 웹 서버</h1>
    <p>만들 준비 되셨나요?</p>
</body>
</html>

//server2.js
const http = require('http');
const fs = require('fs').promises;

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

 

localhost:8080 으로 접속하면 아래와 같은 결과를 얻을 수 있다.

Noe.js 웹 서버

만들 준비 되셨나요?

REST API(Representational State Transfer)

  • 서버의 자원을 정의하고 자원에 대한 주소를 지정하는 방법
  • /user이면 사용자 정보에 관한 정보를 요청하는 것
  • /post 면 게시글에 관련된 자원을 요청하는 것

HTTP 요청 메서드

  • GET : 서버 자원을 가져오려고 할 때 사용
  • POST : 서버에 자원을 새로 등록하고자 할 때 사용(또는 뭘 써야할 지 애매할 때)
  • PUT : 서버의 자원을 요청에 들어있는 자원으로 치환하고자 할 때 사용
  • PATCH : 서버 자원의 일부만 수정하고자 할 때 사용
  • DELETE : 서버의 자원을 삭제하고자 할 때 사용

서버 주소 뒤에 아무 것도 입력하지 않으면 GET 요청을 보내겠다는 뜻이다.

HTTP 상태 코드 에서 201은 생성 되었다는 의미가 있다.

 

GET, POST, PUT, DELETE 요청 보내기

//restServer.js
const http = require('http');
const fs = require('fs').promises;
const path = require('path');

const users = {}; // 데이터 저장용

http.createServer(async (req, res) => {
  try {
    if (req.method === 'GET') {
      if (req.url === '/') {
        const data = await fs.readFile(path.join(__dirname, 'restFront.html'));
        res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
        return res.end(data);
      } else if (req.url === '/about') {
        const data = await fs.readFile(path.join(__dirname, 'about.html'));
        res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
        return res.end(data);
      } else if (req.url === '/users') {
        res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
        return res.end(JSON.stringify(users));
      }
      // /도 /about도 /users도 아니면
      try {
        const data = await fs.readFile(path.join(__dirname, req.url));
        return res.end(data);
      } catch (err) {
        // 주소에 해당하는 라우트를 못 찾았다는 404 Not Found error 발생
      }
    } else if (req.method === 'POST') {
      if (req.url === '/user') {
        let body = '';
        // 요청의 body를 stream 형식으로 받음
        req.on('data', (data) => {
          body += data;
        });
        // 요청의 body를 다 받은 후 실행됨
        return req.on('end', () => {
          console.log('POST 본문(Body):', body);
          const { name } = JSON.parse(body);
          const id = Date.now();
          users[id] = name;
          res.writeHead(201, { 'Content-Type': 'text/plain; charset=utf-8' });
          res.end('등록 성공');
        });
      }
    } else if (req.method === 'PUT') {
      if (req.url.startsWith('/user/')) {
        const key = req.url.split('/')[2];
        let body = '';
        req.on('data', (data) => {
          body += data;
        });
        return req.on('end', () => {
          console.log('PUT 본문(Body):', body);
          users[key] = JSON.parse(body).name;
          res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
          return res.end(JSON.stringify(users));
        });
      }
    } else if (req.method === 'DELETE') {
      if (req.url.startsWith('/user/')) {
        const key = req.url.split('/')[2];
        delete users[key];
        res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
        return res.end(JSON.stringify(users));
      }
    }
    res.writeHead(404);
    return res.end('NOT FOUND');
  } catch (err) {
    console.error(err);
    res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
    res.end(err.message);
  }
})
  .listen(8082, () => {
    console.log('8082번 포트에서 서버 대기 중입니다');
  });

 

<쿠키의 필요성>

요청에는 한 가지 단점이 있음

  • 누가 요청을 보냈는지 모름(IP 주소와 브라우저 정보 정도만 앎)
  • 로그인을 구현하면 됨
  • 쿠키와 세션이 필요

쿠키 : 키=값의 쌍

  • name=zerocho
  • 매 요청마다 서버에 동봉해서 보냄
  • 서버는 쿠키를 읽어 누구인지 파악

쿠키 넣는 것을 직접 구현

  • writeHead: 요청 헤더에 입력하는 메서드
  • set-Cookie : 브라우저에게 쿠키를 설정하라고 명령

처음에 접속할 때 set-Cookie 를 통해서 쿠키를 생성한 후에

새로고침을 하게 되면 쿠키를 갖고 접속하게 된다.

개발자도구 Network 탭에서 Request Headers 에서 Cookie 에 입력되어서 브라우저에서 보내준다.

(처음에는 없었음)

 

//cookie.js
const http  = require('http');

http.createServer((req, res)=>{
    console.log(req.url, req.headers.cookie);
    res.writeHead(200, {'Set-Cookie':'mycookie=test'});
    res.end('Hello Cookie');
})
.listen(8083, ()=>{
    console.log('8083번 포트에서 서버 대기 중입니다.')
})

//실행 결과 값
D:\code\node\4.3>node cookie.js
8083 포트에서 서버 대기 중입니다.
/ undefined   <-- 처음은 쿠키가 없음
/favicon.ico mycookie=test
/ mycookie=test <-- 새로고침 하면 쿠키가 입력되어서 오게 된다.
/favicon.ico mycookie=test

 

 

http 요청과 응답은 헤더와 본문을 가짐

  • 헤더는 요청 또는 응답에 대한 정보를 가짐
  • 본문은 주고받는 실제 데이터
  • 쿠키는 부가적인 정보이므로 헤더에 저장

< 세션 사용하기 >

쿠키의 정보는 노출되고 수정되는 위험이 있음

  • 중요한 정보는 서버에서 관리되고 클라이언트에는 세션 키만 제공
  • 서버에 세션 객체(session)생성 후, uniqueInt(키)를 만들어 속성명으로 사용
  • 속성 값에 정보 저장하고 uniqueInt를 클라이언트에 보냄

https

웹 서버에 SSL 암호화를 추가하는 모듈

  • 오고 가는 데이터를 암호화해서 중간에 다른 사람이 요청을 가로채더라도 내용을 확인할 수 없음
  • 요즘에는 https 적용이 필수(개인 정보가 있는 곳은 특히)
//server1-3.js

const https = require('https');
const fs = require('fs');

https.createServer({
  cert: fs.readFileSync('도메인 인증서 경로'),
  key: fs.readFileSync('도메인 비밀키 경로'),
  ca: [
    fs.readFileSync('상위 인증서 경로'),
    fs.readFileSync('상위 인증서 경로'),
  ],
}, (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(443, () => {
    console.log('443번 포트에서 서버 대기 중입니다!');
  });

 

인증기관에서 인증서를 받아야 한다.

https 는 port를 443으로 생략이 가능하다.

http2

SSL 암호화와 더불어 최신 HTTP 프로토콜인 http/2를 사용하는 모듈

  • 요청 및 응답 방식이 기존 http/1.1 보다 개선됨
  • 웹의 속도도 개선됨
//server1-4.js
const http2 = require('http2');
const fs = require('fs');

http2.createSecureServer({
  cert: fs.readFileSync('도메인 인증서 경로'),
  key: fs.readFileSync('도메인 비밀키 경로'),
  ca: [
    fs.readFileSync('상위 인증서 경로'),
    fs.readFileSync('상위 인증서 경로'),
  ],
}, (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(443, () => {
    console.log('443번 포트에서 서버 대기 중입니다!');
  });

 

cluster

실무에서는 http2 적용하면서 cluster 도 같이 적용하는 것이 좋다.

기본적으로 싱글 스레드인 노드가 CPU 코어를 모두 사용할 수 있게 해주는 모듈

  • 포트를 공유하는 노드 프로세스를 여러 개 둘 수 있음
  • 요청이 많이 들어왔을 때 병렬로 실행된 서버의 개수만큼 요청이 분산됨
  • 서버에 무리가 덜 감
  • 코어가 8개인 서버가 있을 때 : 보통은 코어 하나만 활용
  • cluster로 코어 하나당 노드 프로세스 하나를 배정 가능
  • 성능이 8배가 되는 것은 아니지만 개선됨
  • 단점 : 컴퓨터 자원(메모리, 세션 등) 공유 못함
  • Redis 등 별도 서버로 해결
//cluster.js
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`마스터 프로세스 아이디: ${process.pid}`);
  // CPU 개수만큼 워커를 생산
  for (let i = 0; i < numCPUs; i += 1) {
    cluster.fork();
  }
  // 워커가 종료되었을 때
  cluster.on('exit', (worker, code, signal) => {
    console.log(`${worker.process.pid}번 워커가 종료되었습니다.`);
    console.log('code', code, 'signal', signal);
//    cluster.fork();  주석 처리 하고 코어개수만큼 접속하면 코어개수 넘어갔을 때 서버가 연결이 안됨
// 주석 풀면 코어개수 이상만큼 접속해도 계속 프로세스 띄우기 때문에 서버 연결이 가능함
  });
} else {
  // 워커들이 포트에서 대기
  http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    res.write('<h1>Hello Node!</h1>');
    res.end('<p>Hello Cluster!</p>');
    setTimeout(() => { // 워커 존재를 확인하기 위해 1초마다 강제 종료
      process.exit(1);
    }, 1000);
  }).listen(8086);

  console.log(`${process.pid}번 워커 실행`);
}

//실행결과
D:\code\node\4.5>node cluster.js
마스터 프로세스 아이디: 19868
19928 워커 실행
19960 워커 실행
19988 워커 실행
19968 워커 실행
20020 워커 실행
20036 워커 실행
20068 워커 실행
20168 워커 실행
20128 워커 실행
20220 워커 실행
20088 워커 실행
20292 워커 실행
// 코어개수만큼 접속하면 코어개수 넘어갔을 때 서버가 연결이 안됨
20088 워커가 종료되었습니다.
code 1 signal null
20292 워커가 종료되었습니다.
code 1 signal null
20220 워커가 종료되었습니다.
code 1 signal null
20168 워커가 종료되었습니다.
code 1 signal null
20128 워커가 종료되었습니다.
code 1 signal null
20068 워커가 종료되었습니다.
code 1 signal null
20036 워커가 종료되었습니다.
code 1 signal null
20020 워커가 종료되었습니다.
code 1 signal null
19928 워커가 종료되었습니다.
code 1 signal null
19968 워커가 종료되었습니다.
code 1 signal null
19960 워커가 종료되었습니다.
code 1 signal null
19988 워커가 종료되었습니다.
code 1 signal null

 

'Javascript > Node' 카테고리의 다른 글

노드 교과서 섹션 1  (0) 2023.09.06
노드 교과서 섹션 2  (0) 2023.09.05
노드 교과서 섹션 4  (0) 2023.08.31
노드 교과서 섹션 5  (0) 2023.08.29
노드 교과서 섹션 6  (0) 2023.08.27

npm이란

Node Package Manger

  • 노드의 패키지 매니저
  • 다른 사람들이 만든 소스 코드들을 모아둔 저장소
  • 남의 코드를 사용하여 프로그래밍 기능
  • 이미 있는 기능을 다시 구현할 필요가 없어 효율적
  • 오픈 소스 생태계를 구성중
  • 패키지 : npm에 업로드된 노드 모듈
  • 모듈이 다른 모듈을 사용할 수 있듯 패키지도 다른 패키지를 사용할 수 있음
  • 의존 관계라고 부름

package.json

현재 프로젝트에 대한 정보와 사용 중인 패키지에 대한 정보를 담은 파일

  • 같은 패키지라도 버전별로 기능이 다를 수 있으므로 버전을 기록해두어야 함
  • 동일한 버전을 설치하지 않으면 문제가 생길 수 있음
  • 노드 프로젝트 시작 전 package.json 부터 만들고 시작함(npm init)
D:\code\node\publish>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (publish) npmtest
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author: hd
license: (ISC) MIT
About to write to D:\code\node\publish\package.json:

{
  "name": "npmtest",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "hd",
  "license": "MIT",
  "description": ""
}


Is this OK? (yes) yes
// npm init 완료 하면 package.json 파일이 생성됨

// package.json
{
  "name": "npmtest",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "hd",
  "license": "MIT",
  "description": ""
}

 

>> npm run [스크립트명]으로 스크립트 생성

 

D:\code\node\publish>npm run test

> npmtest@1.0.0 test
> echo "Error: no test specified" && exit 1   //에러를 발생시키면서 프로세스를 종료

"Error: no test specified"

 

// 아래와 같이 설치를 하면 package.json 에 dependencies 에 버전이 표시된다.
// node_modules 디렉토리가 생성되면서 스크립트들이 생긴다.
D:\code\node\publish>npm i express

added 64 packages, and audited 65 packages in 8s

12 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

//package.json
{
  "name": "npmtest",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "hd",
  "license": "MIT",
  "description": "",
  "dependencies": {
    "express": "^4.19.2"
  }
}

 

// 아래와 같이 -D 옵션으로 설치를 하면 package.json 에 devDependencies에 버전이 표시된다.
// devDependencies는 개발할 때만 쓰이는 패키지들이다.
D:\code\node\publish>npm i -D nodemon

added 29 packages, and audited 94 packages in 6s

16 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

//package.json
  "devDependencies": {
    "nodemon": "^3.1.4"
  }

node_modules 크기가 크기 때문에 지워서 배포를 한다.(package.json 만 가지고 있다가 설치를 한다.)

npm i -g rimraf (-g는 글로벌 설치, 글로벌 설치는 dependencies 에 기록되지 않는다.)

npm i rimraf -D

npx 를 사용하면 글로벌 설치한 것처럼 사용이 가능하다.

글로벌 설치는 최대한 기피하는 것이 좋다.

SemVer 버저닝

노드 패키지의 버전은 SermVer(유의적 버저닝)방식을 따름

  • Major(주 버전), Minor(부 버전), Patch(수 버전)
  • 노드에서는 배포를 할 때 항상 버전을 올려야 함
  • Major는 하위 버전과 호환되지 않은 수정 사항이 생겼을 때 올림
  • Minor는 하위 버전과 호환되는 수정 사항이 생겼을 때 올림
  • Patch는 기능에 버그를 해결했을 때 올림

예) 1.0.7 ←- 1 : Major

        ←- 0 : Minor

              ←- 7 : Patch

버전 앞에 기호를 붙여 의미를 더함

dependencies에서 아래와 같이 고정된다는 뜻이다.

^1.0.7 : Major까지 고정

~1.0.7 : Minor까지 고정

1.0.7 : Patch까지 고정

  • @latest는 최신을 의미
  • @next로 가장 최신 배포판 사용 가능(불안정함)
  • 알파/베타/RC 버전이 존재할 수 있음

npm i express@latest ← 최신 버전 설치

npm i express@3 ← Major 3인 버전 설치

npm i express@3.5.1 ← 3.5.1 정확한 버전 설치

npm i express@next ← 가장 최신 배포판

 

npm outdated : 어떤 패키지에 기능 변화가 생겼는지 알 수 있음

npm uninstall 패키지명 : 패키지 삭제 (npm rm 패키지명 으로도 가능)

npm search 패키지명 : npm 패키지를 검색할 수 있음

npm info 패키지명 : 패키지의 세부 정보 파악 가능

D:\code\node\publish>npm search express

express
Fast, unopinionated, minimalist web framework
Version 4.19.2 published 2024-03-25 by wesleytodd
Maintainers: dougwilson linusu sheplu blakeembrey ulisesgascon wesleytodd mikeal
Keywords: express framework sinatra web http rest restful router app api
https://npm.im/express

D:\code\node\publish>npm info express

express@4.19.2 | MIT | deps: 31 | versions: 276
Fast, unopinionated, minimalist web framework
http://expressjs.com/


npm publish : 자신이 만든 패키지를 배포

npm unpublish : 자신이 만든 패키지를 배포 중단(배포 후 24시간 내에만 가능)

https://docs.npmjs.com/cli/v9/commands ←명령어 바뀌는 경우 있으니 사이트 참조

 

'Javascript > Node' 카테고리의 다른 글

노드 교과서 섹션 2  (0) 2023.09.05
노드 교과서 섹션 3  (0) 2023.09.01
노드 교과서 섹션 5  (0) 2023.08.29
노드 교과서 섹션 6  (0) 2023.08.27
노드 교과서 섹션 8  (0) 2023.08.23

express 와 nodemon 를 npm i 로 설치하고 아래와 같이 실행하면 익스프레스 서버가 실행된다.

 

// app.js

const express = require('express');

const app = express();

app.set('port', process.env.PORT || 3000);
app.get('/', (req, res)=>{
    res.send('hello express!');
});

app.listen(app.get('port'), ()=> {
    console.log('익스프레스 서버 실행');
});

 


// nodemon 으로 실행하면  파일에 변화가 있을 때마다 재기동을 해준다.
D:\code\node\6.1>nodemon app
[nodemon] 3.1.4
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node app.js`
익스프레스 서버 실행
[nodemon] restarting due to changes...
[nodemon] starting `node app.js`
익스프레스 서버 실행

// package.json 에서 scripts에 아래와 같이 되어 있으면 npm start 로도 실행이 가능하다.
  "scripts": {
    "start": "nodemon app"
  },

//실행 결과
D:\code\node\6.1>npm start

> express_test@1.0.0 start
> nodemon app

[nodemon] 3.1.4
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node app.js`
익스프레스 서버 실행

 

express 에서 html 파일 불러오기

 

//app.js
const express = require('express');
const path = require('path');
const app = express();

app.set('port', process.env.PORT || 3000);
app.get('/', (req, res)=>{
    res.sendFile(path.join(__dirname,'index.html'));
});

app.listen(app.get('port'), ()=> {
    console.log('익스프레스 서버 실행');
});

 

nodemon은 html 파일을 감시하지 않는다.

 

와일드카드는 보통 다른 라우터들보다 아래에 위치해야 정상적으로 작동된다.

미들웨어는 next()를 해주어야 그 다음 미들웨어가 실행된다.

미들웨어는 여러 개를 같이 사용도 가능하다.

에러 미들웨어는 반드시 next까지 4개가 다 들어있어야 한다.

서버가 클라이언트에게 status code를 마음대로 보낼 수가 있다.(에러가 발생해도)

 

res.json 은 응답을 보낼 뿐이지 return을 하는 것이 아니다.

 

// morgan cookie-parser express-session 설치
D:\code\node\6.1>npm i morgan cookie-parser express-session

added 11 packages, and audited 105 packages in 1s

16 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

 

morgan을 설치하고 실행한 후에 접속하면 비슷한 것 같지만 한 줄이 추가된다.

클라이언트에서 어떤 요청이 왔는지 서버에 기록된다.

얼마나 속도가 걸렸는지, 용량은 얼만큼인지(아래는 173 byte) , status code 도 같이 기록된다.

 

// 클라이언트에서 어떤 요청 왔는지 기록됨.
D:\code\node\6.1>npm start

> express_test@1.0.0 start
> nodemon app

[nodemon] 3.1.4
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node app.js`
익스프레스 서버 실행
모든 요청에 실행하고 싶어요.
GET / 200 10.919 ms - 173
모든 요청에 실행하고 싶어요.
GET /favicon.ico 404 1.772 ms - 14

 

// 개발시에는 dev
app.use(morgan('dev'));
//  실무에서는 combined 사용, 조금 더 자세해진다. ip , 시간, 요청 , 브라우저 등등 알 수 있다.
app.use(morgan('combined'));

 

app.use(cookieParser('zerochopassword'));
app.get('/', (req, res)=>{
    req.cookies // {mycookie : 'test}
    req.signedCookies; // 서명된 쿠키
    // 쿠키 생성 (name deprecated)
    res.cookie('name', encodeURIComponent(name), {
        expires: new Date(),
        httpOnly : true,
        path : '/',
    })
    // 쿠키 삭제 (name deprecated)
    res.clearCookie('name', encodeURIComponent(name),{
        httpOnly: true,
        path : '/',
    })
});

 

// bodyparser 가 express 안에 있다.
app.use(express.json()); // 클라이언트에서 json 데이터를 보냈을 때 파싱
app.use(express.urlencoded({extended: true})); //클라이언트에서 폼 데이터 보냈을 때 파싱
//extend가 true 면 qs, false 면 querystring (qs를 추천)

 

// 정적파일 처리
// app.use('요청경로',express.static('실제 경로'));
app.use('/',express.static(__dirname, 'public-3030')); // public은 유명하니깐 다른 것을 붙이는 것이 보안에 좋다.
//localhost:3000/zerocho.html         d:\code\node\6.1\public-3030\zerocho.html
//localhost:3000/hello.css         d:\code\node\6.1\public-3030\hello.css

 

'Javascript > Node' 카테고리의 다른 글

노드 교과서 섹션 3  (0) 2023.09.01
노드 교과서 섹션 4  (0) 2023.08.31
노드 교과서 섹션 6  (0) 2023.08.27
노드 교과서 섹션 8  (0) 2023.08.23
노드 교과서 섹션 9  (0) 2023.08.22

MySQL과 같은 SQL 데이터베이스와는 다른 유형의 데이터

  • NoSQL의 대표주자인 mongoDB(몽고디비)사용
SQL(MySQL) NoSQL(몽고디비)
규칙에 맞는 데이터 입력 테이블 간 JOIN 지원 안정성, 일관성 용어(테이블, 로우, 칼럼) 자유로운 데이터 입력 컬렉션 간 JOIN 미지원 확장성, 가용성 용어(컬렉션, 다큐먼트, 필드)

 

  • JOIN : 관계가 있는 테이블끼리 데이터를 합치는 기능(몽고디비 aggregate 흉내 가능)
  • 빅데이터, 메시징, 세션 관리 등(비정형 데이터)에는 몽고디비 사용하면 좋음
//몽고디비 실행 (아래 경로에서 shift 누르고 오른쪽 마우스 눌러서 powershell 실행
PS C:\Program Files\MongoDB\Server\7.0\bin> ./mongod --ipv6
{"t":{"$date":"2024-08-14T22:56:08.454+09:00"},"s":"I",  "c":"NETWORK",  "id":4915701, "ctx":"thread1","msg":"Initialized wire specification","attr":{"spec":{"incomingExternalClient":{"minWireVersion":0,"maxWireVersion":21},"incomingInternalClient":{"minWireVersion":0,"maxWireVersion":21},"outgoing":{"minWireVersion":6,"maxWireVersion":21},"isInternalClient":true}}}

 

//몽고쉘 실행
Please enter a MongoDB connection string (Default: mongodb://localhost/):

Current Mongosh Log ID: 66bcb84be9c5ca3e0a228fb4
Connecting to:          mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.2.15
Using MongoDB:          7.0.12
Using Mongosh:          2.2.15

For mongosh info see: https://docs.mongodb.com/mongodb-shell/


To help improve our products, anonymous usage data is collected and sent to MongoDB periodically (https://www.mongodb.com/legal/privacy-policy).
You can opt-out by running the disableTelemetry() command.

------
   The server generated these startup warnings when booting
   2024-08-14T22:56:09.973+09:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
   2024-08-14T22:56:09.973+09:00: This server is bound to localhost. Remote systems will be unable to connect to this server. Start the server with --bind_ip <address> to specify which IP addresses it should serve responses from, or with --bind_ip_all to bind to all interfaces. If this behavior is desired, start the server with --bind_ip 127.0.0.1 to disable this warning
------

test>
// admin 으로 변경
test> use admin
switched to db admin
// root 계정 생성
admin> db.createUser({user:'root', pwd:'manager', roles: ['root']})
{ ok: 1 }

 

mongod 있는 화면 끄고 다시 재실행

// --auth 붙여서 다시 실행
PS C:\Program Files\MongoDB\Server\7.0\bin> ./mongod --ipv6 --auth
{"t":{"$date":"2024-08-14T23:07:03.228+09:00"},"s":"I",  "c":"NETWORK",  "id":4915701, "ctx":"thread1","msg":"Initialized wire specification","attr":{"spec":{"incomingExternalClient":{"minWireVersion":0,"maxWireVersion":21},"incomingInternalClient":{"minWireVersion":0,"maxWireVersion":21},"outgoing":{"minWireVersion":6,"maxWireVersion":21},"isInternalClient":true}}}

 

//몽고쉘 경로에서 shift 누르고 오른쪽 마우스로 파워셀 열고 아래와 같이  root 계정으로 접속
PS C:\Users\john\Downloads\mongosh-2.2.15-win32-x64\mongosh-2.2.15-win32-x64\bin> ./mongosh admin -u root -p manager
Current Mongosh Log ID: 66bcbaa044900f7385228fb4
Connecting to:          mongodb://<credentials>@127.0.0.1:27017/admin?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.2.15
Using MongoDB:          7.0.12
Using Mongosh:          2.2.15

For mongosh info see: https://docs.mongodb.com/mongodb-shell/

------
   The server generated these startup warnings when booting
   2024-08-14T23:07:03.627+09:00: This server is bound to localhost. Remote systems will be unable to connect to this server. Start the server with --bind_ip <address> to specify which IP addresses it should serve responses from, or with --bind_ip_all to bind to all interfaces. If this behavior is desired, start the server with --bind_ip 127.0.0.1 to disable this warning
------

admin>

 

컬렉션 생성하기

따로 생성할 필요 없음

  • 다큐먼트를 넣는 순간 컬렉션도 자동 생성됨 ( mysql과 비교하며 컬렉션 : 테이블, 다큐먼트 : 데이터 로우)
  • 직접 생성하는 명령어도 있음
// 컬렉션 직접 생성하는 명령어 ( db를 nodejs로  잘 선택해서 생성하기)
admin> use nodejs;
switched to db nodejs
nodejs> show dbs;
admin   132.00 KiB
config  108.00 KiB
local    72.00 KiB
nodejs> db
nodejs
nodejs> db.createCollection('users')
{ ok: 1 }
nodejs> db.createCollection('comments')
{ ok: 1 }
nodejs> show collections
comments
users
nodejs>

 

insert 명령어

 

// insert 한개는 insertOne, insert 여러개는 insertMany
// 몽고디비는 오타 체크를 안하니 주의하기
nodejs> db.users.insertOne({ name : 'zero', age: 24, married: false, comment: '안녕하세요. 간단히 몽고디비 사용 방법에  대해 알아봅시다.', createdAt: new Date() });
{
  acknowledged: true,
  insertedId: ObjectId('66bcbdc744900f7385228fb5')
}
nodejs> db.users.insertOne({ name : 'nero', age : 32, married: true, comment: '안녕하세요. zero 친구 nero입니다.', createdAt: new Date() });
{
  acknowledged: true,
  insertedId: ObjectId('66bcbe7144900f7385228fb6')
}

// 조회하기
nodejs> db.users.find({name:'zero'})
[
  {
    _id: ObjectId('66bcbdc744900f7385228fb5'),
    name: 'zero',
    age: 24,
    married: false,
    comment: '안녕하세요. 간단히 몽고디비 사용 방법에 대해 알아봅시다.',
    createdAt: ISODate('2024-08-14T14:23:03.633Z')
  }
]


// users에 해당하는 ObjectId를 아래와 같이  복사해서 insert 하는 명령어
nodejs> db.comments.insertOne({ commenter: ObjectId('66bcbdc744900f7385228fb5'), comment: '안녕하세요. zero의 댓글입니다.', createdAt: new Date() });
{
  acknowledged: true,
  insertedId: ObjectId('66bcbf5d44900f7385228fb7')
}
nodejs>

 

조회 명령어

// 조회하기 (find는 전체 조회, findOne으로 하나만 조회)
nodejs> db.users.find({});
[
  {
    _id: ObjectId('66bcbdc744900f7385228fb5'),
    name: 'zero',
    age: 24,
    married: false,
    comment: '안녕하세요. 간단히 몽고디비 사용 방법에 대해 알아봅시다.',
    createdAt: ISODate('2024-08-14T14:23:03.633Z')
  },
  {
    _id: ObjectId('66bcbe7144900f7385228fb6'),
    name: 'nero',
    age: 32,
    married: true,
    comment: '안녕하세요. zero 친구 nero입니다.',
    createdAt: ISODate('2024-08-14T14:25:53.749Z')
  }
]
nodejs> db.comments.find({});
[
  {
    _id: ObjectId('66bcbf5d44900f7385228fb7'),
    commenter: ObjectId('66bcbdc744900f7385228fb5'),
    comment: '안녕하세요. zero의 댓글입니다.',
    createdAt: ISODate('2024-08-14T14:29:49.856Z')
  }
]

 

두 번째 인수로 조회할 필드를 선택할 수 있음(1은 추가, 0은 제외)

nodejs> db.users.find({}, {_id : 0, name :1 , married:1 });
[ { name: 'zero', married: false }, { name: 'nero', married: true } ]

 

첫 번째 인수로 조회 조건 입력 가능

  • $gt나 $or같은 조건 연산자 사용
nodejs> db.users.find({ age: {$gt: 30}, married: true }, {_id : 0, name: 1, age: 1 });
[ { name: 'nero', age: 32 } ]
nodejs> db.users.find({ $or: [{ age : {$gt: 30}}, {married : false }]}, {_id : 0, name:1, age: 1});
[ { name: 'zero', age: 24 }, { name: 'nero', age: 32 } ]

 

update 명령어

 

nodejs> db.users.updateOne({name :'nero'}, {$set: { comment : '안녕하세요. 이 필드를 바꿔보겠습니다.'}});
{
  acknowledged: true, //정상반영
  insertedId: null,
  matchedCount: 1,  //하나를 찾아서
  modifiedCount: 1,  // 하나를 변경햇다.
  upsertedCount: 0
}

 

delete 명령어

//delete 명령어
nodejs> db.users.deleteOne({name: 'nero'});
{ acknowledged: true, deletedCount: 1 }

// delete 된 것 확인
nodejs> db.users.find({});
[
  {
    _id: ObjectId('66bcbdc744900f7385228fb5'),
    name: 'zero',
    age: 24,
    married: false,
    comment: '안녕하세요. 간단히 몽고디비 사용 방법에 대해 알아봅시다.',
    createdAt: ISODate('2024-08-14T14:23:03.633Z')
  }
]

 

 

'Javascript > Node' 카테고리의 다른 글

노드 교과서 섹션 4  (0) 2023.08.31
노드 교과서 섹션 5  (0) 2023.08.29
노드 교과서 섹션 8  (0) 2023.08.23
노드 교과서 섹션 9  (0) 2023.08.22
노드 교과서 섹션 10  (0) 2023.08.19

기존에 만들었던 노드버드 서비스가 기동된 상태에서 localhost:8002 로 별도의 api 서버를 만든다. routes 나 controllers 에서 auth 를 제외한 파일들을 지워주고 index.js 를 만들어 준다. 그리고 indexRouter를 생성해준다.

 

//app.js 파일

const express = require('express');
const cookieParser = require('cookie-parser');
const morgan = require('morgan');
const path = require('path');
const session = require('express-session');
const nunjucks = require('nunjucks');
const dotenv = require('dotenv');
const passport = require('passport');

dotenv.config();
const authRouter = require('./routes/auth');
const indexRouter = require('./routes');
const { sequelize } = require('./models');
const passportConfig = require('./passport');

const app = express();
passportConfig(); // 패스포트 설정
app.set('port', process.env.PORT || 8002);
app.set('view engine', 'html');
nunjucks.configure('views', {
  express: app,
  watch: true,
});
sequelize.sync({ force: false })
  .then(() => {
    console.log('데이터베이스 연결 성공');
  })
  .catch((err) => {
    console.error(err);
  });

app.use(morgan('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(session({
  resave: false,
  saveUninitialized: false,
  secret: process.env.COOKIE_SECRET,
  cookie: {
    httpOnly: true,
    secure: false,
  },
}));
app.use(passport.initialize());
app.use(passport.session());


app.use('/auth', authRouter);
app.use('/', indexRouter);

app.use((req, res, next) => {
  const error =  new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
  error.status = 404;
  next(error);
});

app.use((err, req, res, next) => {
  res.locals.message = err.message;
  res.locals.error = process.env.NODE_ENV !== 'production' ? err : {};
  res.status(err.status || 500);
  res.render('error');
});

app.listen(app.get('port'), () => {
  console.log(app.get('port'), '번 포트에서 대기중');
});

 

Domain 테이블을 만들어준다.

 

// models/domain.js 파일

const Sequelize = require("sequelize");

class Domain extends Sequelize.Model {
  static initiate(sequelize) {
    Domain.init(
      {
        host: {
          type: Sequelize.STRING(80),
          allowNull: false,
        },
        type: {
          type: Sequelize.ENUM("free", "premium"),
          allowNull: false,
        },
        clientSecret: {
          type: Sequelize.UUID,
          allowNull: false,
        },
      },
      {
        sequelize,
        timestamps: true,
        paranoid: true,
        modelName: "Domain",
        tableName: "domains",
      }
    );
  }

  static associate(db) {
    db.Domain.belongsTo(db.User);
  }
}

module.exports = Domain;

 

계정은 기존에 8001에서 만들었던 계정을 사용하고 로그인 전 화면과 로그인 후에 화면은 아래와 같다.

 

controllers에서 Domain 만드는 코드까지 작성한 후에 화면은 아래와 같다.

// controllers/index.js 파일

const { User, Domain } = require("../models");
const { v4: uuidv4 } = require("uuid");

exports.renderLogin = async (req, res, next) => {
  try {
    const user = await User.findOne({
      where: { id: req.user?.id || null },
      include: { model: Domain },
    });
    res.render("login", {
      user,
      domains: user?.Domains,
    });
  } catch (err) {
    console.error(err);
    next(err);
  }
};
exports.createDomain = async (req, res, next) => {
  try {
    await Domain.create({
      UserId: req.user.id,
      host: req.body.host,
      type: req.body.type,
      clientSecret: uuidv4(), //랜덤한 uuid를 넣어준다.
    });
    res.redirect("/");
  } catch (err) {
    console.error(err);
    next(err);
  }
};

 

Executing (default): INSERT INTO `domains` (`id`,`host`,`type`,`clientSecret`,`createdAt`,`updatedAt`,`UserId`) VALUES (DEFAULT,?,?,?,?,?,?);

 

토큰이 여러 종류가 있는데 JWT (JasonWebToken) 을 많이 사용한다.

헤더, 페이로드, 시그니처 3종류로 나누어져 있다.

jwt의 단점은 내용물이 들어있기 때문에 용량이 크다.

lecture-call 이라는 클라이언트 역할의 서버와

lecture-api 라는 api를 제공하는 서버의 코드를 작성한다. 작성한 후에 localhost:4000/test 로 접속을 하면 lecture-api 서버에서는 아래와 같이 200 코드가 출력되고 토큰을 클라이언트로 넘겨준다.

 

POST /v1/token 200 30.022 ms - 250
GET /v1/test 200 3.573 ms - 78

 

자기가 작성한 게시글 과 해시태그 검색하는 코드를 작성한 후 결과는 아래와 같습니다.

 

작성한 글 결과 화면

 

해시태그 검색한 결과 화면

 

API 사용량 제한을 위해서는 express-rate-limit 을 설치해줘야 한다.

D:\code\node\lecture-api>npm i express-rate-limit

added 1 package, and audited 300 packages in 3s

41 packages are looking for funding
  run `npm fund` for details
 

 

- v1 버전을 사용했을 때 : call 하는 클라이언트 서버의 request 함수에서 throw error 로 처리하면 아래와 같이 에러코드만 나오고 안내 메세지가 안 나온다.

 


//controller/

const request = async (req, api) => {
    try {
      if (!req.session.jwt) {
        // 세션에 토큰이 없으면
        const tokenResult = await axios.post(`${URL}/token`, {
          clientSecret: process.env.CLIENT_SECRET,
        });
        req.session.jwt = tokenResult.data.token; // 세션에 토큰 저장
      }
      return await axios.get(`${URL}${api}`, {
        headers: { authorization: req.session.jwt },
      }); // API 요청
    } catch (error) {
      if (error.response?.status === 419) {
        // 토큰 만료시 토큰 재발급 받기
        delete req.session.jwt;
        return request(req, api);
      } // 419 외의 다른 에러면
      throw error;
      // return error.response;
    }
  };

 

return error.message 로 변경하면 아래와 같이 안내 메세지가 화면에 출력 된다.

v2로 변경하고 나서는 아래와 같이 정상적으로 호출된다.

 

하지만 1분에 1번만 호출되도록 제한했기 때문에 다시 호출하면 정상 호출 안된다.

 

웹브라우저에서 api를 호출할 때는 아래와 같이 cors 에러가 발생한다. 기존에는 서버에서 직접 api를 호출 했기 때문에 cors 에러가 발생하지 않았다.

 

cors 에러를 해결하기 위해 cors를 설치하고 추가해주면 아래와 같이 해결된다.

D:\code\node\lecture-api>npm i cors

added 1 package, and audited 301 packages in 3s

41 packages are looking for funding
  run `npm fund` for details

 

'Javascript > Node' 카테고리의 다른 글

노드 교과서 섹션 6  (0) 2023.08.27
노드 교과서 섹션 8  (0) 2023.08.23
노드 교과서 섹션 10  (0) 2023.08.19
노드 교과서 섹션 11  (0) 2023.08.18
노드 교과서 섹션 12  (0) 2023.08.17

웹 소켓으로 실시간 데이터 전송하기

WS 모듈 사용하기

필요한 것들을 아래와 같이 설치해준다.

 

D:\code\node\gifchat>npm i cookie-parser dotenv express express-session morgan nunjucks && npm -i D nodemon

added 82 packages, and audited 83 packages in 5s

14 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
Unknown command: "D"

To see a list of supported npm commands, run:
  npm help

 

이번에 사용할 ws 도 추가로 설치해준다.

D:\code\node\gifchat>npm i ws

added 1 package, and audited 84 packages in 1s

14 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

 

웹소켓에는 네 가지 상태가 있다. CONNECTING(연결 중), OPEN(열림), CLOSING(닫는 중), CLOSED(당힘) 이다. OPEN일 때만 에러 없이 메세지를 보낼 수 있다.

보낼 때는 ws.send 로 보내고 받을 때는 ws.on 으로 받는다.

//socket.js 파일

const WebSocket = require("ws");

module.exports = (server) => {
  const wss = new WebSocket.Server({ server });

  wss.on("connection", (ws, req) => {
    // 웹소켓 연결 시
    const ip = req.headers["x-forwarded-for"] || req.socket.remoteAddress;
    console.log("새로운 클라이언트 접속", ip);
    ws.on("message", (message) => {
      // 클라이언트로부터 메시지
      console.log(message.toString());
    });
    ws.on("error", (error) => {
      // 에러 시
      console.error(error);
    });
    ws.on("close", () => {
      // 연결 종료 시
      console.log("클라이언트 접속 해제", ip);
      clearInterval(ws.interval);
    });

    ws.interval = setInterval(() => {
      // 3초마다 클라이언트로 메시지 전송
      if (ws.readyState === ws.OPEN) {
        ws.send("서버에서 클라이언트로 메시지를 보냅니다.");
      }
    }, 3000);
  });
};

 

서버를 실행하면 아래와 같이 서버에서 클라이언트로 메세지를 보내는 것 확인 가능

 

 

서버에서도 확인 가능

 

 

Socket.IO 모듈 사용하기

socket.io에서는 키와 값으로 보낸다.

emit(’키’,’값’)

socket.io는 http로 연결을 한다.

socket.io는 polling 과 websocket 을 둘 다 지원한다.websocket으로만 사용하도록 설정할 수도 있다.

socket.js 와 index.html을 변경하면 아래와 같이 확인이 가능하다.

 

//socket.js

const SocketIO = require("socket.io");

module.exports = (server) => {
  const io = SocketIO(server, { path: "/socket.io" });

  io.on("connection", (socket) => {
    // 웹소켓 연결 시
    const req = socket.request;
    const ip = req.headers["x-forwarded-for"] || req.socket.remoteAddress;
    console.log("새로운 클라이언트 접속!", ip, socket.id, req.ip);
    socket.on("disconnect", () => {
      // 연결 종료 시
      console.log("클라이언트 접속 해제", ip, socket.id);
      clearInterval(socket.interval);
    });
    socket.on("error", (error) => {
      // 에러 시
      console.error(error);
    });
    socket.on("reply", (data) => {
      // 클라이언트로부터 메시지
      console.log(data);
    });
    socket.interval = setInterval(() => {
      // 3초마다 클라이언트로 메시지 전송
      socket.emit("news", "Hello Socket.IO");
    }, 3000);
  });
};


// index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>GIF 채팅방</title>
  </head>
  <body>
    <div>F12를 눌러 console 탭과 network 탭을 확인하세요.</div>
    <script src="/socket.io/socket.io.js"></script>
    <script>
      const socket = io.connect("http://localhost:8005", {
        path: "/socket.io",
        transports: ["websocket"],
      });
      socket.on("news", function (data) {
        console.log(data);
        socket.emit("reply", "Hello Node.JS");
      });
    </script>
  </body>
</html>

 

실시간 채팅방 만들기

몽고디비를 사용한다. 필요한 파일들을 아래과 같이 설치한다.

 

D:\code\node\gifchat>npm i mongoose multer color-hash@2

added 39 packages, and audited 167 packages in 9s

 

몽고디비를 사용해야 하기 때문에 실행시켜준다.

PS C:\Program Files\MongoDB\Server\7.0\bin> ./mongod --ipv6

 

서버를 시작해서 몽고디비까지 접속이 잘 되는 것을 확인다.

D:\code\node\gifchat>npm start

> gif-chat@0.0.1 start
> nodemon app

[nodemon] 2.0.20
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node app.js`
(node:4060) [MONGODB DRIVER] Warning: useNewUrlParser is a deprecated option: useNewUrlParser has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version
(Use `node --trace-warnings ...` to show where the warning was created)
8005 번 포트에서 대기중
몽고디비 연결 성공

 

에러 없이 서버가 잘 실행되면 아래와 같이 2개의 웹브라우저에서 접속해본다.

서버에서도 2개의 세션이 붙은 것을 확인할 수 있다.

 

#c587b0 J5RByMDgnkNN_nux7zrvG6EGaEi0d-QJ
Mongoose: rooms.find({}, {})
GET / 304 27.828 ms - -
room 네임스페이스 접속 해제
GET /main.css 304 1.593 ms - -
room 네임스페이스에 접속
Mongoose: rooms.find({}, {})
GET / 304 4.459 ms - -
room 네임스페이스 접속 해제
GET /main.css 304 1.506 ms - -
room 네임스페이스에 접속
room 네임스페이스 접속 해제
chat 네임스페이스에 접속
#76862d 6t7e0goBwVLrd4Ny2Ok5g6FDYK-5Cmsj
GET /room 200 3.556 ms - 806

 

방을 생성하면 다른 세션에서 실시간으로 방이 생성된 것을 확인할 수 있고 방 입장도 가능하다.

 

입장하거나 퇴장하는 메세지도 잘 출력 되는지 확인한다.

 

메세지도 양쪽에서 잘 출력 되는지 확인한다.

 

최종적으로 gif 파일 올리기 까지 정상적으로 올라가는 것을 확인할 수 있다.

'Javascript > Node' 카테고리의 다른 글

노드 교과서 섹션 8  (0) 2023.08.23
노드 교과서 섹션 9  (0) 2023.08.22
노드 교과서 섹션 10  (0) 2023.08.19
노드 교과서 섹션 12  (0) 2023.08.17
노드 교과서 섹션 13  (0) 2023.08.09

+ Recent posts