정보실

웹학교

정보실

Nodejs Deno 배우기 : 채팅 앱

본문

원본 기사 : https://aralroca.com/blog/learn-deno-chat-app


Node.js는 2009 년 Ryan Dahl이 C++로 작성했습니다. Ryan은 2012 년 Node.js를 떠났으며 이 시점에서 자신의 목표를 어느 정도 달성했다고 느꼈습니다.


그의 목표는 이제 다릅니다. Node.js에서 수정할 수 없는 일부 디자인 오류가 있음을 알고 V8로 빌드 된 다른 JavaScript (또한 TypeScript) 런타임을 작성하기로 결정했습니다. Deno (in Rust). Deno 1.0.0은 2020 년 5 월 13 일에 최종 출시 될 예정입니다.


https://dev.to/aralroca/learn-deno-chat-app-37f0 


Deno logo 

간단한 채팅 애플리케이션을 구현하여 Deno의 작동 방식과 Node와의 차이점을 살펴 보겠습니다.


Deno 설치 


curl, iwr, Homebrew, Chocolatey를 사용하여 Deno를 설치하는 방법은 여러 가지가 있습니다. 

여기에서 설치 방법을 참조하십시오. Deno는 단일 바이너리 실행 파일이며 외부 종속성이 없습니다.


제 경우에는 Homebrew를 사용하겠습니다.


➜  ~ brew install deno
➜  ~ deno --version
deno 1.0.0-rc1
v8 8.2.308
typescript 3.8.3


우리가 볼 수 있듯이 여기에는 npm이 없습니다. 

Npm은 Node 생태계에서 필수 요소가 되기 시작했습니다. 그리고 그것은 모듈을 위한 중앙 집중식 (비공개로 제어되는) 저장소입니다. 

이것은 이제 Deno로 바뀌고 있습니다. 우리는 나중에 package.json과 node_modules없이 패키지를 설치하는 방법을 볼 것입니다.


최신 버전으로 업그레이드하려면 deno upgrade를 수행해야 합니다.


가능한 모든 사용법을 보려면 deno help를 실행하는 것이 좋습니다.


USAGE:
    deno [OPTIONS] [SUBCOMMAND]

OPTIONS:
    -h, --help                     Prints help information
    -L, --log-level <log-level>    Set log level [possible values: debug, info]
    -q, --quiet                    Suppress diagnostic output
    -V, --version                  Prints version information

SUBCOMMANDS:
    bundle         Bundle module and dependencies into single file
    cache          Cache the dependencies
    completions    Generate shell completions
    doc            Show documentation for a module
    eval           Eval script
    fmt            Format source files
    help           Prints this message or the help of the given subcommand(s)
    info           Show info about cache or info related to source file
    install        Install script as an executable
    repl           Read Eval Print Loop
    run            Run a program given a filename or url to the module
    test           Run tests
    types          Print runtime TypeScript declarations
    upgrade        Upgrade deno executable to newest version

ENVIRONMENT VARIABLES:
    DENO_DIR             Set deno's base directory (defaults to $HOME/.deno)
    DENO_INSTALL_ROOT    Set deno install's output directory
                         (defaults to $HOME/.deno/bin)
    NO_COLOR             Set to disable color
    HTTP_PROXY           Proxy address for HTTP requests
                         (module downloads, fetch)
    HTTPS_PROXY          Same but for HTTPS


Visual Studio Code를 사용하는 경우 Deno를 쉽게 사용할 수 있도록 이 플러그인을 설치하는 것이 좋습니다.



간단한 "Hello World" 


Deno의 간단한 "Hello world"를 위해서는 .js 또는 .ts 파일을 만들고 deno run [file]로 실행하면 됩니다.


.ts의 경우 컴파일 + 실행되며 .js의 경우 파일이 직접 실행됩니다.


// example.ts file
console.log('Hello from Deno 🖐')


그리고 shell에서 :


➜  deno run example.ts
Compile file:///Users/aralroca/example.ts
Hello from Deno 🖐

tsconfig.json 파일은 Deno에 일부 TypeScript 기본값이 있기 때문에 선택 사항입니다. tsconfig.json을 적용하려면 deno run -c tsconfig.json [file]을 사용해야 합니다.


그건 그렇고, Deno는 가능한 경우 웹 표준을 사용합니다. window, fetch, Worker를 사용할 수 있습니다 ... 코드는 Deno 및 브라우저와 호환 되어야 합니다.


index.html 제공 


Deno는 자체 표준 라이브러리 https://deno.land/std/를 가지고 있으므로 모듈을 사용하기 위해 URL에서 직접 가져올 수 있습니다. 

목표 중 하나는 최소한의 연결로 단일 실행 파일 만 제공하는 것입니다. 이런 식으로 URL을 프로젝트로 가져 오거나 CLI의 경우 https : // ...를 실행하여 직접 실행하면 됩니다.


http 서버를 만들고 index.html을 제공하기 위해 https://deno.land/std/http/ 모듈을 사용할 것입니다.


server.ts와 index.html이라는 두 개의 파일을 만들 것입니다.


index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta charset="utf-8" />
    <title>Example using Deno</title>
  </head>
  <body>index.html served correctly</body>
</html>


server.ts

import { listenAndServe } from "https://deno.land/std/http/server.ts";

listenAndServe({ port: 3000 }, async (req) => {
  if (req.method === "GET" && req.url === "/") {
    req.respond({
      status: 200,
      headers: new Headers({
        "content-type": "text/html",
      }),
      body: await Deno.open("./index.html"),
    });
  }
});

console.log("Server running on localhost:3000");


Common.js 대신 기본적으로 ESmodule을 사용하여 파일 확장자가 항상 끝에 있음을 나타냅니다. 또한 async-await와 같은 최신 기능을 지원합니다.


또한 더 이상 서식에 대해 걱정할 필요가 없습니다. 도구를 Prettier로 사용하는 대신 deno fmt 명령으로 파일을 포맷 할 수 있습니다.


server.ts 실행을 처음으로 실행하면 "Hello World"예제와 관련하여 두 가지 차이점이 있습니다.


  1. http 모듈에서 모든 종속성을 다운로드합니다. yarn 또는 npm install을 사용하는 대신 프로젝트를 실행하기 전에 필요한 모든 종속성을 설치해야 합니다. 이것은 캐시 되기 때문에 처음으로 발생합니다. 캐시를 정리하려면 --reload 명령을 사용할 수 있습니다.
  2. Uncaught PermissionDenied : "127.0.0.1:3000"에 대한 네트워크 액세스 오류가 발생하고 --allow-net 플래그로 다시 실행됩니다. Deno는 기본적으로 안전합니다. 이것은 우리가 그물에 접근하거나 파일 (index.html)을 읽을 수 없다는 것을 의미합니다. 이것은 노드에 비해 크게 개선 된 것 중 하나입니다. Node에서 CLI 라이브러리는 우리의 동의 없이 많은 일을 할 수 있습니다. 예를 들어 Deno를 사용하면 한 폴더에서만 읽기 액세스를 허용 할 수 있습니다 : deno --allow-read = / etc. 모든 권한 플래그를 보려면 deno run -h를 실행하십시오.


이제 index.html을 제공 할 준비가 되었습니다.


➜ deno run --allow-net --allow-read server.ts
Compile file:///Users/aralroca/server.ts
Server running on localhost:3000


Deno server serving an index.html 


WebSocket 사용 


노드의 WebSockets, UUID 및 기타 필수 요소는 핵심 요소가 아닙니다. 즉, 타사 라이브러리를 사용하려면 사용해야 합니다. 그러나 Deno 표준 라이브러리를 사용하여 WebSockets 및 UUID를 여러 가지 중에서 사용할 수 있습니다. 다시 말해, 이제는 항상 유지 보수 되므로 유지 보수에 대해 걱정할 필요가 없습니다.


간단한 채팅 앱을 계속 구현하려면 다음을 사용하여 새로운 파일 chat.ts를 만듭니다.

import {
  WebSocket,
  isWebSocketCloseEvent,
} from "https://deno.land/std/ws/mod.ts";
import { v4 } from "https://deno.land/std/uuid/mod.ts";

const users = new Map<string, WebSocket>();

function broadcast(message: string, senderId?: string): void {
  if(!message) return
  for (const user of users.values()) {
    user.send(senderId ? `[${senderId}]: ${message}` : message);
  }
}

export async function chat(ws: WebSocket): Promise<void> {
  const userId = v4.generate();

  // Register user connection
  users.set(userId, ws);
  broadcast(`> User with the id ${userId} is connected`);

  // Wait for new messages
  for await (const event of ws) {
    const message = typeof event === 'string' ? event : ''

    broadcast(message, userId);

    // Unregister user conection
    if (!message && isWebSocketCloseEvent(event)) {
      users.delete(userId);
      broadcast(`> User with the id ${userId} is disconnected`);
      break;
    }
  }
}


이제 server.ts에 대화를 표시하기 위해 엔드 포인트 / ws를 등록하십시오.


import { listenAndServe } from "https://deno.land/std/http/server.ts";
import { acceptWebSocket, acceptable } from "https://deno.land/std/ws/mod.ts";
import { chat } from "./chat.ts";

listenAndServe({ port: 3000 }, async (req) => {
  if (req.method === "GET" && req.url === "/") {
    req.respond({
      status: 200,
      headers: new Headers({
        "content-type": "text/html",
      }),
      body: await Deno.open("./index.html"),
    });
  }

  // WebSockets Chat
  if (req.method === "GET" && req.url === "/ws") {
    if (acceptable(req)) {
      acceptWebSocket({
        conn: req.conn,
        bufReader: req.r,
        bufWriter: req.w,
        headers: req.headers,
      }).then(chat);
    }
  }
});

console.log("Server running on localhost:3000");


클라이언트 측 부분을 구현하기 위해 이전 기사에서 살펴본 것처럼 npm, babel 및 webpack없이 모듈을 직접 사용할 수 있도록 Preact를 선택합니다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Chat using Deno</title>
  </head>
  <body>
    <div id="app" />
    <script type="module">
      import { html, render, useEffect, useState } from 'https://unpkg.com/htm/preact/standalone.module.js'

      let ws

      function Chat() {
        // Messages
        const [messages, setMessages] = useState([])
        const onReceiveMessage = ({ data }) => setMessages(m => ([...m, data]))
        const onSendMessage = e => {
          const msg = e.target[0].value

          e.preventDefault()
          ws.send(msg)
          e.target[0].value = ''
        }

        // Websocket connection + events
        useEffect(() => {
          if (ws) ws.close()
          ws = new WebSocket(`ws://${window.location.host}/ws`)
          ws.addEventListener("message", onReceiveMessage)

          return () => {
            ws.removeEventListener("message", onReceiveMessage)
          }
        }, [])

        return html`
          ${messages.map(message => html`
              <div>${message}</div>
          `)}

          <form onSubmit=${onSendMessage}>
           <input type="text" />
           <button>Send</button>
          </form>
        `
      }

      render(html`<${Chat} />`, document.getElementById('app'))
    </script>
  </body>
</html>


결과:

Chat implemented with Deno + Preact 


스타일이 없는 매우 못생긴 채팅이지만 기능적입니다. 여기서 우리의 목표는 Deno의 작동 방식을 이해하는 것입니다.


타사 및 deps.ts 규칙 


모듈의 URL을 직접 가져 와서 Deno 표준 라이브러리를 사용하는 것과 같은 방식으로 타사 라이브러리를 사용할 수 있습니다.


그러나 https://deno.land/x/의 생태계는 아직 매우 작습니다. 하지만 좋은 소식이 있습니다. https://www.pika.dev/에서 패키지를 사용할 수 있습니다. Parcel 또는 Minibundle과 같은 도구 덕분에 노드 라이브러리를 모듈로 컴파일하여 Deno 프로젝트에서 재사용 할 수 있습니다.


모든 채팅 메시지를 camelCase로 변환하기 위해 camel-case 패키지를 사용할 것입니다!


Importing camel-case lib from pika web 


chat.ts 파일에 이 가져 오기를 추가해 봅시다 :

import { camelCase } from 'https://cdn.pika.dev/camel-case@^4.1.1';
// ...before code
const message = camelCase(typeof event === 'string' ? event : '')
// ... before code


server.ts를 다시 실행하면 camel-case 패키지가 다운로드 됩니다. 이제 작동한다는 것을 알 수 있습니다.


Using camel-case package on deno 


그러나 이 camelCase 도우미를 둘 이상의 파일에 사용하려면 모든 가져 오기를 어디서나 추가하는 것이 번거롭습니다. URL은 사용해야 하는 패키지 버전을 나타냅니다. 즉, 종속성을 업그레이드하려면 모든 가져 오기를 검색하고 바꿔야 합니다. 이것은 우리에게 문제를 일으킬 수 있지만 걱정하지 마십시오.이 문제를 해결하는 종속성에 대한 Deno 규칙이 있습니다. 모든 프로젝트 종속성을 내보내는 deps.ts 파일 작성


// deps.ts file
export { camelCase } from 'https://cdn.pika.dev/camel-case@^4.1.1';


그리고


// chat.ts file
import { camelCase } from './deps.ts';
// ...
const message = camelCase(typeof event === 'string' ? event : '')
// ...


테스팅 


우리는 쓸모없는 camilize.ts 유틸리티를 만들어서 camelCase의 텍스트를 멋진 추가 기능으로 반환 할 것입니다. 대문자 당 🐪가 포함되어 있습니다. 왜? Deno로 테스트하는 방법을 알아보십시오.


/**
 * Return the text in camelCase + how many 🐪
 * 
 * @example "this is an example" -> "thisIsAnExample 🐪🐪🐪"
 * @param text 
 * @returns {string}
 */
export function camelize(text: string) {
  // @todo
}


그런데 deno doc [file]을 사용하여 파일의 JSdoc을 시각화 할 수 있습니다.


➜  deno doc camelize.ts 
function camelize(text: string)
  Return the text in camelCase + how many 🐪


test.ts 파일을 만들어 봅시다. 테스트 러너는 Deno.test()를 사용하여 Deno의 핵심에 내장되어 있으며 STD https://deno.land/std/testing/asserts.ts를 사용하여 어설 션을 사용할 수 있습니다.


import { assertStrictEq } from "https://deno.land/std/testing/asserts.ts";
import { camelize } from "./camelize.ts";

Deno.test("camelize works", async () => {
  assertStrictEq(camelize("this is an example"), "thisIsAnExample 🐪🐪🐪");
});


모든 테스트를 실행하려면 deno test 만 실행하면 됩니다.


➜  deno test
Compile file:///Users/aralroca/test.ts
running 1 tests
test camelize works ... FAILED (0ms)

failures:

camelize works
AssertionError: actual: undefined expected: thisIsAnExample 🐪🐪🐪
    at assertStrictEq (asserts.ts:224:11)
    at test.ts:5:3
    at asyncOpSanitizer ($deno$/testing.ts:36:11)
    at Object.resourceSanitizer [as fn] ($deno$/testing.ts:70:11)
    at TestApi.[Symbol.asyncIterator] ($deno$/testing.ts:264:22)
    at TestApi.next (<anonymous>)
    at Object.runTests ($deno$/testing.ts:346:20)

failures:

        camelize works

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (0ms)


물론 유틸리티를 아직 구현하지 않았기 때문에 실패하지만 여전히 셸에서 오류가 어떻게 표시되는지 확인할 수 있습니다.


camelize 유틸리티를 구현 한 후 :


import { camelCase } from "./deps.ts";

/**
 * Return the text in camelCase + how many 🐪
 * 
 * @example "this is an example" -> "thisIsAnExample 🐪🐪🐪"
 * @param text 
 * @returns {string}
 */
export function camelize(text: string) {
  const camelCaseText = camelCase(text);
  const matches = camelCaseText.match(/[A-Z]/g) || [];
  const camels = Array.from({ length: matches.length })
    .map(() => "🐪")
    .join("");

  return `${camelCaseText} ${camels}`;
}


이제 모든 테스트가 통과되었습니다.


➜  deno test
Compile file:///Users/aralroca/camelize.ts
running 1 tests
test camelize works ... ok (3ms)

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (3ms)

감시자를 사용하여 모든 테스트를 실행할 때마다 실행하지 않으려면 nodemon을 기반으로 https://deno.land/x/denon/을 사용한 다음 denon test를 실행할 수 있습니다.


이제 chat.ts에서 도우미를 사용할 준비가 되었습니다.


camels on the message 


디버깅 


Deno로 디버깅하려면 :


  1. 코드 어딘가에 디버거를 추가하십시오. 코드 라인.
  2. --inspect-brk 플래그로 실행하십시오. deno run --inspect-brk ... 또는 deno test --inspect-brk ...를 실행하여 테스트를 디버그 합니다.
  3. Chrome에서 chrome : // inspect 페이지를 엽니 다.
  4. 원격 대상 섹션에서 "inspect"을 눌러 합니다.
  5. 스크립트 실행 재개 버튼을 누르면 코드가 중단 점에서 일시 중지됩니다.

Debugging with Deno 


결론 


TypeScript에서 간단한 채팅 앱을 만들어 Deno의 작동 방식을 배웠습니다. 우리는 npm, package.json, node_modules, webpack, babel, jest, prettier없이 그것을 했습니다. 우리가 필요하지 않기 때문에 Deno는 이것을 단순화합니다.


권한, deno 명령, deno 내부 사용 방법, 타사 종속성 사용 방법, 파일 제공, 웹 소켓, 파일 형식 지정, 테스트, 디버깅 등 Deno 프로젝트로 시작하는 중요한 사항을 살펴 보았습니다.


이 기사가 2020 년 5 월 13 일에 나올 때 프로젝트에서 Deno 1.0.0을 사용하는 데 도움이 되기를 바랍니다.


이 기사의 코드 


내 GitHub에 코드를 업로드했습니다.


참고 문헌 



페이지 정보

조회 25회 ]  작성일20-05-15 17:55

웹학교