마이스터고등학교에 재학하며 다양한 프로젝트 경험을 쌓을 수 있었습니다.
다양한 프로젝트를 진행하며 여러 기술을 사용해보았지만, 정작 꼭 사용해보고 싶었던 기술인 Websocket기술을 사용해 본 경험이 전무했습니다.
마침 제가 꼭 지원하고 싶은 회사에서 Websocket기술을 바탕으로 한 서비스를 개발한다는 소식을 듣고, Websocket을 이용한 간단한 채팅 앱을 구현해보도록 하겠습니다.
백엔드 먼저 설정한 후 리액트 프로젝트를 설정하겠습니다.
터미널
mkdir back
cd back
express socket_project
cd socket_project
npm init -y
npm install socket.io // socket.io 설치
npm install express //express 설치
npm install
이제 app.js파일을 생성한 후 app.js파일의 기본 세팅을 해주겠습니다.
//app.js
const httpServer = require("http").createServer();
const io = require("socket.io")(httpServer, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
},
});//cors 오류로 인한 설정
io.on("connection", (socket) => {
console.log("connection");
socket.on("init", (payload) => {
console.log(payload);
});
});
httpServer.listen(80);
이제 클라이언트 세팅을 해주겠습니다.
간단한 프로젝트이므로 cra를 통해 세팅해주겠습니다.
cd ..// 루트 디렉토리로 이동
create-react-app front
cd front
npm install socket.io-client
//app.js
import React, { useEffect } from "react";
import "./App.css";
import io from "socket.io-client";
const socket = io.connect("http://localhost:80");
socket.emit("init", { name: "jaehoon" });
function App() {
useEffect(() => {
return () => {
socket.close();
};
}, []);
return (
<div>
<input></input>
</div>
);
}
export default App;
이제 서버와 클라이언트를 실행시키면
다음과 같이 정상적으로 연결됨을 확인할 수 있습니다.
이제 본격적으로 채팅 앱을 만들기 전 소켓 통신의 흐름에 대해 살펴보도록 하겠습니다.
클라이언트 소켓은 처음 생성 한 후
(import io from "socket.io-client";)
서버 측에 연결을 요청합니다.
(const socket = io.connect("http://localhost:80");)
서버 소켓에 연결이 받아지면 데이터를 송수신하고
(socket.emit("init", { name: "jaehoon" });)
모든 처리가 완료되면 소켓을 닫습니다.
(socket.close();)
자 이제 본격적으로 채팅 앱을 위한 클라이언트의 스타일을 작성하도록 하겠습니다.
//App.js
import React, { useEffect } from "react";
import "./App.css";
import io from "socket.io-client";
const socket = io.connect("http://localhost:80");
socket.emit("init", { name: "jaehoon" });
function App() {
useEffect(() => {
return () => {
socket.close();
};
}, []);
return (
<div className="App">
<div className="Box">
<div className="ChatBox">
</div>
<div className="InputBox">
<input placeholder="내용"></input>
<input placeholder="이름"></input>
<button>등록</button>
</div>
</div>
</div>
);
}
export default App;
//App.css
.App {
width: 100vw;
height: 100vh;
padding : 0;
display: flex;
justify-content: center;
align-items: center;
}
.Box{
width:40%;
height: 80%;
border:1px solid black;
border-radius: 1rem;
}
.ChatBox{
border:1px solid black;
border-radius: 1rem;
height:95%;
width: 100%;
display: flex;
justify-content: center;
}
.InputBox{
display: flex;
height: 5%;
flex:1;
justify-content: center;
align-items: center;
}
.Chat{
display: flex;
justify-content: start;
align-items: center;
width:80%;
height:20%;
border:1px solid sandybrown;
border-radius: 1rem;
}
.ChatLog{
margin-left:100px;
}
위와 같은 뷰를 완성했으니, 이제 서버쪽 로직을 구현해보도록 하겠습니다.
// back/app.js
const httpServer = require("http").createServer();
const io = require("socket.io")(httpServer, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
},
});
io.on("connection", (socket) => {
console.log("connection");
socket.on("init", (payload) => {
console.log(payload);
});
socket.on("send message", (item) => {//send message 이벤트 발생
console.log(item.name + " : " + item.message);
io.emit("receive message", { name: item.name, message: item.message });
//클라이언트에 이벤트를 보냄
});
});
httpServer.listen(80);
io.on을 통해 클라이언트와 연결을 하고, send message라는 이벤트가 발생했을 때 클라이언트로부터 받은 값을 바탕으로 receive message 이벤트를 발생시킵니다.
이제 클라이언트 쪽 코드를 살펴보겠습니다.
//App.js
import React, { useCallback, useEffect, useState } from "react";
import "./App.css";
import io from "socket.io-client";
const socket = io.connect("http://localhost:80");
socket.emit("init", { name: "jaehoon" });
function App() {
const [chatArr, setChatArr] = useState([]);
const [chat, setChat] = useState({ name: "", message: "" });
useEffect(() => {
return () => {
socket.close();
};
}, []);
useEffect(() => {
socket.on("receive message", (message) => {
setChatArr((chatArr) => chatArr.concat(message));
}); //receive message이벤트에 대한 콜백을 등록해줌
}, []);
const buttonHandler = useCallback(() => {
socket.emit("send message", { name: chat.name, message: chat.message });
//버튼을 클릭했을 때 send message이벤트 발생
}, [chat]);
const changeMessage = useCallback(
(e) => {
setChat({ name: chat.name, message: e.target.value });
},
[chat]
);
const changeName = useCallback(
(e) => {
setChat({ name: e.target.value, message: chat.message });
},
[chat]
);
return (
<div className="App">
<div className="Box">
<div className="ChatBox">
{chatArr.map((ele) => (
<div className="Chat">
<div>{ele.name}</div>
<div className="ChatLog">{ele.message}</div>
</div>
))}
</div>
<div className="InputBox">
<input placeholder="내용" onChange={changeMessage}></input>
<input placeholder="이름" onChange={changeName}></input>
<button onClick={buttonHandler}>등록</button>
</div>
</div>
</div>
);
}
export default App;
useEffect를 통해 처음 렌더링 되었을 때 receive message이벤트에 대한 콜백을 등록합니다.
그리고 버튼이 클릭될 때 마다 input의 값을 send message이벤트를 통해 전달해줍니다. 모두 정상적으로 동작한다면 아래와 같은 결과를 확인할 수 있습니다.
소켓 통신은 실시간 통신, 특히 채팅 서비스를 구현할 때 꼭 필요한 기술입니다. 이 글을 작성하며 처음 사용해본 기술이지만 굉장히 흥미롭게 다가왔고, 소켓 통신에 대한 개념을 정리할 수 있는 좋은 기회 였다고 생각합니다. 긴 글 읽어주셔서 감사합니다!
마이스터고등학교에서 이른 나이부터 개발자 실무를 공부하시는게 부럽네요
저는 31살에 개발 시작해서 굉장히 고생하고있습니다