2022-05-02,03

GGAE99·2022년 5월 3일
0

진도

목록 보기
42/43

오늘 정리할 목록이다.

오늘 정리할거

  1. 웹소켓

1. 웹소켓

하나의 TCP 접속에 전이중 통신 채널을 제공하는 컴퓨터 통신 프로토콜이다.
웹에서 채팅을 하거나 하는 바로바로 반응이 이루어지는 코드를 짤 떄 사용한다. 하나의 웹소켓을 만들어 그 소켓에 접속한 유저들끼리 즉각적으로 데이터를 주고받을 수 있다.

웹소켓을 사용하려면 먼저 웹소켓 라이브러리를 추가해야한다.

		<!-- spring-websocket -->
		<dependency>
		    <groupId>org.springframework</groupId>
		    <artifactId>spring-websocket</artifactId>
		    <version>5.0.6.RELEASE</version>
		</dependency>
		<!-- jackson-databind -->
		<dependency>
		    <groupId>com.fasterxml.jackson.core</groupId>
		    <artifactId>jackson-databind</artifactId>
		    <version>2.12.3</version>
		</dependency>

이때 본인의 스프링 버전이랑 맞는 라이브러리를 가져와야한다.

지금은 웹소켓을 이용한 간단한 전체채팅 코드를 짜볼 것 이다.

	<h1>전체채팅</h1>
	<hr>
	<button onclick="initChat('${sessionScope.m.memberId}');">채팅 시작하기</button>
	<hr>
	<div class="chatting">
		<div class="messageArea"></div>
		<div class="sendBox">
			<input type="text" id="sendMsg">
			<button id="sendBtn" onclick="sendMsg();">전송</button>
		</div>
	</div>

채팅 프로그램의 html이다.
세션에 저장되어있는 아이디를 값으로 보내주고 채팅을 시작한다.
cahtting이라는 클래스 div는 "채팅 시작하기" 버튼을 클릭했을때 slideDown()으로 내려올 수 있도록해줬다.
동시에 웹소켓에 접속할 수 있도록 자바스크립트를 짜놓았다.

	//웹소켓 객체용 변수
	let ws;
	//접속회원 아이디용 변수
	let memberId;
	//채팅을 시작하는 함수
	function initChat(param){
		memberId = param;
		//웹소켓 연결 시도
		ws = new WebSocket("ws://본인의 ip주소/chat.do");
		//웹소켓 연결이 성공하면 실행할 함수 지정
		ws.onopen = startChat;
		//서버에서 화면으로 데이터를 전송하면 처리할 함수 지정
		ws.onmessage = receiveMsg;
		//웹소켓 연결이 종료되면 실행할 함수 지정
		ws.onclose = endChat;
		$(".chatting").slideDown();
	}

ws에는 웹소켓 객체를, memberId에는 세션에서 받아온 아이디를 받아온다.
이렇게 "버튼 시작하기"를 누르면 웹소켓(ws)에 접속한다.
ws.onopen은 웹소켓 연결을 성공했을때 실행한다.
startChat()메소드를 실행하도록 지정해뒀다.

	function startChat(){
		//msg라는 키값으로 회원아이디를 웹소켓 서버로 전송
		const data = {type:"enter",msg:memberId};
		ws.send(JSON.stringify(data));//data객체를 문자열로 변환해서 웹소켓 서버로 전송	
		appendChat("<p>채팅방에 입장했습니다.</p>");//화면채팅방에 입장했음을 알려줌
	}

startChat()은 data라는 변수에 "enter"타입의 msg라는 키값으로 memberId값을 보내준다.
웹소켓에 보낼때(ws.send) data를 문자열로 변환해서 보내준다.
그리고 appendChat()함수를 이용해 띄워준 채팅창에 채팅방에 입장했음을 알려준다.
appendChat함수는 채팅창에 매개변수로 받은 문자열을 추가해주는 함수로짰고, 다음과 같다.

	//.messageArea에 메세지를 추가하는 함수
	function appendChat(msg){
		$(".messageArea").append(msg);
		$(".messageArea").scrollTop($(".messageArea")[0].scrollHeight);
	}

순서가 이상하지만 다시 ws변수를 보면 본인의 ip주소와 뒤의 맵핑이 있는걸 확인할 수 있다.
이게 이 jsp에서 보내주는 데이터를 받을 웹소켓을 지정하는 코드인데, 그 부분은 WEB-INF에 spring > appServlet > servlet-context.xml에서 설정해 줄 수 있다.

	<!-- 웹소켓 전체채팅 객체 생성 -->
	<beans:bean id="allMemberChat" class="ee.or.common.AllMemberChat"/>
	<!-- 웹소켓 mapping -->
	<websocket:handlers>
		<websocket:mapping handler="allMemberChat" path="/chat.do"/>
	</websocket:handlers>

웹소켓 전체채팅 객체를 AllMemberChat클래스의 아이디는 allMemberChat으로 지정했고,
웹소켓으로 연결하는 설정을 allMemberChat아이디를 객체로 사용해 path를 지정해서 이 path에 접속하면, AllMemberChat에 클래스를 이용하는 웹소켓이라고 지정하는 것 이다.

다시 startChat()으로 가보면, 웹소켓에 data를 보내주는 것을 확인할 수 있다.
이제 보내준 memberId로 뭘 할거냐면, 웹소켓에 접속한 계정을 제외하고 전체채팅에 접속해있었던 모든 계정에게 새로운 참가자가 왔다고 알려주도록 할것 이다.

먼저 데이터를 보내줄 AllMemberChat을 만들어보자.

public class AllMemberChat extends TextWebSocketHandler{
	//접속한 회원 세션을 저장하는 리스트(채팅용)
	private ArrayList<WebSocketSession> sessionList;
	//세션별 아이디를 저장할 map
	private HashMap<WebSocketSession, String> memberList;	
}

먼저 AllMemberChat을 사용하게위해 TextWebSocketHandler라는 클래스를 상속해줬다.
스프링에서 제공해주는 클래스로 소켓에 접속했을때, 소켓에서 작업을 처리할 때, 소켓 접속을 해체했을 때 실행할 메소드들을 지정한다.

public void afterConnectionEstablished : 웹소켓에 최초로 접속했을때 수행되는 메소드
protected void handleTextMessage : 소켓으로 데이터가 전송되었을때 수행되는 메소드
public void afterConnectionClosed : 소켓에서 연결이 끊겼을 때 실행되는 메소드

	public AllMemberChat() {
		super();
		sessionList = new ArrayList<WebSocketSession>();
		memberList = new HashMap<WebSocketSession, String>();
	}

다음으로 접속한 회원들의 세션을 저장할 sessionList와 세션과 회원 아이디를 저장할 memberList 변수를 각각 선언해줬다.

	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception{
		//System.out.println("접속세션 : "+session);
		//웹소켓 접속 시 접속 세션을 list에 추가
		sessionList.add(session);
		System.out.println("현재 접속 회원 수 : "+sessionList.size());
	}

웹소켓에 접속했을때, 세션의 정보를 받아와서 sessionList에 추가한다.
확인을 위해 현재 접속 회원 수를 spring에 띄워줬지만, 없어도 괜찮은 코드이다.

@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message)throws Exception{
		//문자열을 Json 객체로 변환해줄 객체
		JsonParser parser = new JsonParser();
		JsonElement element = parser.parse(message.getPayload());
		//키가 type인 값을 추출
		String type = element.getAsJsonObject().get("type").getAsString();
		String msg = element.getAsJsonObject().get("msg").getAsString();
		if(type.equals("enter")) {	//새로 채팅방에 회원이 들어온 경우
			//키가 msg인 값을 추출
			memberList.put(session,msg);
			//이미 접속한 회원들에게 새 회원이 접속했다는 메세지 전송
			String sendMsg = "<p>"+msg+"님이 입장하셨습니다.</p>";
			for(WebSocketSession s : sessionList) {
				if(!s.equals(session)) {
					//클라이언트 전송용 객체생성
					TextMessage tm = new TextMessage(sendMsg);
					//클라이언트에 전송
					s.sendMessage(tm);
				}
			}

startChat()에 보면 웹소켓에 데이터를 보내주고있다. 그때 handleTextMessage메소드가 실행되는데, startChat()에서 type이 "enter"인 memberId값을 보내주고있다. type을 정해주는 이유는 웹소켓에 전해지는 값이 어떤 용도로 사용될 것 인지 구분하기 위함이다. 채팅 내용도 보내줄 거기 때문에 타입을 지정했다.
보내준 데이터를 message라는 매개변수로 받아주고, JsonElement를 이용해 받아온 Json객체를 String타입으로 변환해서 각각 type, msg라는 변수로 만들어줬다.
여기서 type을 한번 비교해준다. 만약 type이 "enter"라면 이 프로그램은 회원이 채팅방에 접속했기에 보내지는 데이터라는 것을 알 수 있도록 해준 것 이다. 채팅방에 접속해있는 다른 회원들에게 정보를 알려주기 위해서 for문을 사용해 받아온 session을 키값으로 가지고있는 sessionList를 제외한 모든 session에 접속한 회원이 있다는 것을 아이디와 함께 알려주도록 만들었다.
TextMessage 객체를 만들어서 클라이언트에게 데이터를 보내주면서 마무리한다.

다음은 서버에 메세지를 전달해주는 상황이다.

	//전송버튼 클릭 시 입력한 메세지를 전송하는 함수
	function sendMsg(){
		const msg = $("#sendMsg").val();
		if(msg != ''){
			const data = {type:"chat",msg:msg};
			ws.send(JSON.stringify(data));
			appendChat("<div class='chat right'>"+msg+"</div>");
			$("#sendMsg").val("");
		}
	}

서버에 메세지를 전달해주는 메소드로, 보내줄 문자열인 sendMsg의 값을 msg변수에 넣어줬다.
만약 보내줄 메세지 칸이 비어있다면 보낼 수 없도록 {msg != ' '} 조건을 걸어줬다.
type은 chat으로 설정해서 서버에 type과 msg를 보내줬다.

	   }else if(type.equals("chat")) {//채팅메세지를 입력한 경우
			String sendMsg = "<div class='chat left'><span class='chatId'>"+memberList.get(session)+" : </span>"+msg+"</div>";
			for(WebSocketSession s : sessionList) {
				if(!s.equals(session)) {
					//클라이언트 전송용 객체생성
					TextMessage tm = new TextMessage(sendMsg);
					//클라이언트에 전송
					s.sendMessage(tm);
				}
			}
		}

이제 if를 쓴 이유가 나온다.
만약 받아온 type값이 "enter"가 아니고 "caht"일때 컴퓨터가 이건 접속했을때 들어오는 메세지가 아닌, 채팅을 보내서 들어오는 메세지라고 알 수 있는 것 이다.
만약 type값이 "chat"이라면 session을 이용해 보낸 회원 아이디와 채팅 내용을 채팅을 보낸 회원을 제외한 모든 전체채팅에 접속해있는 회원에게 보내주도록 만들었다.

ws.onmessage = receiveMsg;

여기서 데이터를 보낸 회원을 제외한 회원들에게 initChat의 onmessage가 동작한다.
receiveMst함수가 동작하도록 만들었다.

	function receiveMsg(param){
		appendChat(param.data);		
	}

받아온 데이터를 appenChat메소드를 통해 채팅창에 띄워주도록 만들었다.
아까 빼먹었는데 사실 다른 회원이 입장할때도 똑같이 receiveMsg함수가 동작한거다.

마지막으로 웹소켓의 연결이 끊어졌을 때 이다.

	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception{
		//접속종료 시 종료된 세션을 list에서 제거
		sessionList.remove(session);
		String sendMsg = "<p>"+memberList.get(session)+"님이 퇴장하셨습니다.</p>";
		TextMessage tm = new TextMessage(sendMsg);
		for(WebSocketSession s : sessionList) {
			s.sendMessage(tm);
		}
	}

받아온 데이터를 이용해 memberList에서 회원 아이디를 뽑아다가 다른 접속해있는 회원들에게 접속을 종료한 회원 아이디를 알려주는 데이터를 전송한다.
또 receiveMsg함수가 동작하는 것 이다.
어떤 session에서건 데이터를 받을 때(onmessage) receiveMsg가 동작하도록 만든 것 이다.

오늘은 여기까지만 하겠다!
어제 갑자기 일정이 생겨서 업로드를 못해가지구 다음날 업로드한다...ㅠㅠㅠㅠ
빠잉

0개의 댓글