하루동안 삽질하면서 독학 한 후기는...
우선 Spring boot가 아니고 Spring으로 구현된것들을 찾아보기 힘들었다.....
그래서 하나하나 직접 프로젝트에 공부한것들을 기반으로 하나 둘 넣어보다보니
시간이 좀 걸렸다..
하지만 결과는 대만족 ! 실시간 채팅이 어떻게 구현되는지 알고 직접 만들어보니
이렇게 뿌듯할수가 !
본론으로....
<!-- socket -->
<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>
<!-- stomp -->
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-stomp</artifactId>
<version>5.3.2.RELEASE</version>
</dependency>
그 후 Client에서 server로 send 또는 구독, 연결될때 받아주는 controller를 만들어준다 (stompController)
필자의 프로젝트는 톰캣path가 /druwa이므로 client에서 연결할때는 /druwa/chat으로해야한다.
클라이언트가 server쪽으로 통신할때 그 다리를 연결해주는 WebSocketBroker안에
레지스터 endPoints가필요한데 2번그림에 있는 registerStompEndpoints()메소드이다.
연결이되면 Broker를 통해 (/topic : 암시적 1:N통신 /queue : 1:1 통신) 설정해주면된다.
spring mvc 모델로 구현 할경우는 tomcat server에 있는 프로젝트 path에 맞게 설정해주면된다
ex) Druwa프로잭트패스 = /druwa 니까 Endpoint에 나와있는 경로(/chat)다음 추가로
SockJS(/druwa/chat); 이렇게 Client에 연결해주면된다
그 다음 var client = Stomp.over(sock); 코드를 작성해줌으로써 client와 server에 파이프통(연결)을 해준다고보면된다.
<script>
// 2. connection이 맺어지면 실행된다.
client.connect({}, function () {
// 3. send(path, header, message)로 메시지를 보낼 수 있다.
client.send('/druwa/chat/join', {}, JSON.stringify({
auction_no: roomId,
dw_member_no: member,
message : member+"님 입장",
sessionUrl : sock._transport.unloadRef
}));
if(member != "0"){
// 4. subscribe(path, callback)로 메시지를 받을 수 있다. callback 첫번째 파라미터의 body로 메시지의 내용이 들어온다.
//'/topic/room/'+roomId 이 구독자들에게만 뿌려줌
// 해당 방번호로 데이터가 들어올때마다 실행됨
client.subscribe('/topic/room/'+roomId, function (chat) {
var content = JSON.parse(chat.body);
console.log('회원이 채팅침');
if(content.type =="user_JOIN"){
chatBox.append('<li style="text-align:center;">(' + content.dw_member_nick + ')' + content.message +'<small style="font-size: 2px;"> '+content.date+'</small>' +'</li>');
}else if(content.type =="user_SEND_MESSAGE"){
if(content.dw_member_no == member){
chatBox.append('<li style="margin-right: 9px; text-align: right;">' + content.dw_member_nick + ' : ' + content.message +'<small style="font-size: 2px;"> '+content.date+'</small>' + '</li>');
}else{
chatBox.append('<li style="margin-left: -26px;">' + content.dw_member_nick + ' : ' + content.message +'<small style="font-size: 2px;"> '+content.date+'</small>' + '</li>');
}
}else if(content.type =="user_DO_LOGIN"){
$('#message').val("로그인 후 이용해주세요 !");
}
});
}
</script>
위에코드를 보면 client.connect();는 서버가 연결될때 실행되는코드이다.
server와 client가 연결될때 수행할 로직을 작성할 수 있다. ex) 채팅방입장, 로그남기기 등등
코드상에선 채팅방 입장 로직을 작성했다.
코드를 잠깐 설명하자면 connection이 되면서 client.send('/druwa/chat/join', {},값); controller에 /druwa/chat/join 라는 매핑이 되어있는 메소드를 실행하기 위해 작성했다.
그 후 값을 보낼 수 있는데 메소드의 3번째에 값을 넣어주면 된다. 되도록 JSON을 보내주면 좋다. (JAVA쪽에선 객체또는 Map이 되겠다.)
그러면 server에선
//채팅방입장
@MessageMapping("/chat/join")
public AuctionChatMessage sendMsg(String sessionUrl, AuctionChatMessage message) throws Exception {
Date day = new Date();
SimpleDateFormat sDate2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
String now = sDate2.format(day);
System.out.println(message.getAuction_no()+" 번방 열음");
MemberVO memberVO = memberService.getMember(message.getDw_member_no());
if(memberVO != null) {
String user_nick = memberVO.getDw_member_nick();
message.setDate(now);
message.setType("user_JOIN");
message.setDw_member_nick(user_nick);
message.setMessage("님이 입장하셨습니다.");
//해당 룸에 맞게 데이터를 뿌려줌
messageTemple.convertAndSend("/topic/room/"+message.getAuction_no(),message);
}else {
System.out.println("비회원입장");
message.setType("user_DO_LOGIN");
//해당 룸에 맞게 데이터를 뿌려줌
// messageTemple.convertAndSend("/queue/"+message.getAuction_no()+"/"+message.getSessionUrl() ,message);
}
return message;
}
이 메소드가 실행되는것이다. 그리고 server는 값을받아서 service 로직을 처리한뒤
messageTemple.convertAndSend("구독url", 보내줄값);
을 통해서 다시 client에 보내주게되는것이다.
여기서 코드를 작성하며 재밌던것이 있었는데
<script>
var sock = new SockJS("/");
var sessionId = sock._transport.unloadRef;
</script>
이 부분이다.
트위치나 아프리카TV같은곳을보면 " 로그인해야 채팅을 할수있다 ! " 라는 메세지를 하나의 client에 보내려면 유일한 주소가 필요한데 그 주소를 만들수있는것이 바로 저거다.
예를들어 "/queue/room/"+room_no+"/"+sessionId 를 해주면 해당 방 번호에 있는 client들중 특정한 client에게 메세지를 보낼 수 있다.
되짚어보자면....
server가 client에 신호를 보내주려면 spring에서 제공해주는 SimpMessageSendingOperations객체가
필요하다, 그 외에도있는데 Stomp로 구현하는 간단한 채팅이면 이 객체면 충분한것같다.
SimpMessageSendingOperations 안에 convertAndSend(); 메소드를 통해 client로 구독된url(?)에 값을 보낸다.
ex) convertAndSend("/topic/room/"+roomid, 보낼값);
여기서 중요한게있는데 client와 server가 주고받는값은 JSON <ㅡㅡ> Map 형태가 되어야한다.
String도 가능하다.
server가 convertAndSend();로 client에 값을 보내주면 client는 connection할때 구독한 client.subscribe("구독url", 실행시킬메소드, 또는 어떤로직);
이런식으로 client가 신호를 받을 수 있다.
즉, connection안에 subscribe메소드가 있다면 연결이 되어있는한 구독된url로 값,신호가 들어오면 client는 반응한다는 소리다.