[Web KIT640] Day 20. 간단한 웹 채팅 프로그램 💬 (express & React)

vinca·2023년 2월 22일
0

🍉 Web Frontend

목록 보기
21/22
post-thumbnail

Introduction

간단한 소켓프로그래밍/소켓통신을 통한 웹 채팅 프로그램을 구현해보자. 🎨

아래 소스코드가 길어 보이지만, CSS 및 주석으로 인해 길어 보일 뿐 실제로 사용되는 소스코드는 30줄 정도 밖에 안된다.

(실제로 코드를 읽어보면 단순하다.)

소스코드

서버(backend)클라이언트(frontend)로 나눠진다.

서버인 백엔드부터 살펴보도록 하자.

서버(backend) 📡

1. 필요한 패키지 설치

먼저 node.js를 이용한 서버를 사용할 것이므로, 다음 명령어를 통해 백엔드 디렉터리에 패키지를 설치해 주도록 한다.

// backend 디렉터리 생성 및 이동
mkdir backend
cd backend

// 서버 패키지 설치
npm init -y 
npm install socket.io
npm install express
npm install

2. 서버 코드 작성

이후 backend 폴더 내 app.js를 다음과 같이 작성해 주도록 하자.

  • backend / app.js
// backend/app.js
const httpServer = require("http").createServer();
const io = require("socket.io")(httpServer, {
  cors: {
    origin: "http://localhost:3000",
    methods: ["GET", "POST"],
  },
});

// on은 받기, emit은 보내기
// 최소 실행됨
io.on("connection", (socket) => {
  console.log("connection");

  // [수신] 클라이언트로부터 메세지를 받음
  socket.on("init", (payload) => {
    console.log(payload);
  });

  // [수신] 클라이언트로부터 메세지를 받음
  socket.on("send message", (item) => {
     console.log(item.name + " : " + item.message);

     // [송신] 연결된 클라이언트 전체에 메세지를 보냄
    io.emit("receive message", { name: item.name, message: item.message });
    
  });


});

httpServer.listen((80), function(){
  console.log('Http server listening on port 80');
});

3. 소켓 서버 실행

터미널을 열고, node app.js를 통해 작성한 소켓 서버를 실행시켜 보자.

다음과 같이 나온다면 서버는 완료되었다.🎉
실행된 서버는 그대로 두도록하자. (클라이언트 작성하고 바로 쓸 거니까.)


클라이언트(frontend) 📱

이제 클라이언트 쪽으로 넘어가보자.
서버가 실행중인 터미널은 그대로 두고, 새 터미널 창을 하나 더 만든다.

클라이언트는 React를 이용해서 구성할 것이다.
단순하게 구현할 것이므로 npx CRA를 이용해서 아래와 같이 react-app를 설치한다.

1. React 프로젝트 만들기

// backend 폴더에서 빠져나온다.
cd ..

// 프로젝트 폴더에서 입력.
npx create-react-app frontend
cd frontend
npm install socket.io-client

이 과정을 거치면 src, public, node_moudules등 React 실행에 필요한 기본적인 폴더 및 파일들이 설치된다.

2. 소스코드 편집

이제 소스코드인 src 폴더의 파일들의 내용을 아래와 같이 편집해 주도록 하자.

  • frontend / src / index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';


const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <App />
);
  • frontend / src / App.js
//App.js
import React, { useCallback, useEffect, useState } from "react";
import "./App.css";
// 0. 클라이언트 소캣 생성
import io from "socket.io-client";

// React 창이 새롭게 생설될 때 마다, 클라이언트 또한 새롭게 생성됨
// 1. 서버로 소켓 conncet 요청
const socket = io.connect("http://127.0.0.1:80");

// 2. 클라이언트에서 서버쪽으로 데이터를 전달 (데이터  송/수신 확인)
socket.emit("init", "[init] Client -> Server");

function App() {
  // State
  // chartArr = [];
  const [chatArr, setChatArr] = useState([]);
  // chat = { name: "", message: "" };
  const [chat, setChat] = useState({ name: "", message: "" });
  
  // useEffect : [deps] 컴포넌트가 랜더링 될 때마다 특정 작업을 수행하도록 함.  (function, [deps])로 구성 됨
  // [deps] 배열에 여러 컴포넌트들이 들어 있다면 해당 컴포턴트가 변경된 개수 만큼 실행
  // [deps]에 빈 배열 -> 최초 한번만 실행
  // 3. 소켓 종료
  useEffect(() => {
    return () => {
      socket.close();
    };
  }, []);



  // [수신] 서버로부터 온 메세지를 받음
  useEffect(() => {
    socket.on("receive message", (message) => {
      // chatArr 배열에 chatArr 콜백함수를 뒤에 붙여줌. (추가해줌)
      setChatArr((chatArr) => chatArr.concat(message));
    }); //receive message이벤트에 대한 콜백을 등록해줌
  }, []);



  // [요약] 한 클라이언트가 서버로 메세지를 전송하고, 해당 메세지는 서버에서 연결된 모든 클라이언트들에게 보낸다.
  // useCallback : deps가 변경될 때만 기억해둔 콜백함수를 새로 생성해서 사용 
  // 즉, 모든 렌더링 마다 만드는 것이 아니라, 함수내용을 기억해놓고 특정 조건(의존성 변경)에서만 함수를 재 생성해서 사용하도록 하는 것.

  // [송신] 서버로 메세지를 전달.
  //전송 버튼을 눌렀을 때 send message이벤트 발생
  const buttonHandler = useCallback((e) => {
    socket.emit("send message", { name: chat.name, message: chat.message }); 
  }, [chat]);

  
  // setChat으로 State를 변경
  
  // 내용이 변경될 때 -> 현재 이벤트가 발생한 객체에서의 변경되는 부분을 가지고 옴
  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">
          {console.log(chatArr, "< 현재 입력된 채팅 배열 ")}
          {chatArr.map((ele) => (
            <div className="Chat">
              <div className="ChatName">[{ele.name}]</div>
              <div className="ChatLog">{ele.message}</div>
            </div>
          ))}
        </div>


        {/* 입력 창 */}
        <div className="InputBox">
          
          <input className="InputName" placeholder="이름" onChange={changeName}></input>
          <span></span>
          <input className="InputText" placeholder="내용" onChange={changeMessage}></input>
          <button onClick={buttonHandler}>등록</button>
        </div>
        
      </div>
    </div>
  );
}

export default App;

길어보이지만 주석이 절반이다. 정말.

  • frontend / src / App.css
.App {
  width: 100vw;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}
.Box{
  width:40%;
  height: 80%;
  border:3px solid black;
  border-radius: 1rem;
  
}
.ChatBox{
  border:1px solid deepskyblue;
  border-radius: 1rem;
  height:95%;
  width: 99%;
 display: flex;
 flex-direction: column;
 overflow: auto;
 background-color: deepskyblue;
 
}
.Chat{
  align-items: center;
  border:3px solid whitesmoke;
  border-radius: 1rem;
  background-color: skyblue;

  
  display: grid;
  grid-template-columns: minmax(10px, max-content) 1fr;
  width: 350px;
  margin : 3px;
}


.ChatName{
  font-weight: bold;
  margin-left: 10px;

}
.ChatLog{
  margin-left:30px;
  background-color: whitesmoke;
  border-radius: 0.5rem;
}
.InputBox{
  display: flex;
  height:5%;
  justify-content: center;
  align-items: center;

  
}

.InputText {
  width: 50%;
  border-radius: 3rem;
  margin : 5px
}
.InputName {
  width: 30%;
  border-radius: 3rem;
}

3. React 서버 실행

node App.js 가 아닌, npm start를 통해서 실행 시켜주도록 하자.

cd frontend
cd src
npm start

참고로 서버처럼 node App.js한다면 실행되지 않는다.
그 이유는 JSX 문법을 js으로 compile할 수 없기 때문.

실행이 완료되면 왼쪽과 같이 React 서버가 동작중인 주소가 출력된다.
해당 주소를 인터넷 창에 그대로 입력해서 접속해보면, 다음과 같이 채팅화면이 출력되는 것을 볼 수 있다.


결과 확인

자, 이제 모든 코드 작성 및 기타 등등이 완료되었다. 채팅을 해보자!

먼저 다음과 같이 서버쪽 콘솔화면을 보면 클라이언트가 연결 되었다는 것이 출력되어 있다.

이제 채팅을 쳐보면?

서버로 메세지가 전달 된 것을 확인할 수 있다!

채팅의 꽃인 서로 다른 클라이언트창 2개를 켜서 테스트 해보면 잘 되는 것을 확인할 수 있다!

End + etc

  • 소스코드에 대한 상세한 설명은 주석으로 대체한다.
    주석만 보고도 충분히 잘 이해할 수 있다. (함수의 자세한 사용법 같은 건 찾아보는 게,,)

  • 활용한 소스코드 출처

  • 해당 프로그램은 AWS EC2서버를 통해 실제 여러 사람이 들어와 통신할 수 있도록 만들 수 있다.

    • 실제처럼 사용해 보고싶다면, 여기를 참고.
profile
붉은 배 오색 딱다구리 개발자 🦃Cloud & DevOps

0개의 댓글