JinHee's Board
Project - Socket과 ElasticSearch 를 활용한 채팅 사이트 개발일지 [4] 본문
Project - Socket과 ElasticSearch 를 활용한 채팅 사이트 개발일지 [3]
구현 결과
- 로그인시 현재 참여중인 채팅방 목록 표시
- 사용자간의 채팅 구현
채팅방 목록 표시
Elastic Index 생성 ( 채팅방 목록 )
채팅방 구분에 필요한 키와 그 외의 데이터를 담을 인덱스를 생성한다.
인덱스 생성에 필요한 매핑을 설정한다. ( 필드 타입 지정 등.. )
PUT chat_room
{
"mappings" : {
"properties" : {
"room_date" : {
"type" : "date",
"ignore_malformed" : true
},
"room_id" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword"
}
}
},
"room_name" : {
"type" : "text"
},
"room_users" : {
"type" : "text"
}
}
}
}
room_id : 채팅방 구분 키 , room_name : 채팅방 이름
room_users : 채팅방 참여자 id 목록 (구분자 ,) , room_date : 채팅방 생성일
삽입된 데이터 예시)
서버(Back) 단 코드 수정
서버에서는 화면에서 로그인시 ElasticSearch 서버에 로그인한 id가 포함되어있는 채팅방 목록 검색을 실행하고
그 결과를 리턴할 소켓을 생성한다. ( 채팅방 목록 검색시 query_string 을 사용했다 )
App.js 에 아래 코드 추가
socket.on('loadRoom',(user) => {
console.log("Call Room Lists");
let callList = esService.search("chat_room", {
"query": {
"query_string": {
"query": "room_users : *"+user+"*"
}
}
});
callList.then(function (result) {
socket.emit('renderlist', result.hits.hits); // 클라이언트(화면) 에게 결과 전송
})
});
화면(Front) 단 코드 수정
로그인에 성공하면 histroy.push 를 실행하여 채팅방 목록이 있는 페이지로 이동한다.
socket.on('checkLogin', (user) => {
history.push({
pathname : '/roomList/' + user,
user_id : user
})
})
위의 pathname에서 /roomList/ 경로로 이동하기 위해서 App.js에 라우터를 추가시킨다.
App.js 수정
import Login from "./routes/Login"
import RoomList from './routes/RoomList';
function App() {
return <Router>
<Switch>
<Route path="/roomList/:id" component={RoomList}/>
<Route path="/" component={Login}/>
</Switch>
</Router>;
}
그다음 routes 디렉토리 하위에 RoomList.js파일을 추가한다.
RoomList에는 아래와 같은 기능들이 필요하다.
- 페이지에 이동하자마자 채팅방 목록 출력
- 채팅방 목록중 하나를 클릭하면 채팅방으로 입장
채팅방 목록 출력의 경우 페이지에 이동후 서버에게 한번만 요청해야 하므로 useEffect 를 사용하고 서버로부터 결과를 받아야 한다.
const [room, setRoom] = useState([]);
useEffect(()=>{
socket.emit('loadRoom', location.user_id);
}, []);
socket.on('renderlist', (room) => {
setRoom(room);
});
또한 채팅방 목록에서 각 채팅방을 클릭하면 페이지 이동이 필요하다.
const connect_room = (roomInfo) => {
history.push({
pathname : '/room/' + roomInfo.target.id,
room_id : roomInfo.target.id,
user_id : location.user_id
})
}
RoomList.js 생성
import React from 'react';
import { useState, useEffect } from "react";
import io from "socket.io-client";
import "../css/Room_List.css";
const socket = io("http://localhost:3001/"); //3001 Back단 서버포트
function RoomList({location, history}) {
const [room, setRoom] = useState([]);
useEffect(()=>{
socket.emit('loadRoom', location.user_id);
}, []);
socket.on('renderlist', (room) => {
setRoom(room);
});
const connect_room = (roomInfo) => {
history.push({
pathname : '/room/' + roomInfo.target.id,
room_id : roomInfo.target.id,
user_id : location.user_id
})
}
return <div>
<div id="roomList">
{location.user_id}
</div>
<div className='room_container'>
{
room.map((ele) =>
<div className="room_element" key={ele._source.room_id} id={ele._source.room_id} onClick={connect_room}>
{ele._source.room_name}
</div>
)
}
</div>
</div>
}
export default RoomList
채팅방 목록 구현 결과
채팅 구현
각 방에 참여되어 있는 사용자간의 채팅을 구현한다.
예를 들어 1번방에 A,와 B가 참여하고 있고 2번방에는 C와 D가 참여중이라면 A와 B의 채팅내용을 C와 D는 볼수 없도록 한다.
서버(Back) 단 코드 수정
먼저 채팅방에 입장할 때 해당 채팅방의 id 정보를 전달받을 소캣을 생성한다.
채팅방의 id정보는 참여중인 채팅방 구분에 사용된다.
App.js 수정
socket.on('enterRoom', (roomid) => {
socket.join(roomid); // 방마다 구분하는 소캣 join
console.log("방 들어감")
})
socket.on('reaveRoom', (roomid) => {
socket.leave(roomid);
})
socket.on('send', (user, message, room_id) => {
console.log('메시지전송: ' + user);
//채팅 메세지를 전달
socket.broadcast.to(room_id).emit('recept_message', message, user);
});
채팅방 구분에 사용되는 부분은 socket.join 이다.
socket.join을 통해 어디에 join되어있는지 설정하면 send가 실행되어 채팅 메시지를 broadcast.to 를 통해 전달 할때 join되어 있는 방에만 broadcast 할 수 있다.
화면(Front) 단 코드 수정
RoomList 에서 histroy.push 를 통해 해당 경로로 이동하기 위해 App.js 에 router를 추가한다.
App.js 수정
import Login from "./routes/Login"
import RoomList from './routes/RoomList';
import Room from './routes/Room';
function App() {
return <Router>
<Switch>
<Route path="/room/:roomid" component={Room}/>
<Route path="/roomList/:id" component={RoomList}/>
<Route path="/" component={Login}/>
</Switch>
</Router>;
}
Room.js에는 채팅방에 처음입장시 채팅방 정보를 서버로 전달한다.
const [roomid, setRoomId] = useState("");
useEffect(() => {
setRoomId(location.room_id);
socket.emit('enterRoom', location.room_id);
}, [])
또한 채팅 내용을 입력할 수 있고 채팅 한 내용을 화면에 표시해야 한다.
const [message, setMessage] = useState("");
const [messageLog, setLog] = useState([]);
useEffect(() => {
socket.on('recept_message', (message, user) => {
addMessage(user, message, 'other')
});
}, [])
const addMessage = (name, message, target) => {
let message_div = {
"userName" : name,
"message" : message,
"type" : target
}
if(message_div.type == 'me') message_div.userName = 'me'
setLog((currentArray) => [ ...currentArray,message_div])
}
const doSend = (event) => {
socket.emit('send', location.user_id ,message, roomid); // send 요청
addMessage(location.user_id, message, 'me') // 화면에 채팅내용 표시
setMessage(""); // 메세지 입력창 초기화
event.preventDefault();
}
const onInputMessage = (event) => setMessage(event.target.value);
채팅내용을 화면에 표시할 때는 채팅한 사람이 본인인지 아니면 다른 사람인지 구분해야 한다.
dosend 에서는 채팅을 한사람이 본인 이기 때문에 addMessage를 실행할때 'me' 값을 같이 전달하며
다른 사람의 채팅을 전달받는 recept_message 소캣에서는 addMessage를 실행할때 'other'을 전달한다.
이를 통해 채팅화면에서 내가 작성한 채팅은 me : [내용] 으로 표시하고 다른 사람이 작성한 채팅은[ 작성자id ] : [내용] 으로 표시된다.
(addMessage 의 if 부분 참조)
Room.js 생성
import React from 'react';
import ReactDOM from 'react-dom/client';
import { useState, useEffect } from "react";
import io from "socket.io-client";
const socket = io("http://localhost:3001/");
function Room({ location, history }) {
const [roomid, setRoomId] = useState("");
const [message, setMessage] = useState("");
const [messageLog, setLog] = useState([]);
useEffect(() => {
setRoomId(location.room_id);
socket.emit('enterRoom', location.room_id);
}, [])
useEffect(() => {
socket.on('recept_message', (message, user) => {
addMessage(user, message, 'other')
});
}, [])
const addMessage = (name, message, target) => {
let message_div = {
"userName" : name,
"message" : message,
"type" : target
}
if(message_div.type == 'me') message_div.userName = 'me'
setLog((currentArray) => [ ...currentArray,message_div])
}
const onInputMessage = (event) => setMessage(event.target.value);
const doSend = (event) => {
socket.emit('send', location.user_id ,message, roomid); // check 요청
addMessage(location.user_id, message, 'me')
setMessage("");
event.preventDefault();
}
const goList = (event) => {
socket.emit('reaveRoom', location.room_id);
history.push({
pathname : '/roomList/' + location.user_id,
user_id : location.user_id
})
event.preventDefault();
}
return <div>
<div>
대화내용
</div>
<div>
<form onSubmit={doSend}>
<div id="message_window">
{messageLog.map((message_div, index) => (
<div className={message_div.user} key={index}>{message_div.userName} : {message_div.message}</div>
))
}
</div>
<div>
<input onChange={onInputMessage} type="text" value={message} id="user_message" placeholder="메시지를 입력하세요" /><br />
</div>
<div>
<button onClick={doSend} id="doSend">전송</button>
</div>
</form>
<div>
<button onClick={goList} id="goList">뒤로</button>
</div>
</div>
</div>
}
export default Room
채팅방 구현 결과
개발일지는 여기까지만 작성하지만 차후에는 사용자 목록을 조회하고 사용자들을 초대해 채팅방을 추가하는 등 여러 기능을 추가할 예정이다.
Socket과 ElasticSearch 를 활용한 채팅 사이트 개발일지 마침-
'공부한 내용정리 > 기타' 카테고리의 다른 글
NIFI - Custom Processor 만들어보기 (0) | 2024.03.17 |
---|---|
Project - Socket과 ElasticSearch 를 활용한 채팅 사이트 개발일지 [3] (0) | 2022.05.08 |
Project - Socket과 ElasticSearch 를 활용한 채팅 사이트 개발일지 [2] (0) | 2022.04.23 |
Project - Socket과 ElasticSearch 를 활용한 채팅 사이트 개발일지 [1] (2) | 2022.04.09 |
Apache Spark - OpenSearch 연동 문제 해결 (0) | 2021.10.30 |