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);
}
위 코드를 사용하면 서버쪽에서는 대화가 출력되는데 프론트 상에서는 출력이 되지 않는다.
프론트를 아직 건들지 않아서 이 부분은 참고만했다.
@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();
}
}
}
누구 한명이 나갔을 때 웹 소켓이 종료된다. 이 문제를 해결해야겠다.
'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 |