[ Java / Socket ] 서버/클라이언트 통신 및 토스트 메세지 띄워보기

2024. 6. 29. 13:21· LANGUAGE/└ Java

환경 : DBeaver, Spring Tool Suite4

 

 

챗봇처럼 서버와 클라이언트의 유기적인 통신을 구현하기 위해 테이블을 먼저 생성하고 더미 데이터를 삽입하겠습니다.

 

 

 

   - DB 생성

-- 테이블 및 시퀀스 생성
-- 챗봇에서 가장 중요한 건 질문에 대한 답변
create table chatbot (
chatbot_no number primary key, -- 번호
chatbot_question varchar2(300) not null, --질문
chatbot_answer varchar2(300) not null --답변
);
create sequence chatbot_seq;

-- 더미데이터 생성
insert into chatbot(chatbot_no, chatbot_question, chatbot_answer)
values(chatbot_seq.nextval, '원숭이 엉덩이는', '빨개');
insert into chatbot(chatbot_no, chatbot_question, chatbot_answer)
values(chatbot_seq.nextval, '빨가면', '사과');
insert into chatbot(chatbot_no, chatbot_question, chatbot_answer)
values(chatbot_seq.nextval, '사과는', '맛있어');
insert into chatbot(chatbot_no, chatbot_question, chatbot_answer)
values(chatbot_seq.nextval, '맛있으면', '바나나');
insert into chatbot(chatbot_no, chatbot_question, chatbot_answer)
values(chatbot_seq.nextval, '바나나는', '길어');
insert into chatbot(chatbot_no, chatbot_question, chatbot_answer)
values(chatbot_seq.nextval, '길면', '기차');
insert into chatbot(chatbot_no, chatbot_question, chatbot_answer)
values(chatbot_seq.nextval, '기차는', '빨라');
insert into chatbot(chatbot_no, chatbot_question, chatbot_answer)
values(chatbot_seq.nextval, '빠르면', '비행기');
insert into chatbot(chatbot_no, chatbot_question, chatbot_answer)
values(chatbot_seq.nextval, '비행기는', '높아');
insert into chatbot(chatbot_no, chatbot_question, chatbot_answer)
values(chatbot_seq.nextval, '높으면', '백두산');
commit;

 


 DB와 JAVA 연결

 

   - ChatbotDto.java

@Data @Builder @NoArgsConstructor @AllArgsConstructor
public class ChatbotDto {
	private int chatbotNo;
	private String chatbotQuestion;
	private String chatbotAnswer;
}

 

    - chatbot-mapper.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    
<mapper namespace="chatbot">

	<select id="list" resultType="ChatbotDto">
        	select chatbot_no, chatbot_question
        	from chatbot order by chatbot_no asc
   	 </select>
	
	<select id="find" resultType="ChatbotDto">
		select * from chatbot where chatbot_no = #{chatbotNo}
	</select>

</mapper>

 

   - ChatbotDao.java

@Repository
public class ChatbotDao {

	@Autowired
	private SqlSession sqlSession;
	
	public List<ChatbotDto> selectList(){
		return sqlSession.selectList("chatbot.list");
	}
	public ChatbotDto selectOne(int chatbotNo) {
		return sqlSession.selectOne("chatbot.find", chatbotNo);
	}
}

 

 


서버 생성

 

   - ChatbotWebSocketServer.java

@Slf4j
@Service
public class ChatbotWebSocketServer extends TextWebSocketHandler{

	@Autowired
	private ChatbotDao chatbotDao;
	
	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		//사용자가 접속하면 모든 질문 목록을 사용자에게 전송
		//(모든 질문 목록 → List<ChatbotDto> → JSON)
		
		//목록 조회
		List<ChatbotDto> list = chatbotDao.selectList();
		
		//목록으로 JSON 문자열 생성(수동)
		ObjectMapper mapper = new ObjectMapper();//JSON 변환도구
		String json = mapper.writeValueAsString(list);
		
		//메세지 객체 생성
		TextMessage message = new TextMessage(json);
		
		//전송
		session.sendMessage(message);//질문목록이담긴 메세지객체를 전송
	}
	
	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
		//사용자가 보내는 메세지를 받아서 처리하는 메소드
		//- 사용자는 질문번호를 보낸다
		//- 질문번호를 받아서 상세조회한 뒤 나오는 정보를 전송하면 된다
		int chatbotNo = Integer.parseInt(message.getPayload());
		ChatbotDto chatbotDto = chatbotDao.selectOne(chatbotNo);
		
		ObjectMapper mapper = new ObjectMapper();
		String json = mapper.writeValueAsString(chatbotDto);//JSON 변환
		TextMessage response = new TextMessage(json);//포장
		
		session.sendMessage(response);//전송
	}
}

     (+) 질문 목록을 전송시 JSON으로 변환하고 포장하는 과정이 필요하다.

 

 

    - WebSocketServerConfiguration.java

/**
 * 웹소켓과 관련된 설정을 작성하는 파일
 * 이미 등록해둔 웹소켓 서버들을 가져와서 추가적인 설정을 한 뒤 활성화
 * */

@EnableWebSocket//웹소켓을 사용할 것임을 표시(활성화)
@Configuration//설정파일임을 표시
public class WebSocketServerConfiguration implements WebSocketConfigurer {
	@Autowired
	private ChatbotWebSocketServer chatbotWebSocketServer;
	
	@Override
	public void registerWebSocketHandlers (WebSocketHandlerRegistry registry) {
		//매개 변수로 주어진 registry에 웹소켓 서버를 등록
		//- 반드시 웹페이지처럼 주소가 부여되어야 함
		//- (중요)절대로 다른 주소와 겹치면 안된다.
		registry.addHandler(chatbotWebSocketServer, "/ws/chatbot");
	}
}

 

 

페이지 생성

   - PageController.java

@Controller
@RequestMapping("/page")
public class PageController {	
	@RequestMapping("/chatbot")
	public String chatbot() {
		return "chatbot";
	}
}

 

 

   - chatbot.jsp

라이브러리 : https://apvarun.github.io/toastify-js/

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<!-- javascript toast library -->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css">
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/toastify-js"></script>

<h1>챗봇 예제</h1>

<button class="btn-connect">연결</button>
<button class="btn-disconnect">종료</button>

<hr>

<div class="question-wrapper"></div>
<div class="answer-wrapper"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
	$(".btn-connect").click(function(){
		window.socket = new WebSocket("ws://localhost:8080/ws/chatbot");
		
		//웹소켓을 생성하고 나서 예상되는 각종 상황에 대해 미리 콜백함수를 정의
		//- onopen(연결완료시), onclose(연결종료시), onerror(오류발생시)
		//- onmessage(메세지수신시)
		window.socket.onmessage = function(e){
			//console.log(e.data);//서버에서 전송된 내용(문자열)
			
			var json = JSON.parse(e.data);//자바스크립트 스타일로 해석
			//console.log(json);
			
			if(Array.isArray(json)){ //배열이라면
				//json에 들어있는 데이터 개수만큼 버튼을 생성하여 추가
				$(".question-wrapper").empty();
				$(json).each(function(){
					var button = $("<button>").text(this.chatbotQuestion)
											  .attr("data-no", this.chatbotNo)
			   								  .addClass("btn-question");
					$(".question-wrapper").append(button);
				});
			}
			else {//배열이 아니라면(지금 상황에선 객체)
				//$(".answer-wrapper").text(json.chatbotAnswer);
			
				Toastify({
					  text: json.chatbotAnswer,
					  duration: 3000,
					  //destination: "https://github.com/apvarun/toastify-js",
					  newWindow: true, //false 시 창 하나에 계속 뜸
					  close: true,
					  gravity: "top", // `top` or `bottom`
					  position: "right", // `left`, `center` or `right`
					  stopOnFocus: true, // Prevents dismissing of toast on hover
					  style: {
					    background: "linear-gradient(to right, #00b09b, #96c93d)",
					  },
					  onClick: function(){} // Callback after click
				}).showToast();
			}
		};
	});	
	$(".btn-disconnect").click(function(){
		window.socket.close();
	});
	
	//문서 내에 존재하는 .btn-question에 클릭 이벤트를 예약
	$(document).on("click", ".btn-question", function(){
		var chatbotNo = $(this).data("no");//버튼에 있는 번호(data-no) 추출
		window.socket.send(chatbotNo);//서버로 전송
	});
});
</script>

    (+) toast library를 사용하기 위해 라이브러리 cdn을 추가하여 준 후 사용해야 한다.

    (+) toast library를 사용하지 않아도 무관, 사용 시 각각의 변수 값을 원하는대로 수정하여 사용하면 된다. 

 


 

   - 출력결과

 

 

개인 공부 기록용입니다:)

728x90