• 티스토리 홈
  • 프로필사진
    chantleman
  • 방명록
  • 공지사항
  • 태그
  • 블로그 관리
  • 글 작성
chantleman
  • 프로필사진
    chantleman
    • 분류 전체보기 (328)
      • 프로젝트 (1)
      • react (2)
      • 코딩테스트 (133)
        • lv0 (101)
        • lv1 (10)
        • lv2 (7)
        • lv3 (15)
      • 오류 (14)
      • 리눅스 (5)
      • 자바 (75)
        • spring (7)
      • js (35)
      • 오라클 (39)
        • PLSQL (3)
      • cs (5)
      • 도커 aws (3)
      • 단축키 (3)
      • 나만봐 (0)
  • 방문자 수
    • 전체:
    • 오늘:
    • 어제:
  • 최근 댓글
      등록된 댓글이 없습니다.
    • 최근 공지
        등록된 공지가 없습니다.
      # Home
      # 공지사항
      #
      # 태그
      # 검색결과
      # 방명록
      • websocket
        2024년 11월 18일
        • chantleman
        • 작성자
        • 2024.11.18.:06

         

         

         

        아래 axios 파트 읽어보깅!!

         

        2024.10.29 - [js] - 비동기 ajax, fetch, axios, async/await, promise

         

        비동기 ajax, fetch, axios, async/await, promise

        https://chantleman.tistory.com/entry/XMLHttpRequest  XMLHttpRequestXMLHttpRequest(XHR)서버와 통신을 하도록 하는 객체 1. 객체는 서버와 상호작용하기 위해 사용2. 전체 페이지를 새로고침하지 않아도 URL을 통해

        chantleman.tistory.com

         

         

         

        HTTP는 stateless로 클라이언트에서 요청(request)하면 서버에서 응답(response)하는 구조로 

        클라이언트에서 요청을 해야지만 서버에서 응답을 할 수 있는 단방향구조였지만,

         

        websocket는 stateful 프로토콜로 지속적으로 connection을 유지하기때문에

        클라이언트의 요청으로 최초 접속 후 한 번 연결하면 서버에서도 언제든지 요청하며 통신할 수 있는 양방향 통신이 가능해진당

         

         


         

         

        ↓ SuminSocketConfig.java ↓

        public class SuminSocketConfig implements WebSocketConfigurer  {
        
        	@Autowired
        	private SampleSocketIntercepter sampleSocketIntercepter;
        	
        	@Override
        	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        		
        		log.debug("체킁");
        		registry
        		.addHandler(webSocketHandler(), "/chat")	//endpoint
        		.setAllowedOriginPatterns("*")				//origin. *은 다 풀어준 것
        		.addInterceptors(sampleSocketIntercepter);
        	}
        	
        	@Bean
        	protected WebSocketHandler webSocketHandler() {
        		log.debug("체로롱");
        		return new SampleWebSocketHandler();
        	}
        }

         

         

        /chat  ← 이런걸 endpoint 라고 함 

        /chat 서버가 중개소 역할을 함

         

         

         

        A와 B, C가 통신할 때 chat이라는 중개소를 통해 통신함 (broadcast)

        A와 B가 직접 통신하는 건 p2p

         

         

         

         

        ↓ SampleSocketIntercepter.java ↓

        @Component
        public class SampleSocketIntercepter implements HandshakeInterceptor {
        	@Override
        	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
        			Map<String, Object> attributes) throws Exception {
        
        		HttpServletRequest req = ((ServletServerHttpRequest)request).getServletRequest();
        		HttpSession session  =  req.getSession(false); //세션이 없으면 만들어줌
        	
        		// HttpSession에 뭐가 들어있다면 담자
        		if(session != null) {
        			Enumeration<String> attNames = session.getAttributeNames();
        
        			while(attNames.hasMoreElements()) {
        				String attName = attNames.nextElement();
        				Object attValue = session.getAttribute(attName); 				
        				attributes.put(attName,attValue);
        			}					
        		}
        		// 그냥 계속 진행
        		return  true;  
        		
        	}
        
        	@Override
        	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
        			Exception exception) {
        		// 지금은 할 일이 생각 안남
        	}
        }

         

        로그인 세션 정보가 httpsession에 담기는데

        그걸 handshake interceptor로 중간에 가로채서 세션정보를 webSocketSession에 담는당

         

         

         

        ↓ SampleWebSocketHandler.java ↓

        @Slf4j
        public class SampleWebSocketHandler extends TextWebSocketHandler {
        
        	private static List<WebSocketSession> list = new ArrayList<WebSocketSession>();
        	
        	@Override	//처음 접속됐을떄 자동 실행
        	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        		log.info("## 누군가 접속");
        		list.add(session);
        	}
        
        	@Override	//여기에 모든 처리 로직이 들어감
        	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        		String uMsg = message.getPayload();
        		log.debug("클라이언트가 보낸 메시지 {}"+ uMsg);
        		
        		//접속한 사람들 모두에게 전달, (broadcast)
        		for (WebSocketSession webSocketSession : list) {
        			webSocketSession.sendMessage(message);
        		}
        	}
        	
        	@Override	//접속 해제 됐을때 자동 실행
        	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        		log.info("## 누군가 떠남");
        		list.remove(session);
        	}
        
        }

         

         

         

         

        ↓ index.html ↓

        <script>
            //클라이언트 사이드 웹소켓
            //ws: 웹소켓 프로토콜.   http와 https처럼 ws와 wss가 있음
            let socket = new WebSocket("ws://localhost:8080/chat");	 //자동으로 접속시도
            
            //접속에 성공하면 자동으로 onopen 이벤트 발생
            socket.onopen = ()=>{
                console.log("웹 소켓 연결 되었습니다.");
                socket.send("연결 완료");	//send()로 메소드로 중개소 에 메시지 보낼 수 있음
            }
        
            //서버에서 메시지가 도착하면 자동으로 onmessage 이벤트 발생
            socket.onmessage = ()=>{
                console.log("서버로부터 왔어용", event.data);
                let rDiv = document.createElement("div");
                rDiv.style.border="1px solid orange";
                rDiv.innerHTML=event.data;
                jcChat.appendChild(rDiv);
            }
        
            socket.onclose=() =>{
                console.log("웹소켓이 닫히면 자동으로 실행됨");
                alert("접속이 끊겼습니당");
            }
        
            socket.onerror=()=>{
                console.log("에러 발생");
            }
        </script>

         

         

         

         


         

         

        ↓ SampleWebSocketHandler.java ↓

        @Slf4j
        public class SampleWebSocketHandler extends TextWebSocketHandler {
        
        	private static List<WebSocketSession> list = new ArrayList<WebSocketSession>();
        	
        	@Override	//처음 접속됐을떄 자동 실행
        	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        		log.info("## 누군가 접속");
        		list.add(session);
        	}
        
        	@Override	//여기에 모든 처리 로직이 들어감		//session : 누가 보냈는지, message : 메시지
        	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        		String uMsg = message.getPayload();
        		log.debug("클라이언트가 보낸 메시지 {}", uMsg);
        		//jackson 라이브러리 직접 사용하여 변환!
        		ObjectMapper objMapper = new ObjectMapper(); //변환기 생성
        		
        		//readValue: json문자열을 자바 객체로 변환
        		MsgVO msgvo = objMapper.readValue(uMsg, MsgVO.class);
        		
        
        		log.info("변환 성공? {}", msgvo);
        		
        		msgvo.setMsg("메시지: " + msgvo.getMsg());
        		
        		//writeValueAsString: 자바 객체를 json 문자열로 바꿔줌
        		String jsonMsg = objMapper.writeValueAsString(msgvo);
        		TextMessage txtMsg = new TextMessage(jsonMsg);
        		
        		//접속한 사람들 모두에게 전달, (broadcast)
        		for (WebSocketSession webSocketSession : list) {
        			if(session != webSocketSession) {	// 보낸 사람이 아닐 때만!
        				if(webSocketSession.isOpen()) {
        					webSocketSession.sendMessage(txtMsg);
        				}
        			}
        		}
        	}
        	
        	@Override	//접속 해제 됐을때 자동 실행
        	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        		log.info("## 누군가 떠남");
        		list.remove(session);
        	}
        
        }

         

         

         

        ↓ MsgVO ↓

        @Getter
        @Setter
        @ToString
        public class MsgVO {
        	private String sender;
        	private String receiver;
        	private String kind;
        	private String msg;
        }

         

         

        ↓ index.html ↓

        <!DOCTYPE html>
        <html>
        <head>
        <meta charset="UTF-8">
        <title>웹 소켓 클라이언트</title>
        <style>
        	#jcChat{
        		width:400px;
        		height:400px;
        		overflow:scroll;
        		border: 5px groove gold;
        	}
        </style>
        </head>
        <body>
        	<h1>SOCKET</h1>
        	<div id="jcChat"></div>
        	<input type="text" name="" id="jcTxt" value="" onkeydown="enter()">
        	<button onclick="send()" >전송</button> <br>
        	id: <input type="text" id="jcId" value="">
        	<button onclick="conn()">소켓연결</button>
        	
        	<script>
        		const jcChat = document.querySelector("#jcChat");
        		const jcTxt= document.querySelector("#jcTxt");
        
        		function enter(){
        			if(window.event.keyCode==13){
        				send();
        			}
        		} 
        
        		function send(){
        			if(!socket){
        				alert("연결 먼저 눌러 바보얌");
        				return;
        			}
        
        			let sDiv = document.createElement("div");
        			sDiv.innerHTML=jcTxt.value;
        			sDiv.style.textAlign="right";
        			jcChat.appendChild(sDiv);
        
        			//데이터 구조 설계가 중요!
        			let MsgVO = {
        				sender : "수민6",
        				receiver: "all", //받는 사람을 all로 하면 broadcast
        				kind: "chat",
        				msg: jcTxt.value
        			};
        
        			socket.send(JSON.stringify(MsgVO));
        			jcTxt.value="" ; //다시 입력 가능하도록
        			jcTxt.focus();	
        		}
        
        
        		let socket = null;
        		function conn(){
        			//클라이언트 사이드 웹소켓
        			
        			//localhost 혹은 ip주소
        			let hostName = location.href.split("/")[2];
        
        			//ws: 웹소켓 프로토콜.   http와 https처럼 ws와 wss가 있음
        			socket = new WebSocket(`ws://${hostName}/chat`);	 //자동으로 접속시도
        			//접속에 성공하면 자동으로 onopen 이벤트 발생
        			socket.onopen = ()=>{
        				console.log("웹 소켓 연결 되었습니다.");
        				//socket.send("연결 완료");	//send()로 메소드로 중개소에 메시지 보낼 수 있음
        			}
        
        			//서버에서 메시지가 도착하면 자동으로 onmessage 이벤트 발생
        			socket.onmessage = ()=>{
        				console.log("서버로부터 왔어용", JSON.parse(event.data));
        				let rJson = JSON.parse(event.data);
        				if(rJson.sender == "수민6"){
        					alert(`${rJson.sender}님이 잠에서 깨셨습니당`);
        				}
        
        				let rDiv = document.createElement("div");
        				rDiv.style.textAlign="left";
        				rDiv.innerHTML = rJson.msg;
        
        				jcChat.appendChild(rDiv);
        				jcChat.scrollTo(0,jcChat.scrollHeight);	//스크롤바를 항상 바닥에
        			}
        
        			socket.onclose=() =>{
        				console.log("웹소켓이 닫히면 자동으로 실행됨");
        				alert("접속이 끊겼습니당");
        			}
        
        			socket.onerror=()=>{
        				console.log("에러 발생");
        			}
        		}
        	</script>
        </body>
        </html>

         

         

         


         

        filter와 interceptor 차이

         

        filter : Tomcat에서 dispatcher servlet에 접근할 때 이전에 가로채서 처리

         

        interceptor : 스프링에서 Controller에 접근하기 이전에 가로채서 처리

         

         

        filter는 톰캣과 스프링 사이에서 처리하는 반면, interceptor는 스프링 내부에서 처리

        dispatcher: 서블릿 컨테이너의 가장 앞단에서 http 프로토콜로 들어오는 모든 요청을 먼저 받아 적합한 컨트롤러에 위임해주는 프론트 컨트롤러

         

        브라우저 즉 컨트롤러 이전(url)에서 통제하고싶다면 interceptor,
        서버 사이드(서비스, 매퍼)에서 통제하고싶다면 AOP

         

        웹소켓에서 연결하기 이전에 http 통신을 통해 핸드쉐이크 과정이 일어나는데

        이때 핸드쉐이크 인터셉터를 통해 세션정보를 가져올 수 있음

         

        728x90

        '자바 > spring' 카테고리의 다른 글

        스프링 bean 등록하기 AOP  (0) 2024.12.29
        react, spring boot 연동  (0) 2024.11.26
        파일업로드  (0) 2024.11.14
        @RequestParam, @RequestBody, @ResponseBody  (6) 2024.11.12
        spring 설치 및 초기 설정  (3) 2024.11.08
        다음글
        다음 글이 없습니다.
        이전글
        이전 글이 없습니다.
        댓글
      조회된 결과가 없습니다.
      스킨 업데이트 안내
      현재 이용하고 계신 스킨의 버전보다 더 높은 최신 버전이 감지 되었습니다. 최신버전 스킨 파일을 다운로드 받을 수 있는 페이지로 이동하시겠습니까?
      ("아니오" 를 선택할 시 30일 동안 최신 버전이 감지되어도 모달 창이 표시되지 않습니다.)
      목차
      표시할 목차가 없습니다.
        • 안녕하세요
        • 감사해요
        • 잘있어요

        티스토리툴바