Project

웹 소켓 코드 정리

haedal-uni 2022. 3. 30. 13:37
728x90

build.gradle (의존성 추가)

    // websocket
    implementation 'org.springframework.boot:spring-boot-starter-websocket'

 

 

 

 

WebSocketConfig

@Configuration // 컨테이너 등록
@EnableWebSocketMessageBroker // 웹소켓 서버 사용 설정
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    // connection을 맺을때 CORS 허용
    public void registerStompEndpoints(StompEndpointRegistry registry) {

        registry.addEndpoint("/").setAllowedOrigins("*").withSockJS();
        // endpoint는 양 사용자 간 웹소켓 핸드 셰이크를 위해 지정
    }
}

 

 

 

 

MessageController

@Controller
@ServerEndpoint("/websocket")
public class MessageController extends Socket {
    private static final Logger logger = LoggerFactory.getLogger(MessageController.class);

    private static final List<Session> session = new ArrayList<Session>();
   // 필드에는 사용자 정보를 담기 위해 session list를 선언

    @GetMapping("/chat")
    public String index() {
        return "chat";
    } // 사용자가 입장 시 chat.html을 리턴하고,
     // chat.html에서 웹소켓을 연결하기 위한 주소로 @ServerEndpoint()를 지정한다.

    @OnOpen // 사용자가 페이지에 접속할 때 실행되는 @OnOpen메서드에서 세션 리스트에 담아준다.
    public void open(Session newUser) {
        System.out.println("connected");
        session.add(newUser);
        System.out.println(newUser.getId());
    }  // 사용자가 증가할 때마다 세션의 getId()는 1씩 증가하며 문자열 형태로 지정된다.


    @OnMessage // 사용자로부터 메시지를 받았을 때, 실행된다.
    public void getMsg(String msg) {
        for (int i = 0; i < session.size(); i++) {
                try {
                    session.get(i).getBasicRemote().sendText(msg); 
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }


    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }


    @OnClose // 서버 종료
    public void onClose(Session session, CloseReason closeReason) {
        logger.info(String.format("Session %s closed because of %s", session.getId(), closeReason));
    }
}

 

@OnOpen : 클라이어트가 접속할 때 발생하는 이벤트

@OnClose : 클라이언트가 브라우저를 끄거나 다른 경로로 이동할 때

@OnMessage : 메시지가 수신되었을 때

@OnError : 웹 소켓이 에러가 나면 발생  

출처: https://iamawebdeveloper.tistory.com/85 [나는 웹개발자!]

 

 

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

@ServerEndpoint 어노테이션이 달린 클래스들은 WebSocket이 생성될 때마다 인스턴스가 생성되고

JWA에 의해 관리되기 때문에 스프링의  @Autowired가 설정된 멤버들이 정상적으로 초기화 되지 않는다.

때 이를 연결해 주고 초기화해 주는 클래스가 필요하다.

 [ Spring Boot에서 WebSocket 만들기 ]

 

 

 

+ 추가 설명 ⬇️

더보기

초기 코드

 

 private static Set<Session> clients = Collections.synchronizedSet(new HashSet<Session>());

@OnMessage
public void onMessage(String msg, Session session) throws Exception{
    System.out.println("receive message : " + msg);
    for(Session s : clients) {
        System.out.println("send data : " + msg);
        s.getBasicRemote().sendText(msg);

    }

위 코드를 사용하면 서버쪽에서는 대화가 출력되는데 프론트 상에서는 출력이 되지 않는다.

프론트를 아직 건들지 않아서 이 부분은 참고만했다.

https://jobtc.tistory.com/59

 

 

@Controller
@ServerEndpoint("/websocket")
public class MessageController extends Socket {
    private static final Logger logger = LoggerFactory.getLogger(MessageController.class);
    // OnClose

    private static final List<Session> session = new ArrayList<Session>();
   // 필드에는 사용자 정보를 담기 위해 session list를 선언

    @GetMapping("/chat")
    public String index() {
        return "chat";
    }     // 사용자가 입장 시 chat.html을 리턴하고,
         // chat.html에서 웹소켓을 연결하기 위한 주소로 @ServerEndpoint()를 지정한다.


    @OnOpen // 사용자가 페이지에 접속할 때 실행되는 @OnOpen메서드에서 세션 리스트에 담아준다.
    public void open(Session newUser) {
        System.out.println("connected");
        session.add(newUser);
        System.out.println(newUser.getId());
    }     // 사용자가 증가할 때마다 세션의 getId()는 1씩 증가하며 문자열 형태로 지정된다.


    @OnMessage // 사용자로부터 메시지를 받았을 때, 실행된다.
    public void getMsg(Session recieveSession, String msg) {
        for (int i = 0; i < session.size(); i++) {
            if (!recieveSession.getId().equals(session.get(i).getId())) {
                // 메세지를 보낸 사람의 SessionId와 SessionList의 Id가 같지 않으면 상대방이 보낸 메시지,
                //아이디가 같다면 내가 보낸 메시지다.
                
                try {
                    session.get(i).getBasicRemote().sendText(msg); //"상대 : " +msg
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }else{
                try {
                    session.get(i).getBasicRemote().sendText(msg); // "나 : "+msg
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }


    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        logger.info(String.format("Session %s closed because of %s", session.getId(), closeReason));
    }
}

 

생각해보니 백엔드에서 상대와 나를 구분하기 위해

for문을 사용했고, if ~ else 문을 사용한 것 같아서 for문과 else문을 없앴다.

 

    @OnMessage // 사용자로부터 메시지를 받았을 때, 실행된다.
    public void getMsg(Session recieveSession, String msg) {
        for (int i = 0; i < session.size(); i++) {
            if (!recieveSession.getId().equals(session.get(i).getId())) {
                // 메세지를 보낸 사람의 SessionId와 SessionList의 Id가 같지 않으면 상대방이 보낸 메시지,
                //아이디가 같다면 내가 보낸 메시지다.
                
                try {
                    session.get(i).getBasicRemote().sendText(msg); //"상대 : " +msg
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }else{
                try {
                    session.get(i).getBasicRemote().sendText(msg); // "나 : "+msg
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

⬇️ 아래 코드로 변경

@OnMessage // 사용자로부터 메시지를 받았을 때, 실행된다.
public void getMsg(String msg) {
    for (int i = 0; i < session.size(); i++) {
            try {
                session.get(i).getBasicRemote().sendText(msg); //"상대 : " +msg
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

 

 

 

 

 

누구 한명이 나갔을 때 웹 소켓이 종료된다. 이 문제를 해결해야겠다.

728x90

'Project' 카테고리의 다른 글

Registry 코드 정리 - front (+ 페이징)  (0) 2022.05.25
Registry 코드 정리 - back (+ 페이징)  (0) 2022.05.25
1, 2차 프로젝트 gif 정리  (0) 2022.03.29
3차 프로젝트 gif  (0) 2022.03.28
프로젝트 관련 정리글 링크 첨부  (0) 2022.03.27