ソケット学習

10037 ワード

SOcketのみを使用するサイト


https://www.youtube.com/watch?v=CgV8omlWq2o&t=29s
https://github.com/NikValdez/ChatAppTut
https://hoony-gunputer.tistory.com/entry/socket-io%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

現代JavaScript WebSocket


https://poiemaweb.com/nodejs-socketio

ソケット接続に失敗した場合


https://velog.io/@jun17114/nodeJS%EC%99%80-React-Socket.io%EB%A1%9C-%ED%86%B5%EC%8B%A0

  • 初めてやった.
      ```bash
      # npm으로 패키지를 설치할 때는 아래 명령어를 사용해요!
      # 옵션은 필요한 경우에만 적어줍니다.
      # npm install [옵션] [설치할 패키지 이름]
    
      npm install -g yarn 
    
      # 이 명령어는 "npm으로 yarn을 컴퓨터 전체에 설치한다"는 뜻입니다.
      # -g 옵션은 컴퓨터 전체에서 쓸 수 있게 한다는 뜻입니다.
      ```
    
      ```bash
      yarn add [옵션] [설치할 패키지 이름]
      ```

  • CRA(create-act-app)で始まる応答

  • CRAをガーゼで取り付けましょう!
    # 옵션 global은 전역에 이 패키지를 깔겠다는 뜻입니다.
    yarn add global create-react-app
  •     ```bash
        # yarn create react-app [우리의 첫 리액트 프로젝트 이름]
        # 우리가 설치한 create-react-app 패키지를 써서 프로젝트를 만들어요.
        # 주의! 꼭 sparta_react 폴더 경로에서 입력해주세요!
        yarn create react-app week-1
        ```
    npm i quill
    vscコード=>応答スキャン=>es 7=>vscコード上でrfc自動完了
    delta

    *client
    import React, {useCallback, useEffect, useState} from 'react'
    import Quill from "quill"
    import "quill/dist/quill.snow.css"
    import {io} from 'socket.io-client'
    
    const TOOLBAR_OPTIONS = [
       [{header: [1, 2, 3, 4, 5, 6, false]}],
       [{font: []}],
       [{list: "ordered"}, {list: "bullet"}],
       ["bold", "italic", "underline"],
       [{color: []}, {backgrond: []}],
       [{script: "sub"}, {script: "super"}],
       [{align: []}],
       ["image", "blockquote", "code-block"],
       ["clean"],
    ]
    
    export default function TextEditor() {
       const [socket, setSocket] = useState();
       const [quill, setQuill] = useState();
      useEffect(() => {
          const s = io("http://localhost:3001")
          setSocket(s)
    
          return () => {
              s.disconnect()
          }
      }, [])
    
      useEffect(() => {
          if (socket == null || quill == null) return
          
          const handler = (delta, oldDelta, source) => {
            socket.emit("send-changes", delta)
          }
          quill.on('text-change', handler)
    
          return () => {
              quill.off('text-change', handler)
          }
      }, [socket, quill])
    
       const wrapperRef = useCallback((wrapper) => {
           if (wrapper == null) return
           wrapper.innerHTML = ''
           const editor = document.createElement('div')
           wrapper.append(editor)
           const q = new Quill(editor, { theme: 'snow', modules: {
               toolbar: TOOLBAR_OPTIONS}})
           setQuill(q)
       }, [])
       return (
           <div className="container" ref={wrapperRef}></div>
       )
    }
    
    *server
    const io = require('socket.io')(3001, {
       cors: {
           origin: 'http://localhost: 3000',
           methods: ['GET', 'POST']
       }
    })
    
    io.on("connection", socket => {
       socket.on('send-changes', delta => {
           console.log("connected")
       })
       
    })

    最終バージョン


    https://coding-hwije.tistory.com/24(参照サイト)
    https://github.com/WebDevSimplified/google-docs-clone/blob/main/client/src/TextEditor.js
    client
    import { useCallback, useEffect, useState } from "react"
    import Quill from "quill"
    import "quill/dist/quill.snow.css"
    import { io } from "socket.io-client"
    import { useParams } from "react-router-dom"
    
    //setInterval() 일정한 시간 간격으로 작업을 수행하기 위해 사용
    const SAVE_INTERVAL_MS = 2000
    
    //위의 툴바 부분
    const TOOLBAR_OPTIONS = [
     [{ header: [1, 2, 3, 4, 5, 6, false] }],
     [{ font: [] }],
     [{ list: "ordered" }, { list: "bullet" }],
     ["bold", "italic", "underline"],
     [{ color: [] }, { background: [] }],
     [{ script: "sub" }, { script: "super" }],
     [{ align: [] }],
     ["image", "blockquote", "code-block"],
     ["clean"],
    ]
    
    export default function TextEditor() {
    // const params = useParams()  
     const { id: documentId } = useParams() //뭔가 파라미터...주소...쓴다는 그 느낌
     const [socket, setSocket] = useState()
     const [quill, setQuill] = useState()
    
     useEffect(() => {
       const s = io("http://localhost:3001")
       setSocket(s)
    
       return () => {
         s.disconnect()
       }
     }, [])
    
     useEffect(() => {
       if (socket == null || quill == null) return
    
    //최초 1번 실행되는 once메소드, 첫번째 데이터에 대해서만 데이터가 되돌려지는 예제
       socket.once("load-document", document => { 
         quill.setContents(document)
         quill.enable()
       })
    
       socket.emit("get-document", documentId)
     }, [socket, quill, documentId])
    
     useEffect(() => {
       if (socket == null || quill == null) return
    
       const interval = setInterval(() => { //일정한 시간 간격으로 작업을 수행하기 위해 사용
         socket.emit("save-document", quill.getContents())  
       }, SAVE_INTERVAL_MS)
         //socket.emit 데이터 보낼 때
         //서버의 save-document 이벤트명을 찾아 보내진다
         //클라이언트의 contents를 보냄?
    
       return () => {
         clearInterval(interval)
       }
     }, [socket, quill])
    
     useEffect(() => {
       if (socket == null || quill == null) return
    
       const handler = delta => {
         quill.updateContents(delta)
       }
       socket.on("receive-changes", handler)
    
       return () => {
         socket.off("receive-changes", handler)
       }
     }, [socket, quill])
    
     useEffect(() => {
       if (socket == null || quill == null) return
    
       const handler = (delta, oldDelta, source) => {
         if (source !== "user") return
         socket.emit("send-changes", delta)
       }
       quill.on("text-change", handler)
    
       return () => {
         quill.off("text-change", handler)
       }
     }, [socket, quill])
    
    
    //useCallback 특정함수를 새로 만들지 않고 재사용하고 싶을 때 사용
     const wrapperRef = useCallback(wrapper => {
       if (wrapper == null) return
    
       wrapper.innerHTML = ""
       const editor = document.createElement("div")
       wrapper.append(editor)
       const q = new Quill(editor, {
         theme: "snow",
         modules: { toolbar: TOOLBAR_OPTIONS },
       })
       q.disable()
       q.setText("Loading...")
       setQuill(q)
     }, [])
     return <div className="container" ref={wrapperRef}></div>
    }
    server
    const mongoose = require("mongoose")
    const Document = require("./Document")
    
    mongoose.connect("mongodb://localhost/google-docs-clone", {
     useNewUrlParser: true,
     useUnifiedTopology: true,
     useFindAndModify: false,
     useCreateIndex: true,
    })
    
    const io = require("socket.io")(3001, {
     cors: {
       origin: "http://localhost:3000",
       methods: ["GET", "POST"],
     },
    })
    
    const defaultValue = ""
    
    io.on("connection", socket => {
     socket.on("get-document", async documentId => { //socket.on 데이터 받기
       const document = await findOrCreateDocument(documentId)
       socket.join(documentId) 
       //socket.join 룸을 만든다
       //받아온 documentId라는 이름의 새로운 룸에 접속했다
       socket.emit("load-document", document.data)
    
       socket.on("send-changes", delta => {
         socket.broadcast.to(documentId).emit("receive-changes", delta)
       })
    
       socket.on("save-document", async data => {
         await Document.findByIdAndUpdate(documentId, { data })
       })
     })
    })
    
    async function findOrCreateDocument(id) {
     if (id == null) return
    
     const document = await Document.findById(id)
     if (document) return document
     return await Document.create({ _id: id, data: defaultValue })
    }