BackEnd/Spring Boot

채팅 만들기_1(basic)

Raconer 2023. 4. 16. 23:21
728x90

개요

웹 개발을 하면서 꼭 해봐야 하지만 잘 안해보는 2가지 기술중 하나였습니다.
채팅은 심심치 않게 볼수있는데 웹 서비스 기술이였지만 내가 한 회사, 외부 프로젝트에서는 적용한 적이 없는 기술이였습니다.
그래서 이번에 어디서든 쉽게 적용 할수있게 프로젝트를 하나 만들어 봤습니다.

스펙

  • Spring Boot2.7.7

  • Java 17

  • jsp 사용

  • dependencies

    •   // Spring Boot Web Server Dependency
        implementation 'org.springframework.boot:spring-boot-starter-web'
        // Spring Web Socket
        implementation 'org.springframework.boot:spring-boot-starter-websocket'
        // Find Views Path
        implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
        // Develop Tool
        implementation 'org.springframework.boot:spring-boot-devtools'
      
        // Properties
        compileOnly 'org.projectlombok:lombok'
        annotationProcessor 'org.projectlombok:lombok'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'

      설명

현재 게시글은 채팅 방 보다는 진짜 간단한 소켓 사용으로 인한 메시지 전송 테스트 용이며
다음 게시글에서 채팅 방을 만들고 방에 입장 하여 채팅을 하는 방식을 만들려고 합니다.

Git

채팅 방 없는 채팅

프로젝트 구조

이해 하기 어려우시면 Git을 확인 하시면 도움이 됩니다.

└─ src
   ├─ main
   │  ├─ java
   │  │  └─ com
   │  │     └─ server
   │  │        └─ chat
   │  │           ├─ ChatApplication.java // 단순 실행 
   │  │           ├─ config
   │  │           │  └─ WebSocketConfig.java // ServerEndpointExporter 설정을 위한 Config
   │  │           ├─ controller
   │  │           │  └─ MainController.java // 단순히 화면 이동 페이지 입니다.
   │  │           └─ service
   │  │              └─ ChatService.java // 소켓 서비스가 실행되는 곳
   │  ├─ resources
   │  │  ├─ application.yml // application.properties
   │  │  ├─ logback.xml // 추후 DB 때문에 넣은 파일 소켓과는 관련이 없다.
   │  │  ├─ static // Static 파일인데 아래 파일은 JSP파일에 다 넣어 놓아서 확인할 필요는 없다.
   │  │  │  ├─ css
   │  │  │  │  └─ chat.css
   │  │  │  └─ js
   │  │  │     └─ chat.js
   │  │  └─ templates
   │  └─ webapp
   │     └─ WEB-INF
   │        └─ views
   │           └─ index.jsp // 화면에 띄어지는 JSP CSS 및 JS 파일이 작성 되어 있습니다.

적용 방법

  1. dependency를 추가
    • implementation 'org.springframework.boot:spring-boot-starter-websocket'
  2. WebSocketConfig.java를 생성 후 코드를 추가
  3. @Component public class WebSocketConfig { // 서버 엔드 포인트를 탐색 하는 Bean을 설정 @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
  4. ChatService.java 추가

제가 익숙해서 Service라고 했지 개발자에 따라 달라 질수도 있다고 생각합니다.

@Slf4j
@Service
@ServerEndpoint("/chat")
public class ChatService {
    private static Set<Session> clients = Collections.synchronizedSet(new HashSet<Session>());

    /*
     * 1) onOpen 메서드
     * 클라이언트가 ServerEndpoint값인 "/chat" url로 서버에 접속하게 되면 onOpen 메서드가 실행되며,
     * 클라이언트 정보를 매개변수인 Session 객체를 통해 전달받습니다.
     * 이때 정적 필드인 clients에 해당 session이 존재하지 않으면 clients에 접속된 클라이언트를 추가합니다.
     */
    @OnOpen
    public void onOpen(Session session) {
        log.info("Open Session : {}", session.toString());
        if (clients.contains(session)) {
            log.warn("Already Connect Session", session);
        } else {
            clients.add(session);
            log.info("Session Ope : {}", session);
        }
    }

    /*
     * 2) onMessage 메서드
     * 클라이언트로부터 메시지가 전달되면 WebSocketChat 클래스의 onMessage메서드에 의해 clients에 있는 모든
     * session에 메시지를 전달합니다.
     */
    @OnMessage
    public void onMessage(String msg, Session session) throws Exception {
        log.info("Receive Message : {}", msg);
        for (Session client : clients) {
            log.info("Send Data : {}", msg);
            client.getBasicRemote().sendText(msg);
        }
    }

    /*
     * 3) onClose 메서드
     * 클라이언트가 url을 바꾸거나 브라우저를 종료하면 자동으로 onClose() 메서드가 실행되며 해당 클라이언트 정보를 clients에서
     * 제거합니다.
     */
    @OnClose
    public void onClose(Session session) {
        log.info("Session Close : {}", session);
        clients.remove(session);
    }
}
  1. jsp, js 코드 작성

  2. <body>
    <div id='chat'>
        <h1>WebSocket chating</h1>
        <input type='text' id='mid' value='홍길동'>
        <input type='button' value='로그인' id='btnLogin'>
        <br />
        <div id='talk'></div>
        <div id='sendZone'>
            <textarea id='msg' value='hi...'></textarea>
            <input type='button' value='전송' id='btnSend'>
        </div>
    </div>
    </body>
    <script>
    /* 
     * Web Socket
     */
    
    function getId(id) {
        return document.getElementById(id);
    }
    
    var data = {}; // 전송 데이터 (JSON);
    
    var ws;
    var mid = getId('mid');
    var btnLogin = getId('btnLogin');
    var btnSend = getId('btnSend');
    var talk = getId('talk');
    var msg = getId('msg');
    
    btnLogin.onclick = function () {
        ws = new WebSocket("ws://" + location.host + "/chat");
    
        ws.onmessage = function (msg) {
            console.log(msg)
            var data = JSON.parse(msg.data);
            var css;
    
            if (data.mid == mid.value) {
                css = "class='me'";
            } else {
                css = "class='other'";
            }
            console.log(data)
    
            var item = `<div ${css} >
                        <span><b>\${data.mid}</b></span> [ \${data.date} ]<br/>
                            <span>\${data.msg}</span>
                        </div>`;
    
            talk.innerHTML += item;
            talk.scrollTop = talk.scrollHeight;//스크롤바 하단으로 이동
        }
    }
    
    msg.onkeyup = function (ev) {
        if (ev.keyCode == 13) {
            send();
        }
    }
    
    btnSend.onclick = function () {
        send();
    }
    
    function send() {
        if (msg.value.trim() != '') {
            data.mid = getId('mid').value;
            data.msg = msg.value;
            data.date = new Date().toLocaleString();
            var temp = JSON.stringify(data);
            ws.send(temp);
        }
        msg.value = '';
    
    }
    </script>

간단한 순서 설명

  1. 닉네임 입력후 Login 버튼을 클릭하면 "btnLogin.onclick = function () {" 이 실행되면서 소켓에 연결 요청
  2. "@OnOpen "이 붙어있는 메소드가 실행되면서 세션을 추가 한다.
  3. 여기 까지 하면 소켓이 실행되고 연결 완료
  4. 메시지 입력 후 전송 버튼 클릭
  5. js안에 작성된"send()" 함수가 실행이 되면서 "ws.send(temp)"가 실행
  6. "@OnMessage" 가 실행되면서 clients에 등록된 모든 사용자 에게 메시지를 Response를 한다.
  7. js안에 작성된 "ws.onmessage = function (msg) {" 가 실행이 되면서 화면에 셋팅
  8. 만약 사용자가 브라우저를 종료 하거나 나가게 되면 @OnClose가 실행 되면서 Session을 제거 한다.

간단한 순서 설명은 알아 보기 쉽게 하기 위해 코드 그대로 복붙 하여 작성하였습니다.

후기

여기 까지는 인터넷에 검색 하면 바로 나온다.
이 다음은 STOMP를 사용하여 Socket에서 채팅 방을 자동으로 셋팅 하여 메시지 전송 하는 법을 작성 하겠습니다.

참고

배운거 남주자

728x90

'BackEnd > Spring Boot' 카테고리의 다른 글

Show JPA Query Log  (0) 2023.04.16
채팅 만들기_2(chatting_room)  (0) 2023.04.16
Request PathVariable Enum Converter  (0) 2023.04.16
Enum 등록된 값 이외에 값 등록시  (0) 2023.04.16
TypeHandler  (0) 2023.04.16