웹 소켓 통신
(Web Socket)
웹 소켓(Web Socket)은 두 프로그램간의 메시지를 교환하기 위한 통신방법 중 하나입니다. 웹 소켓은 W3C와 IETF에 의해 자리잡은 표준 프로토콜 중 하나이기 때문에, 현재 인터넷을 사용하는 환경에서 특히 많이 사용되고 있습니다. 예를 들면, 크롬(Chrome)이나 엣지(Edge) 등의 웹 브라우저 같은 경우에 웹 소켓 프로토콜을 지원하고 있기 때문에, 웹 브라우저를 통해서 웹 소켓을 사용하려고 한다면, javascript를 사용해서 어렵지 않게 개발할 수 있습니다.
웹 소켓 특징 2가지
1. 양방향 통신(Full-Duplex)
양방향 통신이란 데이터 송수신을 동시에 처리할 수 있는 통신방법을 말하는 것입니다. 웹 소켓은 클라이언트(client)와 서버(server)가 서로에게 원할 때 데이터를 주고 받을 수 있으니 양방향이라고 말할 수 있는 것이죠.
※ 참고로, 양방향 통신이 아닌 단방향 통신은 한쪽으로만 송신이 가능한 방법을 말하는 것입니다.
2. 실시간 네트워킹(Real Time-Networking)
웹 환경(ex, 웹 브라우저)에서 채팅(chatting), 주식(stock), 비디오(video) 데이터 등의 데이터들은 연속된 데이터를 화면에 빠르게 보여주어야 하거나, 여러 단말기(ex, PC, 스마트폰)에 빠르게 데이터를 교환하는 실시간처리가 필요한 부분들이 있습니다. 예를 들어, 요즘 가상화폐들의 변하는 가격들을 웹 사이트에서 실시간으로 볼 수 있는데 이런 서비스를 구현할 때 사용될 수 있는 것이죠.
※ 웹 소켓 특징 중 하나가 실시간 네트워킹인 이유는 웹 소켓이 아닌 다른 비슷한 기술들로는 웹 브라우저 위에서 실시간으로 통신하기가 웹 소켓에 비해 상대적으로 어렵기 때문입니다.
웹 소켓 이전의 비슷한 기술
- Polling
- Long Polling
- Streaming
- XML Http Request
- Ajax (Asynchronous Javascript + XML)
사실, 웹 소켓이 등장하기 이전에, 웹 소켓의 기능과 동일하게 만들 수 있는 기술들이 있었습니다. 만약에, 웹 소켓의 이해가 부족하거나 웹 소켓을 이용해서 개발하기가 부담스러운 환경이라면, 기존의 기술들인 위 목록에 나와있는 것들 중에서 하나를 골라서 개발을 해도 웹 소켓의 기능을 비슷하게 구현가능 할 것입니다. 본 포스팅에서는 웹 소켓이 가지는 "양방향 통신"과 "실시간 통신"이라는 두가지 특징을 가지고 이전의 다른 기술들보다 조금 더 쉽게 구현할 수 있다는 것만 알아두겠습니다.
※ XML Http Request이나 Ajax의 경우,클라이언트가 서버에게 원할 때 데이터를 보내서 요청하는 것이 목적이기 때문에, 그 반대가 어렵습니다. 서버가 클라이언트에게 원할 때 데이터를 보내는 것은 어렵다는 것이죠.
웹 소켓 동작방법
웹 소켓 Handshaking 메시지 교환
- Web Client는 handshake 요청 메시지를 Web Server에게 보낸다.
- Web Server는 handshake 응답 메시지를 Web Client에게 보낸다.
- Web Client는 data payload frames를 Web Server에게 보낸다.
- Web Server는 data payload frames를 Web Client에게 보낸다.
- Web Client는 close frame을 Web Server에게 보낸다.
- Web Server는 close frame을 Web Client에게 보낸다.
웹 소켓 프로토콜 특징
- 최초 접속에서만 http 프로토콜 위에서 handshaking을 하기 때문에 http header를 사용한다.
- websocket을 위한 별도의 포트는 없으며, 기존 포트(http-80, https-443)를 사용한다.
- websocket 프로토콜의 ws는 http기반으로 운영되고, wss는 https기반으로 운영된다.
- 메시지 (Upgrade:Websocket, Connection:Upgrade)
- 메시지에 포함될 수있는 교환 가능한 메시지는 텍스트(text)와 바이너리(binary)이다.
웹 소켓 개발
웹 소켓 동작에 대한 이해를 돕기 위해, 실제로 웹 소켓을 사용해서 개발해야하는 경우, 개발환경에 따라 어떻게 사용될 수 있는지를 코드 일부를 추가해봤습니다. 우선 위 그림을 보면, 3개의 클라이언트와 한 개의 서버가 그려져 있습니다. 클라이언트 중 둘(=A와 B)은 javascript 코드를 사용해서 구현된 웹 애플리케이션(Web Application) 형태로 웹브라우저 위에서 동작되고, 다른 하나(=C)는 java 코드를 사용해서 구현된 안드로이드 애플리케이션(Android Appliacation) 형태로 동작되고 있다고 생각해보겠습니다. 이 3개의 클라이언트 애플리케이션는 웹 서버인 D와 웹 소켓을 사용해서 통신이 가능합니다.
웹 클라이언트인 A, B, C는 모두 서버 D와 통신하기 위해 "ws://joker.com/say"로 접속요청을 하고 있는 것을 볼 수 있습니다. 클라이언트는 서버에서 웹소켓을 위해 열어둔 해당 주소로 메시지를 보낼 수 있습니다. 반대로,서버가 접속된 클라이언트의 웹 소켓을 통해 메시지를 보낼 수 있죠. 이렇게 서로 웹 소켓이 연결되어 있다면 이를 통해 서로에게 원할 때 메시지를 보낼 수 있는 양방향 통신을 할 수 있게 되는 것입니다.
1. 클라이언트 코드
클라이언트로 사용할 수 있는 코드 몇 가지를 소개하겠습니다. 웹 소켓은 웹 기반의 기술이다보니 크롬(chrome), 엣지(edge) 등의 유명한 브라우저들은 개발자들이 쉽게 사용할 수 있도록 이미 javascript로 제공해주고 있습니다. 먼저, WebSocket API는 거의 대부분 브라우저들이 제공해주고 있습니다. 하지만,가끔 WebSocket API를 제공하지 않는 브라우저들이 있습니다. 그렇다고 사용자들한테 WebSocket이 있는 브라우저로 가서 사용하라고 할 수는 없겠죠. 그래서 웹 브라우저가 WebSocket을 제공해주지 않는다고 하더라도 앞에서 말한 HTTP Streaming이나 HTTP Long Polling 등의 기술들을 통해 WebSocket과 동일하게 동작하도록 시켜주는 SockJS라는 라이브러리가 있습니다. WebSocket API나 SockJS 모두 인터페이스는 거의 동일하기 때문에 큰 차이없이 사용할 수 있을 것입니다.
웹 브라우저가 아닌 다른 플랫폼인 안드로이드에서도 웹 소켓을 사용할 수 있습니다. WebSocketClient라는 라이브러리를 사용하면 웹 소켓 기능을 어렵지 않게 구현할 수 있습니다. 현재(2019년 10월), java나 android에서 웹 소켓을 위해 기본으로 제공해준는 API가 없기 때문에 직접 구현하거나 어느정도 안정되어 사용할 수 있는 WebSocketClient 라이브러리를 사용하면 됩니다.
코드 : javascript
// 거의 모든 브라우저에서 지원
{
var uri = "ws://joker.com/say";
var ws = new WebSocket(uri);
ws.onopen = function() {
..
}
}
코드 : javascript (SockJS)
// SockJS Client 사용
{
var uri = " ws://joker.com/say";
var sock = new SockJS(uri);
sock.onopen = function() {
..
};
}
코드 : Java (in Android)
import org.java_websocket.client.WebSocketClient;
{
Uri uri = new URI("ws://joker.com/say”);
WebSocketClient ws = new WebSocketClient(uri) {
public void onOpen(..) {
..
}
}
}
2. 서버 코드
서버는 스프링(springframework)을 사용하는 경우를 예로 들었습니다. 스프링에서는 웹 소켓에 대한 API를 제공해주고 있기 때문에, 스프링 개발 가이드 등의 많은 곳들에서 관련된 예제들을 살펴볼 수 있을 것입니다. 참고로,아래 registerWebSocketHandlers() 내용 중 “(2)”번 라인의 경우 웹 브라우저에서 WebSocket API를 제공하지 않을 경우, SockJS 라이브러리에 정의된 대로 동작하도록 추가시킨 것입니다. 따라서, "(2)"번 라인의 경우 현재 사용되고 있지만 만약에 모든 브라우저가 기본으로 제공된다면 제거를 해도 상관없을 것입니다.
코드 : Java (using Spring Framework)
// import org.springframework.web.socket.WebSocketHandler;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
private final WebSocketHandlerwsHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new wsHandler(), "/say").setAllowedOrigins("*"); // (1)
registry.addHandler(new wsHandler(), "/say").setAllowedOrigins("*").withSockJS(); // (2)
}
}
웹 소켓 보안 고려사항
1. Non browser clients
웹 소켓은 클라이언트를 웹 브라우저를 사용해서 운영될 경우, header의 origin정보를 확인함으로써, 악성 javascript를 걸러낼 수 있습니다. 이 말은 웹 브라우저에서 기존에 사용하고 있던 동일 근원 정책(Same-Origin Policy)을 통해 악성 리소스(resource)로부터 보호할 수 있다는 것입니다.
※ Same-Origin Policy는 웹 브라우저의 페이지에 보여지기 위해 필요한 이미지(ex, jpg, png)나 javascript나 css등을 포함하는 모든 리소스(resource)들은 동일한 사이트로부터 가지고 와야한다는 정책으로 웹 브라우저에서 보안을 위해 만들어둔 기술을 말하는 것입니다.
웹 소켓은 클라이언트를 웹 브라우저가 아닌 다른 곳에서(ex, android, ios, windows) 운영될 경우, 악성 resource(ex, 악성 javascript)로부터 보호를 할 수 없으므로, 클라이언트와 서버는 이를 고려해서 사용해야 합니다. 웹 소켓은 웹 브라우저 위의 웹 페이지를 통해 사용되는 환경에서 운영되는 프로토콜로 만들어진 것이기 때문에, 웹 브라우저가 아닌 다른 클라이언트에서는 Same-Origin Policy와 같은 정책들을 전혀 사용할 수 없는 것이죠.
2. Origin considerations
앞에 1번 고려사항에서 언급했듯이, 웹 소켓은 origin 모델을 사용해서 악성 javascript로부터 보호하고 있습니다. 웹 브라우저에서 Same-Origin Policy을 통해 보호을 하고 있듯이, 웹 서버에서도 origin을 확인해야 하는 로직이 추가되어야 합니다.따라서, 서버에서 모르는 origin으로부터 요청이 들어온다면 "HTTP 403 Forbidden" 에러 상태 코드로 응답을 해서 통신을 못하도록 막아야 합니다.
3. WebSocket Client Authentication
웹 소켓 프로토콜은 핸드셰이킹(handshaking) 단계에서 서버가 클라이언트를 인증하는 특별한 방법을 제시하지 않고 있습니다. 따라서, 클라이언트 인증이 필요한 경우 HTTP 인증이나 TLS 인증 등을 사용해야 합니다.
※ 참고로, TLS 인증의 경우 핸드셰이킹 과정에서 클라이언트와 서버는 서로에게 자신의 인증서(Ceritificate)를 전달하고 이를 검증함으로써, 인증을 할 수 있습니다.
참고
Javascript WebSocket API 설명
https://developer.mozilla.org/ko/docs/Web/API/WebSocket
SockJS API 라이브러리
https://mvnrepository.com/artifact/org.webjars/sockjs-client/0.3.4
WebSocket 프로토콜 RFC 6455