JAVA
websocket:채팅
docc
2021. 7. 27. 17:16
websocket + 네트워크운영 이해
데이터 보낼 때 송수신 측에서 받는 방식
1) TCP/IP
수신측이 보내는 데이터는 패킷단위로 전송되어 송신측이 받는다.
핸드쉐이크 : 서로 데이터 유실이 없는지 확인하여 계속 소통하며 확인한다.
- 장점 : 안전하다.
- 단점 : 느리다.
2) UDP
핸드쉐이크가 없다.
- 장점 : 빠르다 (TV 같은 것, 빠르다)
- 단점 : 데이터가 몇몇 유실될 수 있다.(가끔 끊기는 이유)
* TCP/IP 기반의 프로토콜 종류 | ||
(1) http : TCP 기반에서 더 추가하여 http 라는 규약이 생긴 것. |
||
(2) smtp | ||
(3) FTP | ||
(4) 사용자가 직접 TCP/IP 기반의 프로토콜을 만들 수도 있다. |
* 네트워크 운영 개념 | ||
client - server 소통 중 | ||
server 1) 서버 socket 준비 2) bind(80) : port 가 80 으로 시작하는 얘로 연결걸 때 본인이 나오겠다고 설정함 (톰캣이면 bind 가 8080) 3) listen (항상 대기) 4) accept ( client 가 연결을 바랄 때 응답 ) 5) receive : 클라이언트 socket 을 전달받음 |
||
client : 1) 클라이언트 socket 준비 2) connect :서버에 연결을 시도:연결을 걸기만 할 뿐 받아들이는 건 못함 3) send : 서버가 accept 한 후 서버에 클라이언트 socket 을 전달 4) close : 통신끝나고 클라이언트가 통화 끝냄 ( 서버에 끝냈다고 전달 하는 것 ) |
||
socket? 의사소통을 주고받는 도구(전화기같은 느낌) client 가 connect 걸어서 socket 으로 소통을 시도 -> acept 하여 server 에 있는 socket 이 반응 |
* 네트워크 운영 중 서버 안의 클라이언트 끼리 소통할 수 있는 방법 ( = 이게 채팅 ) 같은 서버안이라도 서버와 클라이언트의 대화지 클라이언트끼리는 서로를 인식할 수가 없다. 아래에 예제 참고! |
||
1번 방법 : 메세지를 보내는 클라이언트를 server 로 만들어 준다. ( 직접 server 로 만들어 위의 '네트워크 운영 개념' 대로 server가 하는 작업을 만들어줘야 한다. 하지만 아래 spring에서 실습에서 사용한 라이브러리가 저 과정을 자동으로 해줌 ) |
||
javascript 에서 작업 1) new WebSocket : 기본적으로 js 에 있는 객체 ( 그래서 브라우저도 알고 있다. ) 이를 통해 클라이언트의 소켓들을 받아서 서로에게 마치 전달되는 것처럼(server처럼)만들어 줌 |
||
spring 에서 작업 ( 현재 서버는 spring 운영으로 가는 중 ) 1) 'spring-websocket' library 의존성에 다운 ('2)' 의 클래스가 있는 라이브러리.) + 'jackson-databind' library ( ajax 통신할 때 다운받는것 // 이미 받았다. //없다면 아래 참고하여 다운! ) 2) TextWebSocketHandler : 이 클래스를 상속받아 handleTextMessage() 메서드를 override 해준다. = 메세지 처리기 ( 클라이언트에서 온 메시지 처리 ) 3) WebSocketConfigurer: 이 인터페이스를 구현하여 registerWebSocketHandlers() 메서드를 override 하여 '2)' 설정한 클래스를 add = 메세지 처리기 등록 ( spring 이 javascript 의 WebSocket 에 대응하여 응답하라고 알려주기 위해 등록 ) ( 마치 resource 가 오면 WebMvcConfigurer를 상속받은 클래스로 addResourceHandlers() 메서드를 override 해서 해당 요청명과 폴더를 add 했듯, 과정이 똑같다.) |
WebSocket 개요
http 프로토콜이 아닌 ws 프로토콜로 두개의 프로토콜을 한 서버에서 다루는 건 힘들다. (서버 하나 = 프로젝트 하나)
채팅 전용 서버 만드는게 프로젝트 하나를 따로 만드는것과 같은 의미이다.
요청명만 설정해놓으면 다른 프로젝트에서 아래 예제의 프로토콜을 사용하여 (어차피 같은 ip:port 인 host 이다) 연결이 가능하다.
1. 의존성 다운
: spring-websocket 의 version 은 spring-webmvc API 와 똑같은 버전으로
<!-- json --> // 있다면 다운 안받아도 됨
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.1</version>
</dependency>
<!-- spring-websocket -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
1) jackson-databind 이는 Spring 과 관련이 있는 것은 아니고 java 와 js 가 JSON 객체를 주고 받게 하는 @ResponseBody, @RequestBody 를 쓰려는 의미이기 때문에 spring 이 아니더라도 이걸 낀다. (다른 라이브러리도 존재하지만 해당 라이브러리가 가장 괜찮다.) |
||
2) spring-websocket 이는 Spring 과 관련이 있으며 spring 에서 websocket 을 쓸 수 있게 하는 것이다. 해당 라이브러리가 JSON 객체를 주고 받기 때문에 ( js 의 websocket 을 받아야 한다 ) 위의 라이브러리가 필요한 것이다. 이 라이브러리가 jsp-spring 운영체제에서 내가 직접 했어야 할 위의 '네트워크 운영 개념' 의 server 내용을 알아서 작업해준다. client 내용만 설정하면 된다.(jsp안 javascript 가 client 단. java는 중재자하는 중인 실제 server 단) client 내용 중 close() 만 client창에서(jsp) 안해도 되고 server단에서 하면 된다. ( jdbc 라이브러리는 대기상태에 빠져있어서 직접 close() 해줘야 했다.) |
2. 메세지처리기 만들기
TextWebSocketHandler 을 상속받는 클래스 생성
js 에서 들어오는 WebSocket 을 받는 handleTextMessage() 메서드를 override
@Component : @Configuration 들이 @ComponentScan 로 빈으로 등록하는 어노테이션 중 최상위 | ||
@ComponentScan 이 스캔하여 Bean 등록하는 어노테이션 : @Component 과 이에 포함된 @Repository, @Service, @Controller |
||
현재 쓰는 메세지처리기는 역할이 DAO(@Repository) 도 아니고 비지니스영역(@Service) 도 아니고 컨트롤(@Controller) 도 아니기 때문에 @Component 를 썼다. |
//연결 및 메세지 처리기
@Component
public class MessageHandler extends TextWebSocketHandler{
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
//TextMessage payload=[ss], byteCount=2, last=true] 출력
System.out.println(message);//TextMessage 객체로 이 객체의 toString 이 실행되어 위처럼 출력됨
//session 의 sendMessage() 는 받은게 TextMessage 형이니 보낼 때도 TextMessage 형이다.
//그래서 메세지 열만 TextMessage의 메서드인 getPayload() 로 String 형변환 후
//이 메세지만 꺼낸 String 을 다시 TextMessage 형변환 하여 보낸다.
String 메세지만 = message.getPayload();
TextMessage 보낼message = new TextMessage(메세지만+"를 잘받았어");
session.sendMessage(보낼message);
//연결처리
//메세지 처리
//끊기 처리
}
}
3. 메세지처리기 등록
WebSocketConfigurer 을 구현한 클래스 생성
@Configuration
@EnableWebSocket
public class 웹소켓환경설정 implements WebSocketConfigurer{
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MessageHandler(),"/chat");
}
}
@Controller 와의 차이점 : WebSocket 이 돌아갈 TextWebSocketHandler 을 상속받은 클래스는 요청명이 존재해도 컨트롤과는 달리 위에 RequestMapping 을 하는게 아니고 WebSocketConfigurer 구현한 클래스에서 직접 매핑을 시켜줌. 등록은 @ComponentScan 이 아니고 WebSocketConfigurer 의 클래스에서 add 하여 사용한다. |
4. web.xml 의 init-param 에 등록
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
config.MvcConfig
config.BeanConfig
config.웹소켓환결설정
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<multipart-config>
<location>/upload</location>
<max-file-size>5242880</max-file-size><!--5MB-->
<max-request-size>20971520</max-request-size><!--20MB-->
<file-size-threshold>0</file-size-threshold>
</multipart-config>
</servlet>
WebSocket 으로 채팅 구현하기
1. client 상황 만들기(jsp 만들기)
<script>
var 웹소캣;
var 별명;
function 연결하다(){
//연결이 여기에 있으니 이 함수를 적용한 버튼부터 눌러야 웹소켓이 생성되고 session 으로 저장된다.
웹소캣=new WebSocket("ws://localhost:8080/chat");
웹소캣.onopen = function(){
let txt별명 = document.querySelector("#txt별명");
별명 = txt별명.value;
// json 문자열화 해서 보내기
웹소캣.send(JSON.stringify({'별명':별명,'상태':'입장'}));
}
웹소캣.onmessage=function(result){
let 받은메세지 = result.data;
let pMessage = document.querySelector("#pmessage");
pMessage.innerHTML=pMessage.innerHTML+"<br>"+받은메세지;
}
웹소캣.onclose=function(){
웹소캣 = null;
}
}
function 보내다(){
let txtmessage = document.querySelector("#txtmessage");
웹소캣.send(JSON.stringify({'별명':별명,'상태':'채팅',메세지:txtmessage.value}));
}
function 나가다(){
let txtmessage = document.querySelector("#txtmessage");
웹소캣.send(JSON.stringify({'별명':별명,'상태':'퇴장'));
}
</script>
</head>
<body>
별병
<input type="text" id="txt별명"><br>
<button onclick="연결하다()">연결</button><br>
<button onclick="나가다()">나가기</button><br>
메세지
<input type="text" id="txtmessage"/><button onclick="보내다()">전송</button><br>
<!-- p 태그처럼 <p></p> 로 닫히는 태그들만 innerHTML 이 있다. input 태그는 value 로 -->
<p id="pmessage"></p>
</body>
ws : websocket 의 프로토콜 임을 알림(http 처럼) 이 통신규약으로 spring 의 TextWebSocketHandler 로 통신하고 알아서 close()함 | ||
WebSocket : client 측의 socket 으로 연결을 걸 수는 있어도 받을 수는 없다. ( 연결을 거는 것이 메세지를 보내 답장을 받는 것이 아니고 처음에 통신연결을 거는것을 말한다. ) spring 에서는 이 소켓을 session 이라고도 부른다 (HttpSession 과는 이름은 같은 session 이라도 다 다르다.) 소켓을 처음에 받으면 session 을 저장하여 소켓이 계속 server 안에 있게 해야한다 (아래 예제) |
||
onopen : 연결이 되자마자 해당 함수 실행 | ||
send : 해당 웹소켓에 데이터 보냄 | ||
onmessage : 서버측에서 메세지가 오면(sendMessage()) 실행 |
2. json 객체를 받을 클래스 생성
public class 메세지Data {
String 메세지;
String 상태;
String 별명;
public String get메세지() {
return 메세지;
}
public void set메세지(String 메세지) {
this.메세지 = 메세지;
}
public String get상태() {
return 상태;
}
public void set상태(String 상태) {
this.상태 = 상태;
}
public String get별명() {
return 별명;
}
public void set별명(String 별명) {
this.별명 = 별명;
}
}
3. jsp 에서 메시지와 별명 등을 받아서 출력해줄 클래스
//연결 및 메세지 처리기
@Component
public class MessageHandler extends TextWebSocketHandler{
List<WebSocketSession> 연결자들 = new ArrayList<WebSocketSession>();
//ws 프로토콜로 요청받을때마다 작동
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String 메세지만 = message.getPayload();//json이 문자열화 된 것: "{별명: ,상태: ,}"
ObjectMapper objectMapper = new ObjectMapper();
// readValue() : 지금까지 ajax 통신값인 json문자열을 @RequestBody 붙은 객체의 멤버변수에 알아서 들어갔듯이
// json 문자열을 해당 클래스의 멤버변수에 넣어주는 메서드
메세지Data 한메세지Data = objectMapper.readValue(메세지만, 메세지Data.class);
TextMessage 보낼message = null;
if(한메세지Data.get상태().equals("입장")) {
연결자들.add(session);
보낼message = new TextMessage(한메세지Data.get별명()+"님이 입장하였습니다.");
}else if(한메세지Data.get상태().equals("채팅")) {
보낼message = new TextMessage(한메세지Data.get별명()+": "+한메세지Data.get메세지());
}else if(한메세지Data.get상태().equals("퇴장")) {
보낼message = new TextMessage(한메세지Data.get별명()+"님이 퇴장하였습니다.");
}
for(WebSocketSession webSocketSession:연결자들) {
webSocketSession.sendMessage(보낼message);
}
//위의 매개변수인 session 은 List<WebSocketSession> 연결자들 처럼 모든 세션이 아니라
//이 요청을 한 본인만이다.
if(한메세지Data.get상태().equals("퇴장")) {
session.close();
연결자들.remove(session);
}
//연결처리
//메세지 처리
//끊기 처리
}
}
* 네트워크 형상관리 | ||
형상관리 : 소프트웨어 생명주기의 단계적 산출물에 대한 가시성과 추적가능성을 체계화하는 품질보증 활동이다. ( 어떤 프로그램이 수정될 때 버전을 업그레이드 시킨다는 뜻으로 보면 될 듯 ) |
||
1. 의미 - 형상항목 : 버전을 바꿔야 할 대상 ( 버전을 관리할 대상 ) - 베이스라인 : 기준선 (무조건 버전을 바꿔주는 것이 아닌 이 기준을 보며 판단) - 형상관리위원회 : 형상관리를 하는 곳 ( 팀내에서 팀장일 수도 있고 ..등등 ) - CMDB(Configuration Management Database) : 버전이 어떻게 바뀌었는지 이력을 저장함 ( 히스토리 ) |
||
2. 프로세스 형상식별 -> 형상통제 -> 형상감사 -> 형상기록 의 절차를 가지고 있다. - 형상식별 : 버전을 관리할 (변경할) 대상이 있구나 식별. ( 형상항목을 식별 ) - 형상통제 : 변경관리를 연계하여 수행해야 한다. 즉, 변경이 되었어도 그게 버전을 붙일만한 일인지 아직 그럴 정도의 변경이 아닌지 확인. ( 베이스라인이 쓰일 듯 하다. ) - 형상감사 : 변경사항을 확인하고 검토한다. ( 형상관리위원회가 체크할 거 같다. ) - 형상기록 : 형상관리한 히스토리를 남김 ( CMDB가 쓰인다.) |