[22/03/11] 채팅기능(2) WebSocket 연결하기

Que Lin·2022년 3월 11일
0

config폴더를 만들어서 StompWebSocketConfig 클래스를 하나 생성한다.

StompWebSocketConfig

package com.phl.cocolo.chatting;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

//Stomp를 사용하기위해 선언하는 어노테이션
@EnableWebSocketMessageBroker
@Configuration
public class StompWebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/stomp/chat")
                .setAllowedOrigins("http://localhost:8094")
                .withSockJS();
    }

    /*어플리케이션 내부에서 사용할 path를 지정할 수 있음*/
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
    // Client 에서 SEND 요청을 처리
    //Spring docs 에서는 /topic, /queue로 나오나 편의상 /pub, /sub로 변경
        registry.setApplicationDestinationPrefixes("/pub");
        //해당 경로로 SimpleBroker를 등록.
        // SimpleBroker는 해당하는 경로를 SUBSCRIBE하는 Client에게 메세지를 전달하는 간단한 작업을 수행
        registry.enableSimpleBroker("/sub");
        //enableStompBrokerRelay
        //SimpleBroker의 기능과 외부 Message Broker( RabbitMQ, ActiveMQ 등 )에 메세지를 전달하는 기능을 가짐
    }
}

setAllowedOrigins에는 내 서버 주소를 입력한다.

웹소켓을 사용할 컨트롤러를 만들어 준다.

StompChatController

SimpMessagingTemplate 브로커로 메세지를 전달한다.
채팅방에 들어오거나 채팅을 할 때,
db에 채팅 이력을 저장한다.

package com.phl.cocolo.controller;

import com.phl.cocolo.dto.ChatMessageDetailDTO;
import com.phl.cocolo.dto.ChatMessageSaveDTO;
import com.phl.cocolo.entity.ChatMessageEntity;
import com.phl.cocolo.entity.ChatRoomEntity;
import com.phl.cocolo.repository.ChatRepository;
import com.phl.cocolo.repository.ChatRoomRepository;
import com.phl.cocolo.service.ChatService;
import lombok.RequiredArgsConstructor;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;

import java.util.List;

@Controller
@RequiredArgsConstructor
public class StompChatController {

    private final SimpMessagingTemplate template; //특정 Broker로 메세지를 전달
    private final ChatRepository cr;
    private final ChatRoomRepository crr;
    private final ChatService cs;

    //Client 가 SEND 할 수 있는 경로
    //stompConfig 에서 설정한 applicationDestinationPrefixes 와 @MessageMapping 경로가 병합됨
    //"/pub/chat/enter"
    @MessageMapping(value = "/chat/enter")
    public void enter(ChatMessageDetailDTO message) {
        message.setMessage(message.getWriter() + "님이 채팅방에 참여하였습니다.");


        List<ChatMessageDetailDTO> chatList = cs.findAllChatByRoomId(message.getRoomId());
        if(chatList != null){
             for(ChatMessageDetailDTO c : chatList ){
                 message.setWriter(c.getWriter());
                 message.setMessage(c.getMessage());
             }
         }

        template.convertAndSend("/sub/chat/room/" + message.getRoomId(), message);

        ChatRoomEntity chatRoomEntity= crr.findByRoomId(message.getRoomId());
        ChatMessageSaveDTO chatMessageSaveDTO = new ChatMessageSaveDTO(message.getRoomId(),message.getWriter(), message.getMessage());
        cr.save(ChatMessageEntity.toChatEntity(chatMessageSaveDTO,chatRoomEntity));
    }

    @MessageMapping(value = "/chat/message")
    public void message(ChatMessageDetailDTO message) {
        template.convertAndSend("/sub/chat/room/" + message.getRoomId(), message);

        // DB에 채팅내용 저장
        ChatRoomEntity chatRoomEntity= crr.findByRoomId(message.getRoomId());
        ChatMessageSaveDTO chatMessageSaveDTO = new ChatMessageSaveDTO(message.getRoomId(),message.getWriter(), message.getMessage());
        cr.save(ChatMessageEntity.toChatEntity(chatMessageSaveDTO,chatRoomEntity));
    }
//    @MessageMapping 을 통해 WebSocket 으로 들어오는 메세지 발행을 처리한다.
//    Client 에서는 prefix 를 붙여 "/pub/chat/enter"로 발행 요청을 하면
//    Controller 가 해당 메세지를 받아 처리하는데,
//    메세지가 발행되면 "/sub/chat/room/[roomId]"로 메세지가 전송되는 것을 볼 수 있다.
//    Client 에서는 해당 주소를 SUBSCRIBE 하고 있다가 메세지가 전달되면 화면에 출력한다.
//    이때 /sub/chat/room/[roomId]는 채팅방을 구분하는 값이다.
//    기존의 핸들러 ChatHandler 의 역할을 대신 해주므로 핸들러는 없어도 된다.
}

나는 채팅 Controller를 따로 만들지 않고,
멘토링 Controller에서 같이 구현해주었다.

나의 멘토링 조회 화면에서 채팅을 할 수 있는데, 조회화면으로 갈 때 DB에 저장되어있는 채팅방 목록들을 가져와서 view에 보여주었다.

 	private final MentoringService mts;
    private final MemberService ms;
    private final ChatService cs;
    
//나의 멘토링 조회
    @GetMapping("/myMentoring/{memberId}")
    public String myMentoring(@PathVariable ("memberId") Long memberId, Model model){
        //멘티정보로 멘토 신청정보 찾기
        List<MenteeDetailDTO> menteeList = mts.findAllByMemberId(memberId);
        model.addAttribute("menteeList",menteeList);

        //멘토정보로 멘티 찾기
        List<MenteeDetailDTO> mentorList = mts.fundAllMentorMemberId(memberId);
        model.addAttribute("mentorList",mentorList);

        //채팅방 목록 불러오기
        model.addAttribute("rooms", cs.findAllRooms());


        return "/mentoring/myMentoring";
    }
    //채팅방 개설
    @PostMapping(value = "/room")
    public String create(@RequestParam String name,HttpSession session,Model model){
        Long memberId = (Long) session.getAttribute(LOGIN_ID);
        String memberNick = (String) session.getAttribute(LOGIN_NICKNAME);
        log.info("# Create Chat Room , name: " + name);

        cs.createChatRoomDTO(name,memberNick);

        return "redirect:/mentoring/myMentoring/"+memberId;
    }

    //채팅방 조회
    @GetMapping("/room")
    public void getRoom(String roomId, Model model,HttpSession session){
        //        Long memberId = (Long) session.getAttribute(LOGIN_ID);
        //        String memberProfileName = ms.findById(memberId).getMemberProfileName();
        //        model.addAttribute("memberProfileName",memberProfileName);
        log.info("# get Chat Room, roomID : " + roomId);

        model.addAttribute("room", cs.findRoomById(roomId));
    }

참고: https://dev-gorany.tistory.com/235

profile
1일 1커밋 1일 1벨로그!

0개의 댓글