1. 기본 정보
- 환경
- Front: React, Socket.io-client
- Back: Node, Express, Socket.io
- 목표
- socket.io을 활용하여 간단한 채팅 앱 만들기
- React + Node (express)
- DB x
2. 세팅
React 생성 -> npx create-vite@latest client (VITE)
Node 생성 -> npm init -y
- 라이브러리 설치
- front: npm install socket.io-client (socket.io-client)
- back: npm install socket.io (socket.io) / npm install -D nodemon ts-node-dev @types/express @types/node tsconfig-paths typescript
back init code
const PORT = process.env.PORT || 5000;
const app = express();
const expressServer = app.listen(PORT, () => {
console.log(`서버가 포트번호 ${PORT}에서 정상 작동중입니다.`);
});
3. Socket.io 이용
전체적인 흐름정리
- 백 소켓 - 클라이언트 소켓 연결 -> 1. 백에서 연결됨을 알림 -> 2. 클라이언트 해당 내용을 렌더링 -> 3. 클라이언트에서 입력된 메시지 서버로 전송 -> 4. 서버에서 해당 내용 받아서 다시 클라이언트로 전성(보통 이떄 메시지를 db에 저장) -> 5. 클라이언트에서 서버로부터 받은 데이터를 렌더링
Back
- socket.io 설정
const io = new Server(expressServer, {
cors: {
origin: process.env.NODE_ENV === 'production' ? false : ['http://localhost:3000']
}
});
- 연결
io.on('connection', (socket) => {
// broadcast => 해당 메신자의 송신자는 제외함 (자세한 내용: https://socket.io/docs/v3/broadcasting-events/)
socket.broadcast.emit('message', `User ${socket.id.substring(0, 5)}가 연결되었습니다.`);
// 1. 백에서 연결됨을 알림
socket.on('message', (data) => {
io.emit('message', `${socket.id.substring(0, 5)}: ${data}`);
// 4. 서버에서 클라이언트의 입려값(data) 받아서 다시 클라이언트로 전성(보통 이떄 메시지를 db에 저장)
});
});
Front
- socket.io-client 설정
const [socket, setSocket] = useState<Socket | null>(null);
useEffect(() => {
const newSocket = io("http://localhost:5000");
setSocket(newSocket);
return () => {
newSocket.disconnect();
};
}, []);
(useEffect에서 해당 로직을 시키는 이유 => state 변경등으로 인한 컴포넌트 재렌더링마다 새로운 연결을 할것이 아니므로, 의존성을 []로 설정해 첫 로딩시에만 연결하도록 설정)
- 메시지 수신 / 송신
useEffect(() => {
if (socket) {
socket.on("message", (data) => {
setMessages((prev) => [...prev, data]);
});
// 2. 클라이언트 해당 내용을 렌더링 / 5. 클라이언트에서 서버로부터 받은 데이터를 렌더링
}
return () => {
if (socket) {
socket.off("message");
}
};
}, [socket]);
const submitHandler = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (message !== "" && socket) {
socket.emit("message", message);
setMessage("");
}
};
// 3. 클라이언트에서 입력된 메시지 서버로 전송
++ TIP
- 메시지 입력중인거 캐치하기
// back
io.on('connection', (socket) => {
socket.broadcast.emit('message', `User ${socket.id.substring(0, 5)}가 연결되었습니다.`);
socket.on('message', (data) => {
io.emit('message', `${socket.id.substring(0, 5)}: ${data}`);
});
// 추가된 부분
socket.on('activity', (name) => {
socket.broadcast.emit('activity', name);
});
});
// client
const [activity, setActivity] = useState("");
const [isTyping, setIsTyping] = useState(false);
const typingTimeoutRef = useRef<number | null>(null);
useEffect(() => {
if (socket) {
// 추가된 부분
socket.on("activity", (name) => {
setActivity(`${name}님이 작성중입니다,,,`);
if (typingTimeoutRef.current) {
clearTimeout(typingTimeoutRef.current);
}
typingTimeoutRef.current = setTimeout(() => {
setActivity("");
}, 1000);
});
socket.on("message", (data) => {
setMessages((prev) => [...prev, data]);
});
}
...
// 추가된 부분
const keyDownHandler = () => {
if (socket && !isTyping) {
socket.emit("activity", socket.id.substring(0, 5));
setIsTyping(true);
}
};
const keyUpHandler = () => {
setIsTyping(false);
};
return (
<div>
<form onSubmit={submitHandler}>
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
onKeyDown={keyDownHandler}
onKeyUp={keyUpHandler}
/>
<button type="submit">Send</button>
</form>
<ul>
{messages.map((msg, idx) => (
<li key={idx}>{msg}</li>
))}
</ul>
<p>{activity}</p>
</div>
);
4. 데모
깃허브: Github-chatapp
'OLD > chat-app' 카테고리의 다른 글
[ChatApp] Socket.io를 활용하여 간단한 채팅 어플 만들어보기 - 03. Clerk을 이용한 인증 구현 (+ prisma) (0) | 2023.10.15 |
---|---|
[ChatApp] Socket.io를 활용하여 간단한 채팅 어플 만들어보기 - 02. shadcn-ui를 이용한 스타일링 (2) | 2023.10.14 |