[실시간 채팅 웹앱 만들기](2) - 프로젝트 세팅 및 소켓 연결

yoon Y·2022년 10월 17일
0

server와 clinent로 폴더를 분리했다.
server는 localhost:8080포트 사용
clinen는 localhost:3000포트 사용

1. 서버 세팅

express, socket.io 패키지를 설치한 후 app.js파일에 서버를 만들고 socket.io와 연결했다.

1-1. express세팅

// app.js
const express = require("express"); // express모듈 불러오기
const app = express(); // app변수에 express인스턴스 반환
const http = require("http");
const server = http.createServer(app); // 웹서버(객체) 생성
const port = 8080;

app.get("/", (req, res) => {
  res.send("Hello World!");
});

server.listen(port, () => {
  console.log(`서버가 실행됩니다. http://localhost:${port}`);
});

💡 express모듈과 http모듈의 차이?

http모듈

  • 웹서버와 클라이언트 생성 등 관련된 모든 기능을 담당하는 모듈이다.(node에 기본 내장)
  • http.createServer메소드에 핸들러 함수를 전달해서 server(객체)를 생성한다.
  • 핸들러 함수에 인자로 들어오는 request, response객체를 이용해 라우팅하면서 프로그램을 구현한다.

express모듈

  • http모듈에 여러 기능을 추가해서 쉽게 사용할 수 있게 만든 모듈이다.
  • 웹 어플리케이션을 편하게 만들기 위한 각종 라이브러리와 미들웨어 등이 내장되어있다.
  • express모듈로 서버를 생성하면 request객체와 response객체에 다양한 기능이 추가된다.

http모듈 외에 express를 사용하는 이유
http모듈만으로 라우팅을 처리하려면 조건문으로 일일이 복잡하게 구현해야한다.
express를 같이 사용하면 다양한 라이브러리와 미들웨어가 제공되어 편리하게 개발할 수 있다.(라우팅, 모듈화, 다양한 기능들...)

// http모듈만 사용했을 경우
const http = require('http')
const { sendPosts } = require('./sendPosts')

const server = http.createServer((req, res) => {
  const { url, method } = req
  res.setHeader('Content-Type', 'application/json')

  if (url === '/') return res.send({ message: '/ endpoint' })
  if (url === '/signup' && method === 'POS') return res.end(JSON.stringify({ message: '회원가입 완료!' }))
  if (url === '/login' && method === 'POST') return res.end(JSON.stringify({ message: '로그인 완료!' }))
  if (url === '/products' && method === 'GET') return sendPosts(res)

  res.send(JSON.stringify({ message: 'this response answers to every request' }))
})

server.listen(8080, () => { console.log('server is listening on PORT 8000')})
// express와 함께 사용한 경우
const http = require('http')
const express = require('express')
const { sendPosts } = require('./postings')

const app = express()
app.use(express.json())

app.get('/', (req, res) => {
  res.json({ message: '/ endpoint' })
})

app.post('/signup', handleSignUp)
app.post('/login', handleLogin)
app.get('/products', sendPosts)

const server = http.createServer(app)

server.listen(8000, () => {
  console.log('server is listening on PORT 8000')
})

https://quark21.tistory.com/m/331
https://velog.io/@hahan/Http-%EB%AA%A8%EB%93%88%EA%B3%BC-Express-%EB%AA%A8%EB%93%88%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90
https://abc1211.tistory.com/m/225


1-2. socket.io세팅 및 연결

io 객체 - 연결된 전체 클라이언트들과의 interacting을 위한 객체이다.
socket 객체 - 개별 클라이언트와의 interacting을 위한 객체이다.
on메소드

  • io.on: 소켓 서버에 일어나는 이벤트를 수신해 핸들러를 실행시키기 위한 메소드이다.
  • socekt.io: 접속되어 있는 클라이언트로부터의 이벤트+데이터를 수신하기 위한 메소드이다.

emit메소드

  • io.emit : 연결되어있는 모든 클라이언트에게 이벤트+데이터를 발송하기 위한 메소드이다.
  • socket.emit: 접속되어 있는 클라이언트에게 이벤트+데이터를 발송하기 위한 메소드이다.
  • 위 두 메소드와 관련한 다양한 메서드들이 있다.

*클라이언트와 서버 간에 주고 받는 이벤트 이름은 같아야 한다.

// app.js
const express = require("express");
const app = express();
const http = require("http");
const server = http.createServer(app);
const io = require("socket.io")(server);
const port = 8080;

app.get("/", (req, res) => {
  res.send("Hello World!");
});

// 클라이언트가 접속할 떄 소켓 서버에 connection이벤트가 발생하고 이벤트를 일으킨 socket이 전달된다.
io.on("connection", (socket) => {
  console.log("user connected");

  // 접속한 클라이언트의 정보가 수신되면
  socket.on("login", (data) => {
    console.log(
      "Client logged-in:\n name:" + data.name + "\n userid: " + data.userid
    );

    // socket에 클라이언트 정보를 저장한다
    socket.name = data.name;
    socket.userid = data.userid;

    // 접속된 모든 클라이언트에게 메시지를 전송한다
    io.emit("login", data.name);
  });

  socket.on("message", (data) => {
    console.log(data.message);

    const responseMsg = {
      msg: data.msg,
    };

    // 접속된 모든 클라이언트에게 메시지를 전송한다
    io.emit("message", responseMsg);
  });

  socket.on("disconnect", () => {
    console.log("user disconnected");
  });
});

server.listen(port, () => {
  console.log(`서버가 실행됩니다. http://localhost:${port}`);
});

2. 클라이언트

2-1. 환경 구성

  • create nest app ts버전 설치
  • eslint+프리티어 설정
  • ts config설정
  • emotion설치 + 바벨 설정
  • socket.io-client설치

2-2. socket.io세팅 및 연결

*클라이언트와 서버 간에 주고 받는 이벤트 이름은 같아야 한다.

// _app.tsx
import "../styles/globals.css";
import io from "socket.io-client";
import { useEffect } from "react";

// 앱 실행 시 통신하려는 서버와 연결, soceket인스턴스 반환
const socket = io("http://localhost:8080");

// soceket통신 시 발송되어야 할 이벤트와 데이터 등록
function MyApp({ Component, pageProps }) {
  const handleLogin = () => {
    socket.emit("login", {
      name: "yy2122",
      userid: "0",
    });
  };

  const handleSendMessage = (mes) => {
    socket.emit("message", { msg: mes });
  };

  // socket통신 시 수신되어야 할 이벤트와 핸들러 등록
  useEffect(() => {
    socket.on("connect", () => {
      console.log("SOCKET CONNECTED!", socket.id);
    });

    socket.on("login", (data) => {
      console.log(data);
    });

    socket.on("message", (data) => {
      console.log(data.msg);
    });

    socket.on("disconnect", () => {
      console.log("서버와 연결이 해제되었습니다.");
    });

    // 앱 종료 시 등록한 이벤트 해제
    return () => {
      socket.off("connect");
      socket.off("disconnect");
      socket.off("login");
      socket.off("message");
    };
  }, []);

  return (
    <>
      <Component {...pageProps} />
      <button onClick={handleLogin}>로그인</button>
      <button
        onClick={() => {
          handleSendMessage("메세지!!");
        }}
      >
        메세지 전송
      </button>
    </>
  );
}

export default MyApp;

3. 트러블 슈팅

위에 작성한 코드들로 실행했을 때 cors오류가 발생했다.
클라이언트와 서버의 포트가 달라서이다.

해결
소켓 서버 생성 시 cors관련 옵션을 전달해주어 해결했다.

// server - app.js
const express = require("express");
const app = express();
const http = require("http");
const server = http.createServer(app);
const io = require("socket.io")(server); // cors발생 코드
const port = 8080;

// cors관련 옵션 추가한 코드
const io = require("socket.io")(server, {
  cors: {
    origin: "*",
    methods: ["GET", "POST"],
  },
});
profile
#프론트엔드

0개의 댓글