안녕하세요 오늘은 Road To MQ 첫 단계인 WebSocket에 대해 알아보고자 합니다.
대부분 WebSocket과 STOMP를 같이쓰지만 오늘은 WebSocket 자체만을 사용하여 통신을 해보도록 하겠습니다!
기존의 Http 환경에서는 클라이언트->서버로의 단방향 통신만이 가능합니다.
하지만 알람, 통신등에서는 양방향 통신등이 필요한데요 그것을 가능하게 해주는 것중 하나가 WebSocket입니다.
오늘은 간단하게 연결 & 간단한 통신등을 해보도록 하겠습니다!
//WebSocket
implementation 'org.springframework.boot:spring-boot-starter-websocket'
먼저 WebSocket을 위한 의존성을 추가해줍니다.
먼저 채팅을 하려면 채팅방이 필요하겠죠
@Getter
public class ChatRoom {
private String roomId;
private String name;
private Set<WebSocketSession> sessions = new HashSet<>();
@Builder
public ChatRoom(String roomId, String name) {
this.roomId = roomId;
this.name = name;
}
}
채팅방은
- 채팅방 id
- 채팅방 이름
- 들어와있는 세션으로 구분합니다.
(세션은 클라이언트가 WebSocket으로 연결시 얻어올 수 있습니다.)
@Getter
public class ChatMessage {
public enum MessageType {
ENTER, TALK
}
private MessageType type;
private String roomId;
private String sender;
private String message;
}
채팅 메세지는
- MessageType
- 채팅룸 id
- 발신자
- 메세지 내용
으로 구성되어 있습니다.메세지 id 같은 속성은 현재 단계에서 과감히 빼도록 합니다
@Service
@RequiredArgsConstructor
@Slf4j
public class ChatService {
private final ObjectMapper objectMapper;
//repository 대신 사용
private Map<String, ChatRoom> chatRooms;
//서버가 실행되면 실행
@PostConstruct
public void init() {
chatRooms = new LinkedHashMap<>();
}
//채팅방 생성
public ChatRoom createChatRoom(String name) {
String randomId = UUID.randomUUID().toString();
ChatRoom chatRoom = ChatRoom.builder()
.roomId(randomId)
.name(name)
.build();
//chatRooms(목록)에 채팅방 추가
chatRooms.put(chatRoom.getRoomId(), chatRoom);
return chatRoom;
}
//roomId로 채팅방 찾기
public ChatRoom findChatRoom(String roomId) {
return chatRooms.get(roomId);
}
//ChatRoom에 있는 모든 session에 메시지 전송
public void sendToAllMessage(ChatRoom chatRoom, String message) {
chatRoom.getSessions().forEach(session -> {
sendMessage(session, message);
});
}
//session에 메시지 전송
public <T> void sendMessage(WebSocketSession session, T message) {
try {
session.sendMessage(new TextMessage(objectMapper.writeValueAsString(message)));
} catch (IOException e) {
throw new IllegalArgumentException("메시지 전송 실패");
}
}
//MessageType에 따라 로직 실행
public void handleMessage(ChatRoom chatRoom, ChatMessage message, WebSocketSession session) {
//메시지 타입이 ENTER면
if (message.getType().equals(ChatMessage.MessageType.ENTER)) {
//채팅방에 session추가
chatRoom.getSessions().add(session);
//메시지 보낸 사람 이름 가져오기
String sender = message.getSender();
sendToAllMessage(chatRoom, sender + "님이 입장하셨습니다.");
}
else {
//메시지 보낸 사람 이름 가져오기
String sender = message.getSender();
sendToAllMessage(chatRoom, sender + " : " + message.getMessage());
}
}
}
마지막으로 채팅방 생성을 위한 Contoller 작성을 해보겠습니다
@RestController
@RequiredArgsConstructor
@RequestMapping("chat-rooms")
public class ChatController {
private final ChatService chatService;
//RoomName으로 채팅방 생성
@PostMapping
public ChatRoom createRoom(@RequestParam("RoomName") String RoomName) {
return chatService.createChatRoom(RoomName);
}
}
@Component
@RequiredArgsConstructor
@Slf4j
public class WebSocketHandler extends TextWebSocketHandler {
private final ChatService chatService;
private final ObjectMapper objectMapper;
private String roomId;
private ChatRoom chatRoom;
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
//메시지를 ChatMessage 객체로 변환
ChatMessage chatMessage = objectMapper.readValue(payload, ChatMessage.class);
//UUID 가져오기
roomId = chatMessage.getRoomId();
//CharRoom 찾기
chatRoom = chatService.findChatRoom(roomId);
//로직 실행
chatService.handleMessage(chatRoom, chatMessage,session);
}
}
다음은 WebSocketConfigurer을 구현하여 Config 설정을 하였습니다.
@EnableWebSocket
@Configuration
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketConfigurer {
private final WebSocketHandler webSocketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketHandler, "/ws/chat")
.setAllowedOrigins("*");
}
}
로그가 잘 찍히는 것을 볼 수 있습니다!🔫🔫