프로젝트를 하며 WebSocket을 이용해 채팅 기능을 구현할 일이 생겼는데, 채팅의 기술 원리를 잘 모르고 무작정 개발하니 백엔드와 소통도 잘 안되는 것 같아... node.js로 직접 구현해보며 공부해보기로 했다.
기술 스택
- client : react, typescript
- server : node.js, express, socket.io
- DB: 추후에 mongoDB 연결 예정
완성 화면 미리보기
1. react 세팅
vite로 설치해줬다.
$ npm create vite@latest
Need to install the following packages:
create-vite@5.5.4
Ok to proceed? (y) y
> npx
> create-vite
√ Project name: ... chat-client
√ Select a framework: » React
√ Select a variant: » TypeScript
2. node.js 세팅
서버 디렉토리 생성
npm init -y
npm install express --save
npm init -y로 package.json 파일을 만든 후 express를 설치한다.
//app.js
import express from 'express';
import { createServer } from 'node:http';
const app = express();
const server = createServer(app);
app.get('/', (req, res) => {
res.send('<h1>Hello world</h1>');
});
server.listen(3000, () => {
console.log('server running at <http://localhost:3000>');
});
터미널에 node app.js를 입력하면 서버가 켜지고 러닝중이라는 콘솔 로그가 찍힌다.
socket.io 설치
https://socket.io/docs/v4/tutorial/step-1
import express from "express";
import { createServer } from "node:http";
import { Server } from "socket.io";
const app = express();
const server = createServer(app);
server.listen(3000, () => {
console.log("server running at <http://localhost:3000>");
});
const io = new Server(server, {
cors: {
origin: ["<http://localhost:5173>"],
},
});
app.get("/", (req, res) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Socket.IO Test</title>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io();
socket.on('connect', () => {
console.log('Connected to server');
});
socket.on('disconnect', () => {
console.log('Disconnected from server');
});
</script>
</head>
<body>
<h1>Socket.IO Test</h1>
</body>
</html>
`);
});
io.on("connection", (socket) => {
console.log("a user connected");
socket.on("disconnect", () => {
console.log("user disconnected");
});
});
클라이언트 코드를 만들기 전 서버 내에서 socket이 잘 적용되는지 확인하고 싶어서 res.send 내부에 socket을 넣어서 로그를 찍었다.
localhost:3000에 접속했을 때 로그가 잘 찍히네요!
cors origin에는 클라이언트 주소를 입력해줍니다. react -vite 기본 포트인 5173으로 설정했어요.
3. client
socket.io client를 설치해준다.
npm i socket.io-client
import { useEffect, useState, FormEvent } from "react";
import { io } from "socket.io-client";
import "./App.css";
interface MessageProps {
text: string;
username: string;
timestamp: string;
}
const App = () => {
const socket = io("<http://localhost:3000>");
const [messages, setMessages] = useState<MessageProps[]>([]);
const [messageInput, setMessageInput] = useState("");
const username = "테스트";
useEffect(() => {
socket.on("connect", () => {
console.log("connected");
});
socket.on("message", (message) => {
setMessages((prev) => [...prev, message]);
console.log(message);
});
return () => {
socket.off("connect");
socket.off("message");
};
}, []);
const sendMessage = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (messageInput.trim()) {
const messageData = {
text: messageInput,
username: username,
timestamp: new Date().toISOString(),
};
socket.emit("send_message", messageData);
setMessageInput("");
}
};
return (
<>
{messages.map((msg, idx) => (
<div key={idx}>
<div>
<span>{msg.username}</span>
<span>{msg.text}</span>
<span>{new Date(msg.timestamp).toLocaleTimeString()}</span>
</div>
</div>
))}
<form onSubmit={sendMessage}>
<div>
<input
type="text"
value={messageInput}
onChange={(e) => setMessageInput(e.target.value)}
placeholder="메시지를 입력하세요..."
/>
<button type="submit">전송</button>
</div>
</form>
</>
);
};
export default App;
간단하게 메세지를 보내는 폼과 도착한 메세지를 매핑하는 화면을 만들었다.
4. server 수정
import express from "express";
import { createServer } from "node:http";
import { Server } from "socket.io";
const app = express();
const server = createServer(app);
server.listen(3000, () => {
console.log("server running at http://localhost:3000");
});
const io = new Server(server, {
cors: {
origin: ["http://localhost:5173"],
},
});
io.on("connection", (socket) => {
console.log(`a user connected: ${socket.id}`);
socket.on("send_message", (messageData) => {
console.log(messageData);
io.emit("message", messageData);
});
socket.on("disconnect", () => {
console.log(`User disconnected: ${socket.id}`);
});
});
get으로 html을 그리기 위한 코드를 지워줬다.
const socket = io("http://localhost:3000");
const socket = io("ws://localhost:3000");
WebSocket은 http라는 프로토콜을 이용하니까 ws:// 에서만 소켓이 연결될 줄 알았는데, http에서도 된다. socket.io 내부에서 내부적으로 ws로 변경시켜주는게 아닐까 싶다.
WebSocket을 쓰면 데이터를 보낼 때 문자열로 변형시켜서 보내야하는데, socket.io를 사용하면 json을 그대로 써도 된다.
css는 shadcn/ui 도입하면서 조금 달라졌습니다!
Repository 바로가기 (계속 업데이트 됨)
https://github.com/eunjin11/chat-app
'프로젝트 > chat-app' 카테고리의 다른 글
react-router로 채팅방 라우팅하기 (0) | 2024.12.10 |
---|---|
채팅방 리스트 목록 구현하기 (0) | 2024.12.08 |
shadcn/ui 도입하기 (1) | 2024.12.06 |
채팅방 스키마, API 만들기 (mongoDB, thunderClient) (0) | 2024.12.05 |
채팅 앱 mongoDB 연결하기 (0) | 2024.12.05 |