Notice
Recent Posts
Recent Comments
Link
«   2026/06   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

개발자가 될래요

Next + Socket.io 로 채팅 구현(1) 본문

프로젝트

Next + Socket.io 로 채팅 구현(1)

Youcan 2024. 8. 16. 22:01

Next와 Socket.io를 이용하여 채팅기능을 구현해보자.

 

일단 Socket.io란?

- socket.io 란 WebSocket을 기반으로 실시간 웹 애플리케이션을 위한 JavaScript 라이브러리.

- 웹 클라이언트와 서버 간의 실시간 양방향 통신을 가능하게 해주는 Node.js의 모듈

 


왜 WebSocket을 ??

WebSocket : 데이터가 누락되지 않게 하는 TCP 기반의 양방향통신을 제공하는 컴퓨터 프로토콜

기존의 HTTP통신은 단방향 방식으로 연결유지가 안되기 때문에 업데이트 상황을 매번 서버에 요청하고 응답을 받아야 한다. 이 방식은 몇 가지의 문제가 있다.

 1. 요청을 보내는 주기만큼의 지연이 발생할 수 있다.

    - 클라이언트가 보낸 요청과 다음 요청 사이에 업데이트 된 내용은 즉각적으로 응답 받을 수 없다.

 2. 계속해서 불필요한 요청을 보낸다

    - 앞선 지연의 문제를 해결하기 위해 요청을 짧은 시간동안 계속 보낸다고 생각하면, 반응속도나 지연에 대한 공백은 줄어들겠지만, 트래픽의 낭비가 심해진다.

3. HTTP에서의 요청과 응답에 포함되는 헤더 정보의 양도 매 번 부담이 된다.

 

 

하지만 웹소켓은 양방향 통신을 지원하고 서버와의 연결이 유지된 상태에서 데이터가 오갈 수 있게 해주기 때문에 실시간 데이터 전송이 가능해진다.

 

웹소켓 통신은 다음과 같은 방식으로 이루어진다.

1. 클라이언트는 서버에게 웹소켓을 연결하자는 HTTP 요청을 보낸다. 서버는 연결이 가능한 경우, 이를 수락하는 HTTP 응답을 보낸다.(handshake 과정)

 

2. 연결이 이뤄지면 그 때부터 클라이언트과 서버는 HTTP가 아닌, WebSocket 프로토콜을 사용하여 소통한다.

 

3. 클라이언트와 서버는 자유롭게 서로 메시지를 보낼 수 있게 됨.

 

WebSocket에서의 통신은 헤더의 크기가 작고 오버헤드가 적기 때문에 HTTP보다 효율적인 통신이 가능하다.

이 연결은 클라이언트나 서버 중 한 쪽이 종료 할 때까지 이어지는데, 한 쪽에서 종료 요청을 하면, 반대편에서 종료 응답을 보냄으로써 연결을 종료한다.

 


즉 이 WebSocket을 더욱 편리하게 개발하기 위해 만들어진 것이 Node.js의 Socket.io 모듈이다.

 

엄밀히 말하면 WebSocket과 socket.io는 차이점이 존재하지만, 지금은 socket.io를 사용해보도록 하겠다.


그럼 일단 프로젝트를 진행해보자.

 

npx create-next-app@latest

npm install socket.io socket.io-client nodemon

 

일단 next 프로젝트를 생성하고, 필요한 라이브러리들을 설치한다.

 

그리고... Socket.io 공식 문서( How to use with Next.js | Socket.IO )의 예시 코드를 적용해보자

chat-app/Server.ts

import { createServer } from "http";
import next from "next";
import { Server as SocketIOServer, Socket } from "socket.io";

const dev = process.env.NODE_ENV !== "production";
const hostname = "localhost";
const port = 4000;

const app = next({ dev, hostname, port });
const handler = app.getRequestHandler();

app.prepare().then(() => {
  const httpServer = createServer(handler);

  const io = new SocketIOServer(httpServer, {
    cors: {
      origin: "http://localhost:3000", // 클라이언트가 실행되는 도메인
      methods: ["GET", "POST"],
      credentials: true,
    },
  });

  io.on("connection", (socket: Socket) => {
    console.log("New client connected", socket.id);

    socket.on("message", (data: any) => {
      console.log("Message received:", data);
      socket.emit("response", "Message received!");
    });

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

  httpServer
    .once("error", (err: Error) => {
      console.error(err);
      process.exit(1);
    })
    .listen(port, () => {
      console.log(`> Ready on http://${hostname}:${port}`);
    });
});

 

chat-app/socket.ts

"use client";

import { io } from "socket.io-client";

export const socket = io("http://localhost:4000");

 

이 두 개의 파일을 프로젝트의 최상위 루트에 만들어 주었고,

연결 상태를 확인해볼 클라이언트 페이지도 만들어 준다.

chat-app/app/chat/page.tsx

"use client";

import { useEffect, useState } from "react";
import { socket } from "@/socket";

export default function ChatPage() {
  const [isConnected, setIsConnected] = useState(false);
  const [transport, setTransport] = useState("N/A");

  useEffect(() => {
    if (socket.connected) {
      onConnect();
    }

    function onConnect() {
      setIsConnected(true);
      setTransport(socket.io.engine.transport.name);

      socket.io.engine.on("upgrade", (transport) => {
        setTransport(transport.name);
      });
    }

    function onDisconnect() {
      setIsConnected(false);
      setTransport("N/A");
    }

    socket.on("connect", onConnect);
    socket.on("disconnect", onDisconnect);

    return () => {
      socket.off("connect", onConnect);
      socket.off("disconnect", onDisconnect);
    };
  }, []);

  return (
    <div>
      <p>Status: {isConnected ? "connected" : "disconnected"}</p>
      <p>Transport: {transport}</p>
    </div>
  );
}

 

그리고 클라이언트와 서버를 실행해주면..

기존에 연결이 안됐던 상태에서

 

이렇게 정상적으로 연결이 된 모습을 볼 수 있다.