웹소켓
#
Find similar titles
-
최초 작성자
hskim-intern@insilicogen.com
-
최근 업데이트
hskim@insilicogen.com
Structured data
- Category
- Programming
Table of Contents
웹 소켓 #
웹 소켓 이란? #
- 대부분의 웹 기반 클라이언트-서버 애플리케이션에서 HTTP의 요청-응답 모델은 서버로부터 클라이언트로의 정보 전송의 요청이 발생한 경우에서만 이루어지는 한계를 지님(Connection Less)
- 이러한 이유로 클라이언트-서버 간의 실시간 통신이 불가능
- 이를 해결하기 위해 Long-full, comet 등의 기술이 등장하였으나 full-duplex(전이 중) 통신에 대한 요구는 꾸준히 증가
- 웹 소켓 프로토콜은 HTTP 연결을 웹 소켓으로 업그레이드하기 위한 기술로, 이를 통해 웹 소켓의 양 종단 간의 독립된 양방향 메시지 전송이 가능
Life Cycle #
- 클라이언트 Peer에서 HTTP 핸드쉐이크 요청으로 웹 소켓 통신 시작
- 이에 대해 서버 Peer에서 이 핸드쉐이크 요청받아 핸드쉐이크 응답을 전송
- 완전하게 대칭인 전이 중 양방향 연결이 수립
- 어느 한쪽에서 연결을 종료할 때까지 양 Peer는 서로 간의 메시지 송수신이 가능
Java 웹 소켓 #
프로그래밍 방식 #
- JSR 356 스펙은 대부분의 JavaEE 개발자들이 쉽게 적용할 수 있도록 표준패턴과 기술에 따라 Annotation & Injection 기법을 지원
- 두 가지 모두 event-driven 방식에 기반
JSR 356 #
- JSR 356은 자바 클라이언트뿐만 아니라 서버 애플리케이션까지 웹 소켓 연결로 통합할 수 있는 웹 소켓 프로토콜 지원 스펙 의미
- 이에 따라 실제 웹 소켓 구현에 독립적인 웹 소켓 기반 애플리케이션 구현이 가능
- JAVA EE 7에 표준 스펙으로 채택
Broker #
- 뜻 그래도 중개자의 역할
- webSocket을 이용한다는 것은 클라이언트 간의 관계가 아닌 서버와 클라이언트의 peer to peer 관계이므로 메시지를 받아 정리하고 브로드캐스팅할 브로커 역할이 필요
- Spring 내에 SimpleBroker 사용할 수도 있고 외부의 rabbitMQ 같은 플러그인을 사용하여 중개자를 둘 수 있음(외부 플러그인 사용 시 spring은 일종의 서버 역할만 수행)
Interface-driven programming #
- Endpoint 인터페이스의 구현체를 통해 고정된 시그니처의 메서드로 웹 소켓 이벤트를 처리할 수 있음
- 어노테이션 지향 방식과 다르게 고정적인 프로그래밍 방법론에 따라, 고정된 시그니처의 메서드들을 가진 인터페이스(Endpoint)를 상속하여 구현
endPoint #
public class MyOwnEndpint extends javax.websocket.Endpoint{
public void onOpen(Session session, EndpointConfig config) {}
public void onClose(Session session, CloseReason closeReason) {}
public void onError(Session session, Throwable throwable) {}
}
- 수신된 메시지에 대한 핸들러는 Message Handler. Partial 나 Message Handler. Whole 구현체로 작성할 수 있고, 처리할 수 있는 메시지의 종류는 각 구현체의 generic type으로 결정되며, 메시지 핸들러들은 웹 소켓 연결 직후 open 이벤트 핸들러에서 해당 세션에 등록하는 과정을 거쳐야만 함
open #
public void onOpen(Session session, EndpointConfig config){
session.addMessageHandler(new MessageHandler(){...});
}
-
수신메시지는 각 메시지 타입을 직접 핸들링하거나, 메시지 타입 복부 호화를 위한 Encoder, Decoder 등이 제공되는 등 java WebSocket API 등의 여러 가지 지원 요소들을 통해 다양한 형태의 메시지 송수신이 가능
-
가장 기본적인 메시지 전송 형태는 텍스트 기반 메시지, 바이너리 메시지, pong 메시지 등이 있는데, 이러한 메시지들은 인터페이스 지향 방식을 사용하는 경우, 각기 다른 타입의 MessageHandler를 등록하여 처리할 수 있고, 어노테이션 지향 방식을 사용하는 경우, OnMessage 메서드의 파라미터 타입으로 각기 다른 메시지 핸들러를 작성할 수 있음
SockJS #
- 웹 소켓 API 지원 여부가 브라우저 혹은 웹서버에 따라 다른 관계로 다양한 우회 기법들을 추상화하여 공통된 인터페이스를 정의한 라이브러리
- SockJS는 http 프로토콜을 사용하기 때문에 웹 소켓 프로토콜을 지원하지 않는 런타임 환경에서도 애플리케이션의 코드를 변경하지 않고, 양방향 통신 가능
- 클라이언트에서 연결에 대한 요청을 보냈을 때 4번의 요청을 보내 연결을 수립
- handler 등록에 websocket:sockjs 추가
- webSocket 사용 시 ws:// 의 경로를 사용해야 하나 sockJS는 http 위에서 돌아가므로 contextPath/(endPoint) 형식으로 uri 작성
- JSP에서는 sockJS에 대한 CDN 설정 후 sockJS 객체를 생성하여 사용
STOMP(Simple Text Oriented Message Protocol) #
STOMP frame 구조 #
- stomp는 스크립트 언어를 위해 만들어진 문자 기반의 프로토콜
- WebSocket은 기존의 http와는 다르게 Line Header Body의 구조가 없이 메시지만 전달되는 구조이므로 전달 시 규약을 개발자가 구현해야 함
- stomp는 command subscribe send의 전송 구조를 갖기 때문에 개발자가 규약을 정할 필요가 없으며, 전송에 관련된 양 피어가 독립적으로 동작할 수 있는 구조를 가짐
- subscribe => 보내는 사람은 발행자, 받는 사람이 구독자의 개념 => 카테고리를 결정한다는 것은 어떤 구독자에게 보낼 것인가!!! (채팅방의 개념으로 접근)
frame 종류 #
- CONNECT : STOMP 클라이언트가 연결을 수립하고 스트림을 초기화할 때 사용.
- CONNECTED : 클라이언트의 연결 시도를 서버가 수락하면 사용되는 프레임.
- SUBSCRIBE : destination을 대상으로 전송되는 메시지를 청취하기 위해 사용.
- UNSUBSCRIBE : destionation 을 대상으로 한 구독을 취소하기 위해 사용.
- SEND (body) : 클라이언트가 destination을 대상으로 메시지를 전송하기 위해 사용.
- BEGIN (transaction): 트랜잭션의 시작을 의미하는 프레임
- COMMIT (transaction) : 트랜잭션 커밋 프레임
- ABORT (transaction) : 트랜잭션 롤백 프레임
- DISCONNECT : 연결 종료 프레임.
- MESSAGE (body) : 브로커에서 릴레이 된 구독 메시지가 전송될 때 사용되는 프레임.
- RECEIPT : 클라이언트가 보낸 프레임을 서버가 처리 완료하면, 전송되는 프레임.
- ERROR (body) : 에러 발생시 전송되는 프레임
메시지 중계 구조 #
- 스프링 빌트인 메시지 브로커를 사용하는 3개의 메시지 채널
- clientInboundChannel : 웹 소켓 클라이언트로부터 수신할 메시지 채널
- clientOutboundChannel : 웹 소켓 클라이언트에게 메시지를 송신할 채널
- brokerChannel : 애플리케이션 내에서 메시지 브로커에게 메시지를 송신할 채널
- STOMP 메시지 브로커로 연결하기 위해 클라이언트는 CONNECT 프레임을 전송하고, 수락되면 브로커에서 클라이언트로 CONNECTED 프레임이 전송 (연결 종료를 위해 DISCONNETCT 프레임이 전송되면, 서버에서는 이를 확인했다는 의미로 RECEIPT 프레임이 전송되고 소켓은 종료)
- 클라이언트는 SUBSCRIBE 프레임으로 구독하고자 하는 메시지의 카테고리를 결정하는데, 이때 하나의 구독에 대해 id가 부여되고, 구독 메시지의 카테고리는 destination 헤더로 표현
- 클라이언트는 SEND 프레임을 사용하여 메시지를 보내고, 서버에서는 MESSAGE 프레임으로 모든 구독자에게 브로드캐스팅 할 수 있음(이때 서버는 subscription 헤더를 통해 하나의 구독자를 표현하고, desination 헤더로 메시지의 카테고리를 표현)
웹 소켓의 특징 및 장점 #
- 실시간 양방향 통신 : 웹 소켓은 서버와 클라이언트 간에 양방향 통신을 지원하여 실시간 데이터 전송이 가능, 클라이언트나 서버에서 데이터를 전송하면, 상대방은 이를 즉시 수신하고 처리
- 저지연(latency) : 웹 소켓은 지속적인 연결을 유지하므로, 연결을 맺고 해제하는 과정에서 발생하는 지연이 줄어듦, 이에 따라 데이터 전송과 응답 시간이 크게 개선
- 효율적인 데이터 전송: 웹 소켓 프로토콜은 데이터 전송에 필요한 헤더 크기가 작아서 오버헤드가 적음. 이에 따라 대량의 데이터를 전송해도 성능 저하가 적음
- 단일 연결 유지: 웹 소켓은 하나의 TCP 연결을 유지하면서 여러 메시지를 주고받을 수 있음. 이에 따라 매번 연결을 맺고 끊어야 하는 HTTP보다 효율적인 통신이 가능
웹 소켓의 단점 #
- 부하 및 확장 어려움 : 웹 소켓은 지속적인 연결을 유지하므로, 클라이언트 수가 많아질수록 서버 부하가 증가할 수 있음, 이에 따라 서버의 확장이 어려울 수 있으므로 효과적인 부하 분산 및 확장 전략을 고려해야 함
- 서버 및 클라이언트 호환성 문제: 웹 소켓은 모든 브라우저에서 완벽하게 지원되지는 않을 수 있음, 특히 오래된 브라우저나 일부 모바일 디바이스에서 문제가 발생할 수 있음, 이 경우 폴백(fallback) 메커니즘을 구현하여 웹 소켓을 지원하지 않는 환경에서 대체 수단을 제공해야 함
- 보안 문제: 웹 소켓은 추가적인 보안 관련 고려사항이 필요할 수 있음. 웹소켓 연결은 보안 연결(SSL/TLS)을 통해 암호화해야 할 수 있으며, 인증 및 권한 부여 등을 고려해야함
- 네트워크 문제에 대한 처리: 웹 소켓 연결이 강제로 끊어지거나 네트워크 문제가 발생하는 경우에 대한 처리가 필요, 연결이 끊어진 후에도 어떻게 재연결하고 데이터 손실을 방지할 것인지 고려
- 중간 프락시 및 방화벽 문제: 웹소켓 연결은 일부 중간 프락시나 방화벽에서 문제를 일으킬 수 있음, 일부 네트워크 환경에서 웹 소켓 연결이 차단될 수 있으며, 이를 해결하기 위해 추가적인 구성이나 설정이 필요할 수 있음
- 요구되는 기술 지식: 웹 소켓을 구현하려면 해당 프로토콜 및 관련 기술에 대한 이해가 필요, 또한 실시간 통신 패턴에 대한 이해와 함께 클라이언트 및 서버 사이의 데이터 교환 방식을 잘 이해해야 함
- 메모리 관리: 지속적인 연결을 유지하는 웹 소켓은 메모리 사용량 측면에서 고려할 점이 있을 수 있음, 클라이언트 수가 많거나 연결이 오래 지속되는 경우 서버 메모리 사용량이 증가할 수 있음
spring 웹 소켓 이슈 공유 #
- 프로젝트에서 웹 소켓을 이용하여 푸쉬알림 구현과정에서 'refused to execute inline script because it violates the following content security policy directive ~'의 CSP 위반 이슈 발생
- 원인은 Spring에서 제공하는 웹 소켓과 SockJS객체를 사용하면 AbstractSockJsService 추상 클래스에 clientLibraryUrl이 "https://cdn.jsdelivr.net/sockjs/1.0.0/sockjs.min.js" 로 기본 설정 되었었던 이유
- 이러한 이유로 Client에서 endPoint로 HandShake를 요청하고 Server에서는 SockJs 객체를 사용하기 위해 위에 설정된 주소에 접근하여 JS파일을 불러오도록 되어있었고 프로젝트 정책 상 외부 파일을 불러올 수 없었던 이유로 CSP위반 이슈가 발생
- 웹 소켓 설정파일에 sockjs client-library-url path에 직접 js파일 경로를 매핑해주면 해결