[Spring] To Do List 웹페이지 만들기(spring legacy) - 6. 챗봇/실시간 채팅방_스프링 웹소켓

HodooHa·2024년 7월 6일

개인프로젝트로 호두스케쥴러를 개발하는 중에 수업에서 배웠던 스프링 웹소켓(spring-websocke)을 이용한 챗봇/실시간 채팅방을 구현해보았다.
사실 웹소켓에 대해서는 대강 코드들만 이해한 수준이라 완벽하게 내 것으로 만들지 못했다. 본 포스팅은 이를 복기하며 추후 다시 공부하기 위해 남겨보는 것이다.

1. 의존성 주입

[pom.xml 일부]

	<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-websocket</artifactId>
      <version>${org.springframework-version}</version>
	</dependency>

	<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-messaging</artifactId>
      <version>${org.springframework-version}</version>
	</dependency>

2. 설정파일

[WebSocketConfig.java]

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

import com.multi.hodooScheduler.common.interceptor.HttpHandsShakeInterceptor;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

	//채팅방 이름 설정
	@Override
	public void configureMessageBroker(MessageBrokerRegistry config) {
		config.enableSimpleBroker("/task");
		config.setApplicationDestinationPrefixes("/app");
	}
	
	//채팅 내용을 보낼 주소(endPoint == url)
	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {
		//chat: 실시간 채팅용 
		registry.addEndpoint("/chat"); //자바 소켓 통신 가능 
		registry.addEndpoint("/chat").withSockJS(); //자바 스크립트 소켓 통신 
		
		//chat2: 챗봇용 
		registry.addEndpoint("/chat2").setAllowedOrigins("*").addInterceptors(new HttpHandsShakeInterceptor()).withSockJS(); //자바 소켓 통신 가능 
		// 로그인 유무 확인과 로그인한 유저의 정보를 얻어보기 위해 인터셉터 추가
        
        /*
		 * registry.addEndpoint("/chat2").setAllowedOrigins("*").withSockJS(); //자바 스크립트
		 * 소켓 통신
		 */	}

}

  • 나는 위와 같이 호두스케쥴러의 챗봇에서 [일정]과 [다이어리] 리스트를 출력할 수 있도록 만들었다.
  • 따라서 로그인된 유저의 정보가 필요하다.
  • 그러나 WebSocket 연결 후 httpSession의 유저 정보를 가져오려고 하니 가져올 수 없었다.
  • 검색하여 이유를 알아보니 하기와 같았다.
  • 내가 유저의 정보를 저장한 HttpSession과 WebSocket 접속자 정보가 저장되는 WebSocketSession은 다르다.
  • WebSocketSession은 HttpServletResponse, HttpServletRequest 등 servlet 관련 클래스를 사용할 수 없다.
  • 따라서 하기 interceptor를 추가하여 WebSocketHandler 동작 이전에 HttpSession에 접근하여 원하는 정보(로그인 유저)를 WebSocketHandler로 전달할 수 있다.
    [출처: https://a-develop.tistory.com/95 [A-develop:티스토리]]

[HttpHandsShakeInterceptor.java]

import java.util.Map;

import javax.servlet.http.HttpSession;

import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

public class HttpHandsShakeInterceptor implements HandshakeInterceptor {

	@Override
	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
			Map<String, Object> attributes) throws Exception {
		
		if(request instanceof ServletServerHttpRequest) {
			ServletServerHttpRequest servletResquest = (ServletServerHttpRequest) request;
			HttpSession session = servletResquest.getServletRequest().getSession();
			attributes.put("session", session); // 로그인 유저의 정보가 담긴 httpsession을  websocketsession에 session이란 이름으로 추가해준다.
		} 
		return true;
	}

	@Override
	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
			Exception exception) {
		// TODO Auto-generated method stub

	}

}

3. 소스코드

[WebSocketController.java]

@Controller
public class WebSocketController {

	private TaskService taskService;
	private DiaryService diaryService;

	@Autowired
	public WebSocketController(TaskService taskService, DiaryService diaryService) {
		super();
		this.taskService = taskService;
		this.diaryService = diaryService;
	}

	@MessageMapping("/chat2") // 채팅 내용 받을 때 사용하는 주소
	@SendTo("/task/messages2")
	public OutputMessage2 send2(Message message, SimpMessageHeaderAccessor messageHeaderAccessor) { // from: guest,
																									// text:1
		System.out.println("받은 데이터>>" + message);
		HttpSession session = (HttpSession) messageHeaderAccessor.getSessionAttributes().get("session"); 
        // 인터셉터에서 추가한 session을 가져온다.
		UserDTO loginUser = (UserDTO) session.getAttribute("loginUser");
		System.out.println(loginUser);
		OutputMessage2 out = new OutputMessage2();
		String menu = "";
		switch (message.getText()) {
		case "1":
			if (loginUser == null) {
				menu = "로그인이 필요합니다. 로그인을 해주세요.";
			} else {
				try {
					ArrayList<TaskDTO> list = taskService.selectTaskList(loginUser);
					for (TaskDTO t : list) {
						menu += t.getContent() + ": " + (t.getComplete().equals("N") ? "미완, " : "완료, ");
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					menu = "오류발생";
				}
			}

			break;
		case "2":
			if (loginUser == null) {
				menu = "로그인이 필요합니다. 로그인을 해주세요.";
			} else {
				try {
					PageDTO pageDTO = new PageDTO();
					pageDTO.setUserId(loginUser.getUserId());
					pageDTO.setPage(1);
					pageDTO.setStartEnd(1);
					ArrayList<DiaryDTO> list = diaryService.diaryList(pageDTO);
					for (DiaryDTO d : list) {
						menu += "( " + d.getCreatedDate() + " ) 제목: " + d.getTitle() + ", ";
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					menu = "오류발생";
				}
			}
			break;

		default:
			menu = "챗 봇>>선택한 번호는 없는 메뉴입니다. 다시 입력해주세요.";
			break;
		}
		System.out.println("메뉴는" + menu);
		out.setMenu(menu);
		return out;
	}

	@MessageMapping("/chat") // 채팅 내용 받을 때 사용하는 주소
	@SendTo("/task/messages") // 가입주소한 브라우저에 return message를 json으로 변환해서 보내줌.
	public OutputMessage send(Message message) {
		System.out.println("받은 데이터>>" + message);
		OutputMessage out = new OutputMessage();
		out.setFrom(message.getFrom());
		out.setText(message.getText());
		Date date = new Date();
		out.setTime(date.getHours() + ":" + date.getMinutes());
		return out;
	}
}

[결과]

로그인 전

로그인 후

본 포스팅은 멀티캠퍼스의 멀티잇 백엔드 개발(Java)의 교육을 수강하고 작성되었습니다.

profile
성장하는 개발자, 하지은입니다.

0개의 댓글