icon안동민 개발노트

WebSocket 통합


 Next.js App Router 환경에서 WebSocket을 통합하는 것은 실시간 양방향 통신을 구현하는 강력한 방법입니다.

 이 절에서는 WebSocket의 개념, 장점, 그리고 Next.js 애플리케이션에서의 구현 방법에 대해 알아보겠습니다.

WebSocket의 장점

  1. 실시간 양방향 통신
  2. 낮은 지연 시간
  3. 효율적인 서버 리소스 사용
  4. 다양한 실시간 애플리케이션 구현 가능 (채팅, 실시간 협업 등)

Socket.io 라이브러리 설정

  1. 설치
npm install socket.io socket.io-client
  1. WebSocket 서버 설정 (server.js)
server.js
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const next = require('next');
 
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
 
app.prepare().then(() => {
  const server = express();
  const httpServer = http.createServer(server);
  const io = new Server(httpServer);
 
  io.on('connection', (socket) => {
    console.log('A user connected');
 
    socket.on('message', (data) => {
      io.emit('message', data);
    });
 
    socket.on('disconnect', () => {
      console.log('User disconnected');
    });
  });
 
  server.all('*', (req, res) => {
    return handle(req, res);
  });
 
  httpServer.listen(3000, (err) => {
    if (err) throw err;
    console.log('> Ready on http://localhost:3000');
  });
});

클라이언트에서의 WebSocket 연결 관리

 클라이언트 컴포넌트에서 WebSocket 연결을 관리합니다.

app/components/WebSocketComponent.js
'use client'
 
import { useEffect, useState } from 'react';
import io from 'socket.io-client';
 
let socket;
 
export default function WebSocketComponent() {
  const [message, setMessage] = useState('');
  const [messages, setMessages] = useState([]);
 
  useEffect(() => {
    socketInitializer();
 
    return () => {
      if (socket) {
        socket.disconnect();
      }
    };
  }, []);
 
  async function socketInitializer() {
    await fetch('/api/socket');
    socket = io();
 
    socket.on('message', (data) => {
      setMessages((prevMessages) => [...prevMessages, data]);
    });
  }
 
  const sendMessage = async () => {
    if (message.trim()) {
      socket.emit('message', message);
      setMessage('');
    }
  };
 
  return (
    <div>
      <ul>
        {messages.map((msg, index) => (
          <li key={index}>{msg}</li>
        ))}
      </ul>
      <input
        type="text"
        value={message}
        onChange={(e) => setMessage(e.target.value)}
      />
      <button onClick={sendMessage}>Send</button>
    </div>
  );
}

실시간 데이터 업데이트 구현

 WebSocket을 사용하여 실시간 데이터 업데이트를 구현할 수 있습니다.

// server-side
io.on('connection', (socket) => {
  socket.on('updateData', (data) => {
    // 데이터베이스 업데이트 로직
    io.emit('dataUpdated', updatedData);
  });
});
 
// client-side
socket.on('dataUpdated', (updatedData) => {
  setData(updatedData);
});

WebSocket과 서버 컴포넌트 사용

 서버 컴포넌트는 초기 데이터를 로드하고, 클라이언트 컴포넌트는 WebSocket을 통해 실시간 업데이트를 처리합니다.

app/page.js (Server Component)
import WebSocketComponent from './components/WebSocketComponent';
 
export default async function Home() {
  const initialData = await fetchInitialData();
 
  return (
    <main>
      <WebSocketComponent initialData={initialData} />
    </main>
  );
}
app/components/WebSocketComponent.js (Client Component)
'use client'
 
export default function WebSocketComponent({ initialData }) {
  const [data, setData] = useState(initialData);
 
  useEffect(() => {
    socket.on('dataUpdated', (updatedData) => {
      setData(updatedData);
    });
  }, []);
 
  // 렌더링 로직
}

7장 서버, 클라이언트 컴포넌트와의 연관성

 WebSocket 통합은 7장에서 다룬 서버, 클라이언트 컴포넌트의 개념과 밀접하게 연관됩니다.

 서버 컴포넌트는 초기 데이터 로딩을 담당하고, 클라이언트 컴포넌트는 WebSocket 연결을 관리하고 실시간 업데이트를 처리합니다.

 이러한 분리를 통해 서버 사이드 렌더링의 이점을 유지하면서도 동적인 실시간 기능을 구현할 수 있습니다.

실습 : 실시간 채팅 기능 구현

 WebSocket을 사용하여 실시간 채팅 기능을 구현해보겠습니다.

  1. 서버 설정 (server.js)
server.js
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const next = require('next');
 
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
 
app.prepare().then(() => {
  const server = express();
  const httpServer = http.createServer(server);
  const io = new Server(httpServer);
 
  io.on('connection', (socket) => {
    console.log('A user connected');
 
    socket.on('chat message', (msg) => {
      io.emit('chat message', msg);
    });
 
    socket.on('disconnect', () => {
      console.log('User disconnected');
    });
  });
 
  server.all('*', (req, res) => {
    return handle(req, res);
  });
 
  httpServer.listen(3000, (err) => {
    if (err) throw err;
    console.log('> Ready on http://localhost:3000');
  });
});
  1. 채팅 컴포넌트 구현 (app/components/Chat.js)
app/components/Chat.js
'use client'
 
import { useEffect, useState } from 'react';
import io from 'socket.io-client';
 
let socket;
 
export default function Chat() {
  const [message, setMessage] = useState('');
  const [messages, setMessages] = useState([]);
  const [username, setUsername] = useState('');
 
  useEffect(() => {
    socketInitializer();
    return () => {
      if (socket) {
        socket.disconnect();
      }
    };
  }, []);
 
  async function socketInitializer() {
    await fetch('/api/socket');
    socket = io();
 
    socket.on('chat message', (msg) => {
      setMessages((prevMessages) => [...prevMessages, msg]);
    });
  }
 
  const sendMessage = async (e) => {
    e.preventDefault();
    if (message.trim() && username) {
      const msgObject = { username, text: message };
      socket.emit('chat message', msgObject);
      setMessage('');
    }
  };
 
  return (
    <div>
      <div>
        <input
          type="text"
          value={username}
          onChange={(e) => setUsername(e.target.value)}
          placeholder="Enter your username"
        />
      </div>
      <ul>
        {messages.map((msg, index) => (
          <li key={index}>{msg.username}: {msg.text}</li>
        ))}
      </ul>
      <form onSubmit={sendMessage}>
        <input
          type="text"
          value={message}
          onChange={(e) => setMessage(e.target.value)}
          placeholder="Type a message"
        />
        <button type="submit">Send</button>
      </form>
    </div>
  );
}
  1. 채팅 페이지 생성 (app/chat/page.js)
app/chat/page.js
import Chat from '../components/Chat';
 
export default function ChatPage() {
  return (
    <main>
      <h1>Real-time Chat</h1>
      <Chat />
    </main>
  );
}

 이 실습을 통해 WebSocket을 사용하여 실시간 채팅 기능을 구현하고, Next.js App Router 애플리케이션과 통합하는 과정을 경험할 수 있습니다.

 WebSocket을 활용함으로써 낮은 지연 시간과 효율적인 양방향 통신을 구현할 수 있으며, 이는 실시간 협업 도구, 라이브 스트리밍 서비스, 실시간 대시보드 등 다양한 애플리케이션에 적용할 수 있습니다.