이번엔 소켓통신을 통하여 채팅프로그램을 스프링부트에서 만들어보겠습니다.
프로젝트를 생성해서 단순한 채팅방과 추가적으로 방생성에 따른 채팅 구분 등의 과정까지 만들어보겠습니다.
소켓통신을 사용한 채팅프로그램 만들기
스프링부트 프로젝트 생성하기
먼저 프로젝트부터 만들어야겠죠?
Spring Starter Project로 생성합니다.
프로젝트명, group, artifact, package명 등 위 내용으로 채워넣었습니다.
생성하고자 하는 프로젝트로 만드세요~
라이브러리는 WebSocket을 사용할거니까 검색 후 추가해주고, 완료해주겠습니다.
부트 프로젝트 설정하기(pom.xml)
view페이지는 jsp를 사용할 예정입니다.
사용을 위해 pom.xml 설정을 하겠습니다. 아래의 dependency도 추가해주세요.
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- View JSP -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<!-- View JSP -->
다음은 properties의 설정값을 추가하겠습니다.
톰캣 포트를 80으로 변경하고, jsp를 바라볼 수 있도록 설정하겠습니다.
또한, 재시작없이 jsp가 적용되는 설정까지 하도록 하겠습니다.
application.properties
#Tomcat Server Setting
server.port=80
#JSP, HTML ModelAndView Path Setting
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
#JSP to Modify Not Restart Server
server.servlet.jsp.init-parameters.development=true
뷰 설정
디렉토리 생성 및 chat.jsp파일 생성
뷰페이지를 생성합니다.
이제 컨트롤러를 구성하여 넘어가는 것을 보고 바로 소켓통신 설정을 진행해보겠습니다.
컨트롤러 추가
controller패키지 추가 및 MainContoller.java파일을 생성합니다.
package com.psw.chating.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class MainController {
@RequestMapping("/chat")
public ModelAndView chat() {
ModelAndView mv = new ModelAndView();
mv.setViewName("chat");
return mv;
}
}
chat파일을 넘겨주는 view컨트롤러를 생성 후 서버 구동하여 정상적으로 jsp페이지에 접근이 되는지 확인합니다.
localhost/chat으로 접근하니 정상적으로 chat.jsp페이지에 접근하는 모습을 볼 수 있습니다.
이제 본격적으로 WebSocket처리를 연결하고 통신 예제를 진행해보겠습니다.
WebSocket 설정
웹소켓 구현체와 등록해주는 config파일을 생성해보겠습니다. 위 그림처럼 패키지와 자바파일을 생성합니다.
SocketHandler.java
@Component
public class SocketHandler extends TextWebSocketHandler {
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
//메시지 발송
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
//소켓 연결
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
//소켓 종료
}
}
구현체에 등록할 SocketHandler입니다.
afterConnectionEstablished 메소드는 웹소켓 연결이 되면 동작합니다.
acafterConnectionClosed 메소드는 반대로 웹소켓이 종료되면 동작합니다.
handleTextMessage 메소드는 메시지를 수신하면 실행됩니다. 상속받은 TextWebSocketHandler는 handleTextMessage를 실행시키며, 메시지 타입에따라 handleBinaryMessage또는 handleTextMessage가 실행됩니다.
소스를 완성해보겠습니다.
SocketHandler.java
package com.psw.chating.handler;
import java.util.HashMap;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
@Component
public class SocketHandler extends TextWebSocketHandler {
HashMap<String, WebSocketSession> sessionMap = new HashMap<>(); //웹소켓 세션을 담아둘 맵
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
//메시지 발송
String msg = message.getPayload();
for(String key : sessionMap.keySet()) {
WebSocketSession wss = sessionMap.get(key);
try {
wss.sendMessage(new TextMessage(msg));
}catch(Exception e) {
e.printStackTrace();
}
}
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
//소켓 연결
super.afterConnectionEstablished(session);
sessionMap.put(session.getId(), session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
//소켓 종료
sessionMap.remove(session.getId());
super.afterConnectionClosed(session, status);
}
}
구현체를 이제 등록해보겠습니다.
WebSocketConfig.java
package com.psw.chating.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import com.psw.chating.handler.SocketHandler;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer{
@Autowired
SocketHandler socketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(socketHandler, "/chating");
}
}
먼저 생성하였던 구현체를 등록하는 부분입니다.
마지막으로 chat.jsp를 완성하고 메시지가 주고 받아지는지 확인해보겠습니다.
chat.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<meta charset="UTF-8">
<title>Chating</title>
<style>
*{
margin:0;
padding:0;
}
.container{
width: 500px;
margin: 0 auto;
padding: 25px
}
.container h1{
text-align: left;
padding: 5px 5px 5px 15px;
color: #FFBB00;
border-left: 3px solid #FFBB00;
margin-bottom: 20px;
}
.chating{
background-color: #000;
width: 500px;
height: 500px;
overflow: auto;
}
.chating p{
color: #fff;
text-align: left;
}
input{
width: 330px;
height: 25px;
}
#yourMsg{
display: none;
}
</style>
</head>
<script type="text/javascript">
var ws;
function wsOpen(){
ws = new WebSocket("ws://" + location.host + "/chating");
wsEvt();
}
function wsEvt() {
ws.onopen = function(data){
//소켓이 열리면 초기화 세팅하기
}
ws.onmessage = function(data) {
var msg = data.data;
if(msg != null && msg.trim() != ''){
$("#chating").append("<p>" + msg + "</p>");
}
}
document.addEventListener("keypress", function(e){
if(e.keyCode == 13){ //enter press
send();
}
});
}
function chatName(){
var userName = $("#userName").val();
if(userName == null || userName.trim() == ""){
alert("사용자 이름을 입력해주세요.");
$("#userName").focus();
}else{
wsOpen();
$("#yourName").hide();
$("#yourMsg").show();
}
}
function send() {
var uN = $("#userName").val();
var msg = $("#chatting").val();
ws.send(uN+" : "+msg);
$('#chatting').val("");
}
</script>
<body>
<div id="container" class="container">
<h1>채팅</h1>
<div id="chating" class="chating">
</div>
<div id="yourName">
<table class="inputTable">
<tr>
<th>사용자명</th>
<th><input type="text" name="userName" id="userName"></th>
<th><button onclick="chatName()" id="startBtn">이름 등록</button></th>
</tr>
</table>
</div>
<div id="yourMsg">
<table class="inputTable">
<tr>
<th>메시지</th>
<th><input id="chatting" placeholder="보내실 메시지를 입력하세요."></th>
<th><button onclick="send()" id="sendBtn">보내기</button></th>
</tr>
</table>
</div>
</div>
</body>
</html>
동작 확인
부트 어플리케이션을 구동하고 localhost/chat으로 접근하면 위와같은 뷰를 확인 할수 있습니다.
혼자서 3개의 브라우저를 열고 사용자명을 입력 후 테스트를 해보았습니다.
다음장에는 상대방과 자신을 구분을 처리해보겠습니다.
'WEB > Spring' 카테고리의 다른 글
SpringBoot - 스프링부트에서 채팅프로그램(소켓통신) 만들기-3(채팅방 만들기1) (12) | 2020.03.05 |
---|---|
SpringBoot - 스프링부트에서 채팅프로그램(소켓통신) 만들기-2(상대방과 자신을 구분하기) (8) | 2020.03.05 |
SpringBoot - 스프링부트에서 채팅프로그램(소켓통신) 만들기-1(단순 채팅, 메시지 보내기) (54) | 2020.03.05 |
Spring - 파일 업로드 예제 (0) | 2020.02.19 |
Spring - 파일 다운로드 예제 - IE, Chrome (스프링 파일 다운로드 구성) (4) | 2020.02.19 |
Spring - tiles 적용 에러 발생 'org.springframework.web.servlet.view.tiles3.TilesConfigurer' not found (2) | 2020.02.13 |
- 이전 댓글 더보기
-
정승환 2020.07.09 19:40
정말 대단하신거 같아요.
근데 제가 부족해서인지 글을 입력 했을 때 입력한 글이 창에 안 나오더라고요... 혹시 이걸 해결할려면 보통 어떤 방식으로 해결해야 하나요?
현재
sts-3.9.13.RELEASE
spring tool suite 3와 톰켓 9.0
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
을 이용하고 있습니다 -
삐삐 2020.07.19 19:41
WebsocketConfig 클래스를 누가 호출해주는거죠 ?? 이 친구를 호출해야지 클래스가 돌아가는거 아닌가요 ??
-
-
달팽 2020.08.10 17:25
import org.springframework.web.socket.xxxx
제가 Spring legacy로 구현을 하려고 하는데
이부분이 웹소켓이나 핸들러 java에서 임포트가 안되는데 혹시 이게 boot가 아니라서 안되는 걸까요??-
방문해주셔서 감사합니다
음 예전에 비슷한 문의로 일반 스프링에서 진행중인분을 봤는데 그때도 정상적으로 동작하는점 확인했었습니다. 문의내용을 읽어보니 Maven문제 같네요! 해당 포스트글의 경우 3번째 캡처본에서 부트에서 WebSocket을 선택하여 자체적으로 pom.xml에 소켓이 추가되었기때문에 문제없이 사용했습니다.
동일하게 사용하시기 위해 라이브러리 추가를 해주세요.
ex)
<!-- WebSocket -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${org.springframework-version}</version>
</dependency>
또는
<!-- WebSocket -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
-
-
-
jaewoo 2020.10.19 21:33
안녕하세요, 올려주신 내용 그대로 해서 따라하면서 공부하고 있습니다. 좋은내용 올려주셔서 감사해요
올려주신 내용 그대로 따라했는데
127.0.0.1 /chat 으로 하면 404 에러가 뜨네요...
127.0.0.1 자체가 안되나 해서 저 주소만 입력했을땐 internet service 란 안내페이지는 잘뜨고요
이것저것 검색하면서 답을 찾으려했는데 도저히 못찾아서 질문드려봅니다
감사합니다 -
-
안녕하세요!
제가 Spring Legacy를 하면서 추가하였던 dependency는 아래 항목정도가 될 것 같네요.
<!-- WebSocket -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- Simple JSON -->
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<!-- json return -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency> -
404 에러면 spring 설정이 잘못되셨거나 잘 못된 URL로 요청하셔서 발생하는 에러로 추정됩니다.
먼저 확인사항으로 해당 포스팅글까지만 따라하시는거라면 일반 채팅방 페이지인 서버 IP:포트/chat 으로 입력하였을때 구동이 정상적으로 되나요?
저같은 경우엔 톰캣을 등록하고 설정할때 ContextPath가 있는것이 싫어서 "/"으로 변경하여 prefix가 없도록 처리합니다.
요즘 비슷한 질문들이 많이 들어오는데 아무래도 제일 의심가는곳이 톰캣 설정이 아닐까 싶네요 해당 설정부분들을 확인해보세요
-
-
맹복이 2021.03.25 17:16
채팅입력후 엔터만 누르면 websocket is already in closing or closed state. 라고 뜹니다. 인터넷에 다른분들 예제를 따라해도렇고...
function wsOpen(){
ws = new WebSocket("ws://" + location.host + "/main"
wsEvt();
alert("세션열림"
}
이렇게 중간에 자바스크립트를 넣어서 확인하면 wsOpne 메서드 호출시 세션 열림 알림창까진 제대로 뜨고요...뭐가 문제 일까요
참고로 스프링부트 maven 입니다. -
-
김민철 2021.08.04 15:36
===========F12 개발자 도구에서 에러 메시지=====================
chat:55 WebSocket connection to 'ws://localhost:9091/chating/' failed:
wsOpen @ chat:55
chatName @ chat:100
onclick @ chat:132
chat:114 WebSocket is already in CLOSING or CLOSED state.
send @ chat:114
onclick @ chat:141
======================================================
spring.devtools.livereload.enabled=true
server.servlet.context-path=/ez
server.port=9091
# JSP Path (ViewResolver)
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
#JSP to Modify Not Restart Server
server.servlet.jsp.init-parameters.development=true
저는 나머지는 다 똑같고 application.properties만 약간 다른데 이런경우 다르게 설정해야되는 경우가 있을까요
창도 뜨고 하는데 메시지 입력해도 창에 아무 반응이 없습니다.
어떤글보니 contextPath 주소도 포함해서 웹소켓에 연결하라는데 그건 아닌거 같고..
인터넷은 크롬으로 실행해보았습니다. 혹시 공유기면 포트 개방하고 해야되는건 아니겠지요
-
-
개발자1 2021.08.18 17:42
안녕하세요
초반에 패키지 만들고 그 안에 컨트롤러 자바 파일에 @Controller부터 @Request 등..
자동완성도 안뜨고 오류가 나네요 ㅠㅠ 이유를 모르겠습니다 도와주세용
-
-
-
메세지기능 2021.09.10 11:58
localhost/chat 까지 작동이 잘 되어 화면이 보이는데
사용자 이름 등록 후에 메시지를 보내면 화면에 등록이 안되고 입력칸에 내용이 그대로 남아있는데 어떤 문제일까요?
com.psw.chatting 패키지에 ChatApplication.java 는 아무 내용 없는건가요??-
안녕하세요!
일단 해당 예제는 기존 legacy버전의 스프링은 아니고 스프링부트인점을 참고해주세요.
최초 생성시 WebSocket라이브러리를 추가해주는데 이부분을 legacy에서 정상적으로 추가되었는지 확인이 필요할듯 합니다.
해당 주소 localhost/chat 까지는 가지신다는거보니 정상적으로 컨트롤러에 접근해서 jsp까지는 붙는거같네요.
일단 jsp내부에서 동작하는 js로 웹소켓을 잘 열고 서버단으로 접근이 되는지 System.out이나 console.log등으로 체크를해보시는게 좋을 것 같습니다. -
메세지기능 2021.09.13 17:01
번거로우실텐데 답변 주셔서 감사합니다.
현재 servlet-context.xml 파일에
<beans:bean id="socketHandler" class="com.psw.chating.handler.SocketHandler"/>
<websocket:handlers>
<websocket:mapping handler="socketHandler" path="/chating" />
</websocket:handlers>
이렇게 추가하여서 사용자명 과
메시지: 까지는 뜨는데 내용이 넘어오질 않네요 send() 부분에서 문제가 있을까요? ws 를 붙혀줘야 하는건가요?
-
상대방과 대화 2021.09.16 11:26
본인 localhost 뿐만 아니라 다른 상대방과도 대화가 가능한가요??
회사라고 비유를 하면 다른 사원들과 대화가 가능할까요 -
ㅇㅇ 2021.12.16 23:18
덕분에 열심히 공부하고 있습니다. 너무 친절하게 설명해주셔서 많은 도움이 될 것 같아서 감사드립니다.
한 가지 팁을 여쭤보고 싶습니다!!
저는 고정된 방에 db에 저장된 유저(로그인 된 유저)의 이름을 갖고오고 싶은데요.
고정된 방은.. room.jsp에서 애초에 만들어놓으면 될 것 같다고 생각을 하는데
로그인한 유저의 이름을 갖고오는건 어떻게 해야할지 많이 고민입니다ㅜㅜ!
websocket의 session 정보를 갖고오면 이상한 id값이 뜨더라고요ㅠㅠ 혹시나 조언 받을 수 있는 부분이 있을지.. 문의드립니다!
다시금 감사드립니다!!!-
안녕하세요! 제 글이 도움이 되셔서 다행입니다~
오래전에 작성한 글이라 정확하게 테스트는 어려운 상황이라 댓글만 보고 현 예제에서 생각는 형태로 답변드립니다.
일단 로그인된 계정이 담긴 맵 형태의 컬렉션 등으로 관리가 필요할 것으로 보입니다.
afterConnectionEstablished 웹소켓 접근에 해당하는 세션 정보를 담는데, 계정 맵을 하나 더 추가하여 소켓이 연결되면 웹소켓 Id를 베이스로 계정 정보 맵을 하나 더 담으면 복잡하지 않게 처리가 가능할 거 같습니다.
ex)
HashMap<String, UserInfo> userMap = new HashMap<>();
afterConnectionEstablished 메소드
userMap.put(session.getId(), 계정 정보)
이후 handleTextMessage 부분에서 계정정보를 불러와서 계정 이름 : 메시지 형태로 send시키면 될 것 같네요!
-
-
-
개발이 하고싶어요 2022.06.21 19:33
chat:50 WebSocket connection to 'ws://localhost/chtting' failed:
콘솔창에 이렇게 오류가 나는데 저는 처음부터 끝까지 토씨하나 안틀리고 다 복붙했거든요 ㅜㅜ
그런데 왜 오류가 나는지 모르겠어요 ㅜㅜ -