Published On

Next.js 커스텀 서버


Socket.IO 실시간 채팅 구현

Next.js는 기본적으로 정적 사이트 생성(SSG) 및 서버 사이드 렌더링(SSR)을 지원하지만, 실시간 통신 기능은 제공하지 않습니다.

이런 경우 Socket.IO를 사용해 실시간 통신을 구현할 수 있으며,

이를 위해 별도의 백엔드 서버를 만들지 않고 nextjs 프로젝트 내부에서 커스텀 서버를 설정하는 방법을 사용합니다.

이번 글에서는 Next.js에서 Socket.IO로 실시간 채팅 애플리케이션을 만드는 방법을 단계별로 설명합니다.


1. 프로젝트 설정

먼저 커스텀 서버를 만들어 Next.jsSocket.IO를 함께 실행하는 설정을 합니다. 커스텀 서버를 통해 Next.js 애플리케이션과 Socket.IO 서버를 동일한 HTTP 서버에서 운영할 수 있습니다.

2. server.js 파일 생성

프로젝트 루트 디렉토리에 server.js 파일을 생성하여, HTTP 서버와 Socket.IO 서버를 통합합니다. 이 서버는 Next.js 애플리케이션과 실시간 통신을 위한 Socket.IO 서버를 동시에 실행시킵니다.

server.jsjavascript
import { createServer } from "http";
import next from "next";
import { Server } from "socket.io";

const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();

const port = process.env.PORT || 3000;

app.prepare().then(() => {
  const httpServer = createServer(handle);
  const io = new Server(httpServer);

  io.on("connection", (socket) => {
    console.log("A user connected:", socket.id);

    socket.on("message", (msg) => {
      io.emit("message", msg);
    });

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

  httpServer.listen(port, (err) => {
    if (err) throw err;
    console.log(`> Ready on http://localhost:${port}`);
  });
});

3. 클라이언트에서 설정

파일을 생성하여 서버에 소켓 연결을 생성합니다. 이 파일은 클라이언트에서 실시간 소켓 연결을 초기화하고 관리하는 역할을 합니다.

src/socket.jsjavascript
// src/socket.js
"use client"; // 클라이언트 전용 설정

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

export const socket = io(); // Socket.IO 클라이언트 연결 생성

4. 클라이언트 페이지에서 실시간 메시지 처리

이제 실시간 채팅 기능을 페이지에서 구현합니다.(소켓 연결)

useState와 useEffect를 사용하여 실시간으로 소켓을 통해 전송된 메시지를 주고받고 화면에 출력합니다.

src/app/page.tsxjavascript
"use client";

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

export default function Home() {
  const [messages, setMessages] = useState<string[]>([]);
  const [newMessage, setNewMessage] = useState<string>("");

  useEffect(() => {
    socket.on("message", (msg: string) => {
      setMessages((prevMessages) => [...prevMessages, msg]);
    });

    return () => {
      socket.off("message");
    };
  }, []);

  const sendMessage = () => {
    socket.emit("message", newMessage);
    setNewMessage("");
  };

  return (
    <div>
      <h1>Real-time Chat</h1>
      <div>
        {messages.map((msg, index) => (
          <p key={index}>{msg}</p>
        ))}
      </div>
      <input
        type="text"
        value={newMessage}
        onChange={(e) => setNewMessage(e.target.value)}
        placeholder="Enter your message"
      />
      <button onClick={sendMessage}>Send</button>
    </div>
  );
}

5.package.json 수정

package.jsonjson
{
  "scripts": {
    "dev": "node server.js",
    "build": "next build",
    "start": "NODE_ENV=production node server.js",
    "lint": "next lint"
  }
}

모든 설정이 완료되면, 개발 서버(npm run dev)를 실행합니다.


커스텀 서버와 Express 서버 비교

항목Next.js 커스텀 서버Express 서버
설치 및 설정Next.js와 결합된 커스텀 서버를 설정해야 하므로 약간의 추가 설정이 필요함간단한 npm init 및 Express 설치 후 빠르게 서버 구축 가능
실시간 통신Socket.IO 등을 통합해 실시간 기능 구현 가능Socket.IO 및 WebSocket을 쉽게 추가 가능, 독립적 설정 가능
배포 플랫폼Vercel에서는 WebSocket을 지원하지 않음, Heroku 또는 DigitalOcean 등에서 배포 필요Heroku, AWS, DigitalOcean 등 대부분의 플랫폼에서 쉽게 배포 가능
서버 확장성기본적으로 Next.js 기능을 확장해 사용, 하지만 성능 최적화는 제한적일 수 있음클러스터링, 로드 밸런싱 등 서버 확장이 매우 유연함
유연성Next.js에 맞춰 동작, 특정 기능에 제약이 있을 수 있음매우 유연하며 REST API, GraphQL, 파일 서버 등 다양한 방식으로 사용 가능
사용 목적Next.js에서 기본적으로 제공하는 기능을 최대한 활용할 때 적합전통적인 API 서버 구축, 파일 전송 및 다양한 미들웨어 설정이 필요할 때 적합
미들웨어 관리커스텀 서버에서 Express 미들웨어를 추가할 수 있으나 설정이 복잡해질 수 있음미들웨어를 쉽게 추가하고 관리할 수 있음 (e.g., Morgan, Cors)
라우팅Next.js는 페이지 기반의 파일 라우팅을 사용라우팅을 자유롭게 설정 가능하며, RESTful API 구축에 더 적합
SSR/SSG 지원SSR과 SSG를 쉽게 구현할 수 있음 (Next.js 기본 기능)Express 자체에서는 SSR/SSG가 기본적으로 제공되지 않음, 직접 구현 필요
학습 곡선Next.js 커스텀 서버는 다소 복잡할 수 있음Express는 비교적 간단하고 빠르게 배울 수 있음

이전 포스트

Socket.IO 란?

다음 포스트

React hook 이란?