https://youtu.be/ZwFA3YMfkoc?t=354
npm install --save cors nodemon express socket.io
npm install --save react-router socket.io-client react-scroll-to-bottom react-emoji query-string
"scripts" {
"start": "nodemon index.js",
...
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
import React from 'react';
import Chat from './components/Chat/Chat';
import Join from './components/Join/Join';
import { BrowserRouter as Router, Route } from "react-router-dom";
const App = () => {
return (
<Router>
<Route path="/" exact component={Join} />
<Route path="/chat" component={Chat} />
</Router>
);
}
export default App;
import React, { useState, useEffect } from "react";
return (
<h1>임시</h1>
);
}
export default Chat;
import React, { useState, useEffect } from "react";
return (
<h1>임시</h1>
);
}
export default Chat;
const http = require('http');
const express = require('express');
const socketio = require('socket.io');
const cors = require('cors');
const { addUser, removeUser, getUser, getUsersInRoom } = require('./users');
const app = express();
const server = http.createServer(app);
const io = socketio(server);
server.listen(process.env.PORT || 5000, () => console.log(`Server has started.`));
1) 모듈 가져오기
2) PORT 지정하기
3) express()로 서버 만들기
4) http 만들기
5) io 서버 만들기
const express = require("express");
const router = express.Router();
router.get("/", (req, res) => {
res.send({ response: "Server is up and running." }).status(200);
});
module.exports = router;
const router = require('./router');
app.use(router)
io.on('connect', (socket) => {
socket.on(
console.log('we are here!');
socket.on('disconnect', () => {
const user = removeUser(socket.id);
})
});
import React, { useState } from 'react';
import { Link } from "react-router-dom";
import './Join.css';
export default function SignIn() {
const [name, setName] = useState('');
const [room, setRoom] = useState('');
return (
<div className="joinOuterContainer">
<div className="joinInnerContainer">
<h1 className="heading">Join</h1>
<div>
<input placeholder="Name" className="joinInput" type="text" onChange={(event) => setName(event.target.value)} />
</div>
<div>
<input placeholder="Room" className="joinInput mt-20" type="text" onChange={(event) => setRoom(event.target.value)} />
</div>
<Link onClick={e => (!name || !room) ? e.preventDefault() : null} to={`/chat?name=${name}&room=${room}`}>
<button className={'button mt-20'} type="submit">Sign In</button>
</Link>
</div>
</div>
);
}
1) onChange로 데이터 가져오기
2) link to에 쿼리스트링 형식으로 설정하기
import React, { useState, useEffect } from "react";
import queryString from 'query-string';
import io from "socket.io-client";
import TextContainer from '../TextContainer/TextContainer';
import Messages from '../Messages/Messages';
import InfoBar from '../InfoBar/InfoBar';
import Input from '../Input/Input';
import './Chat.css';
const ENDPOINT = 'https://project-chat-application.herokuapp.com/';
let socket;
const Chat = ({ location }) => {
const [name, setName] = useState('');
const [room, setRoom] = useState('');
const [users, setUsers] = useState('');
const [message, setMessage] = useState('');
const [messages, setMessages] = useState([]);
useEffect(() => {
const { name, room } = queryString.parse(location.search);
socket = io(ENDPOINT);
setRoom(room);
setName(name)
socket.emit('join', { name, room }, (error) => {
if(error) {
alert(error);
}
});
}, [ENDPOINT, location.search]);
useEffect(() => {
socket.on('message', message => {
setMessages(messages => [ ...messages, message ]);
});
socket.on("roomData", ({ users }) => {
setUsers(users);
});
}, []);
const sendMessage = (event) => {
event.preventDefault();
if(message) {
socket.emit('sendMessage', message, () => setMessage(''));
}
}
return (
<div className="outerContainer">
<div className="container">
<InfoBar room={room} />
<Messages messages={messages} name={name} />
<Input message={message} setMessage={setMessage} sendMessage={sendMessage} />
</div>
<TextContainer users={users}/>
</div>
);
}
export default Chat;
1) join에서 쿼리스트링으로 정보를 담에 Chat.js로 넘겼다
( 리덕스 관리로 해도 되는 정보지만, 뭐 그렇게 해도 좋지)
import queryString from 'query-string';
const { name, room } = queryString.parse(location.search);
2) 소켓과 연결하기
let socket
const ENDPOINT = 'localhost:5000'
socket = io(ENDPOINT)
3) join 이벤트와 전달할 데이터 정하기
socket.emit("join", {name, room})
4) useEffect return 전에는 join 이벤트를 emit 시키고, 이후에는 disconnect 이벤트를 emit 시킨다
io.on('connect', (socket) => {
socket.on('join', ({ name, room }, callback) => {
const { error, user } = addUser({ id: socket.id, name, room });
if(error) return callback(error);
socket.join(user.room);
socket.emit('message', { user: 'admin', text: `${user.name}, welcome to room ${user.room}.`});
socket.broadcast.to(user.room).emit('message', { user: 'admin', text: `${user.name} has joined!` });
io.to(user.room).emit('roomData', { room: user.room, users: getUsersInRoom(user.room) });
callback();
});
socket.on('sendMessage', (message, callback) => {
const user = getUser(socket.id);
io.to(user.room).emit('message', { user: user.name, text: message });
callback();
});
socket.on('disconnect', () => {
const user = removeUser(socket.id);
if(user) {
io.to(user.room).emit('message', { user: 'Admin', text: `${user.name} has left.` });
io.to(user.room).emit('roomData', { room: user.room, users: getUsersInRoom(user.room)});
}
})
});
1) join 이벤트 발생시 데이터와 컬백함수를 전달받다.
io.on('connect', (socket) => {
socket.on('join', ({ name, room }, callback) => {
const { error, user } = addUser({ id: socket.id, name, room });
if(error) return callback(error);
socket.join(user.room);
socket.emit('message', { user: 'admin', text: `${user.name}, welcome to room ${user.room}.`});
socket.broadcast.to(user.room).emit('message', { user: 'admin', text: `${user.name} has joined!` });
io.to(user.room).emit('roomData', { room: user.room, users: getUsersInRoom(user.room) });
callback();
});
socket.on('sendMessage', (message, callback) => {
const user = getUser(socket.id);
io.to(user.room).emit('message', { user: user.name, text: message });
callback();
});
socket.on('disconnect', () => {
const user = removeUser(socket.id);
if(user) {
io.to(user.room).emit('message', { user: 'Admin', text: `${user.name} has left.` });
io.to(user.room).emit('roomData', { room: user.room, users: getUsersInRoom(user.room)});
}
})
});
const users = [];
const addUser = ({ id, name, room }) => {
name = name.trim().toLowerCase();
room = room.trim().toLowerCase();
const existingUser = users.find((user) => user.room === room && user.name === name);
if(!name || !room) return { error: 'Username and room are required.' };
if(existingUser) return { error: 'Username is taken.' };
const user = { id, name, room };
users.push(user);
return { user };
}
const removeUser = (id) => {
const index = users.findIndex((user) => user.id === id);
if(index !== -1) return users.splice(index, 1)[0];
}
const getUser = (id) => users.find((user) => user.id === id);
const getUsersInRoom = (room) => users.filter((user) => user.room === room);
module.exports = { addUser, removeUser, getUser, getUsersInRoom };
..
const app = express()
const server = http.createServer(app)
cconst io = socketio(server)
io.on('connection', (socket)=> ..
io.on('connection', (socket)=>{
socket.on('join', ({ name, room}, callback) =>{
const{ error, user} = addUser({id: socket.id, name, room})
if (error) return callback(error);
...
io.on('connection', (socket)=>{
socket.on('join', ({ name, room}, callback) =>{
const{ error, user} = addUser({id: socket.id, name, room})
if (error) return callback(error);
socket.emit('message', { user: 'admin', text: `${user.name}, welcome to room ${user.room}.`});
io.on('connect', (socket) => {
socket.on('join', ({ name, room }, callback) => {
const { error, user } = addUser({ id: socket.id, name, room });
if(error) return callback(error);
socket.join(user.room);
socket.emit('message', { user: 'admin', text: `${user.name}, welcome to room ${user.room}.`});
socket.broadcast.to(user.room).emit('message', { user: 'admin', text: `${user.name} has joined!` });
const [name, setName] = useState('');
const [room, setRoom] = useState('');
const [users, setUsers] = useState('');
const [message, setMessage] = useState('');
const [messages, setMessages] = useState([]);
...
setRoom(room);
setName(name)
..
const { name, room } = queryString.parse(location.search);
2) socket을 io(서버) 로 정의한다
socket = io(ENDPOINT);
3) 값을 세팅한다 (useState)
setRoom(room);
setName(name)
4) 이벤트를 발생시킨다
socket.emit('join', { name, room }, (error) => {
if(error) {
alert(error);
}
});
5) ENDPOINT와 location.search가 바뀔 때만 리렌더링한다
(전체파일)
useEffect(() => {
const { name, room } = queryString.parse(location.search);
socket = io(ENDPOINT);
setRoom(room);
setName(name)
socket.emit('join', { name, room }, (error) => {
if(error) {
alert(error);
}
});
}, [ENDPOINT, location.search]);
useEffect(2): 메시지를 보낼 경우 /
useEffect(() => {
socket.on('message', message => {
setMessages(messages => [ ...messages, message ]);
});
socket.on("roomData", ({ users }) => {
setUsers(users);
});
}, []);
return 하는 컴포넌트
return (
<div className="outerContainer">
<div className="container">
<InfoBar room={room} />
<Messages messages={messages} name={name} />
<Input message={message} setMessage={setMessage} sendMessage={sendMessage} />
</div>
<TextContainer users={users}/>
</div>
);
event.prevent
기본 이벤트를 블록하는 것이다.