Computer Engineering/web
[WEB] 실시간 서비스를 구현해보자. HTTP vs WebSocket
soohey
2022. 7. 25. 15:05
실시간으로 소통하는 서비스를 만들려면 어떤 방식을 사용해야 할까?
실시간이라는 말 자체가 연결이 지속되어 있는 상태가 유지되어야 할텐데, 내가 기존에 알고있던 HTTP는 요청 응답후 연결을 닫아버린다.
HTTP의 고질적인 문제를 해결하는 방법(웹 소켓)을 소개하려고 한다.
또한 연결이 지속되지 않아도 실시간처럼 눈속임을 할 수 있는 방법들(코멧)도 살펴보자.
HTTP 통신
- 클라이언트 요청이 있을 때만 서버가 응답하여 해당 정보를 전송하고 곧바로 연결을 종료한다.
- 실시간 연결이 아닌, 필요한 경우에만 서버에 접근하는 콘텐츠 위주의 데이터 연결
- 실시간 서비스를 구현하려면 통신이 종료될때까지 계속해서 http 요청을 서버로 보내야한다. 이러한 연결요청은 과부하 위험이 있다.
소켓 통신
- 서버와 클라이언트가 특정 포트를 통해 실시간으로 양방향 통신을 하는 방식
- http 통신과 다르게 포트를 이용한 연결을 유지하고 있어 스트리밍 중계나 채팅 같은 즉각적인 정보를 주고 받을 수 있다.
이렇게 서버와 클라이언트 사이의 연결이 끊기는 HTTP 통신과 연결을 지속하는 소켓 통신으로 나눠볼 수 있다.
과거에는 이러한 HTTP의 문제점을 보완하여 실시간 서비스로 눈속임을 하는 폴링 및 코멧방식을 사용하였다.
폴링
- 프로그램이 충돌 회피, 동기화 처리를 목적으로 다른 프로그램의 상태를 주기적으로 검사하여 일정 조건이 만족되면 데이터 송수신을 하는 방식
- 주기적인 시간마다 클라이언트가 서버로 요청을 동기적 호출해서 사용가능한 정보가 있는지 확인한다.
- 요청은 주기적인 간격으로 이뤄지며 클라이언트는 정보가 있든 없든 응답을 받는다.
- 정보가 있으면 서버는 정보를 송신하고 없으면 부정적인 응답을 반환하고 클라이언트는 연결을 닫는다.
- 메시지가 전달되는 간격을 정확히 알고 있을 때 적절한 해결책이다.
- 양방향 통신을 근접하게 구현하기 위한 방법
- 폴링의 주기가 짧으면 성능에 부담이 가고, 주기가 길면 실시간 성능이 떨어진다.
- 서버에서 정보 변경이 없으면 응답 과정에서 리소스를 낭비하거나 오버헤드 트래픽이 발생한다.
롱폴링 기법
- 폴링의 문제점을 개선함
- 클라이언트가 요청시 서버가 요청에 대한 연결을 이벤트가 발생할때까지 가지고 있다.
- 변경이 빈번하다면 이득이 크지 않음
- 일반적으로 HTTP protocol과 JavaScript에서 await 문법과 함께 구현된다.
async function subscribe() {
let response = await fetch("/subscribe");
if (response.status == 502) {
// Status 502 is a connection timeout error,
// may happen when the connection was pending for too long,
// and the remote server or a proxy closed it
// let's reconnect
await subscribe();
} else if (response.status != 200) {
// An error - let's show it
showMessage(response.statusText);
// Reconnect in one second
await new Promise(resolve => setTimeout(resolve, 1000));
await subscribe();
} else {
// Get and show the message
let message = await response.text();
showMessage(message);
// Call subscribe() again to get the next message
await subscribe();
}
}
subscribe();
폴링, 롱폴링 모두 푸쉬 기술 구현을 위한 대안이었지만 클라이언트 요청을 통해야만 서버에게 데이터를 전송받는다는 한계점이 존재한다.
-> 불필요한 헤더와 높은 트래픽을 야기하는 단점이 생긴다.
코멧(comet)
- 웹 페이지가 다시 로드되지 않고 실시간으로 변동사항을 반영해 변화하려면 어떻게 해야할까? 예를 들면 실시간 모니터링 도구, 스포츠 실황중계처럼 매우 짧은 시간간격으로 변화하는 서비스, 실시간 채팅 서비스등이 있다.
- 서버가 모니터링하다가 그 상태를 수시로 클라이언트에게 전달해야 한다. 이것을 서버 푸쉬라고 하는데 웹 http 로는 구현할 수 없다. HTTP는 클라이언트가 요청하면 서버는 응답후 연결을 끊는다. 클라이언트가 요청해야 서버가 움직일 수 있다.
- 이러한 문제를 해결하기 위한 편법이 코멧이다.
- 코멧도 HTTP를 이용하기에 완벽한 실시간(real-time)은 아니다. 그저 짧은 시간간격으로 웹페이지를 업데이트하는 것일뿐이다. 하지만 시간간격이 짧으면 짧을수록 서비스에는 큰 문제가 없다. 이런 부분때문에 실시간으로 간주한다.
코멧의 종류 4가지
1. Ajax polling
- Ajax로 상태를 가져온 후 화면을 업데이트하는 자바스크립트를 작성하고, setInterval()로 일정시간마다 이 함수를 호출하는 방식
2. Hidden iframe
- css를 이용해 보이지 않게 한 <iframe>태그를 만들어놓고, 이 태그가 임베디드하는 파일이 연결을 끊지 않고 계속 작동하면서 화면을 업데이트하는 스크립트를 실행하는 방식
3. Long Polling by Ajax
- Ajax로 호출하는 파일이 반복문을 돌며 대기하다가 화면을 업데이트해야할 상황이 되면 다시 동일한 파일을 Ajax로 호출하는 과정을 반복한다.
4. Long Polling by Script Tag
- <script> 태그가 임베디드하는 파일이 화면을 업데이트하는 스크립트를 실행한 후 다시 자신을 임베디드하는 스크립트 태그를 만드는 방식
Server-sent Events
- 웹 브라우저 관섭없이 서버에서 단방향으로 이벤트를 전송한다
- 웹 브라우저가 이벤트를 구독하여 서버의 이벤트를 전송받는 기법이다.
- 웹 브라우저에서는 서버로 이벤트를 요청할 수 없다.
- HTML5 표준에 포함되어있으며 HTTP 프로토콜 기반으로 동작한다. 최소 HTTP 2.0을 통해 이용하는 것을 권장한다.
- 일반적으로 JavaScript의 EventSource()함수를 통해 이용한다.
- 웹소켓과 비교하면 Event Subscribe(이벤트 구독)에 특화되어 있다.
- 단방향 통신만 지원하지만 각 이벤트를 이벤트 ID를 통해 관리하고, 네트워크 장애시 재연결 과정도 웹소켓에 비해 잘 정의되어 있다.
Streaming
- 하나의 웹 요청에 대해 웹 접속을 계속 열어두고 데이터를 이벤트 때마다 지속적으로 내려받는 방식
- 이는 응답마다 다시 요청하는 롱 폴링에 비해 효율적이고 서버의 상태 변경이 잦은 경우 유리하지만 연결을 언제까지 맺을 건이지에 관한 유효성 관리등의 부담이 발생한다.
웹소켓
- 위와 같은 방법들은 HTTP가 가지는 한계인 단방향 메시지 교환 규칙을 변경하지 않고 구현한 방식이다. 때문에 코드가 복잡해진다.
- 브라우저와 서버사이의 동적인 양방향 메시지 송수신을 위해서 HTML5 표준안에 WebSocket AP가 등장했다. 웹서버, 클라이언트 모두 websocket API가 지원되어야 한다.
- socket.io는 node.js모듈로서 자바스크립트를 이용해 브라우저 종류에 상관없이 실시간 웹을 구현할 수 있도록 하는 기술이다.
- 서버와 클라이언트 사이 웹소켓이 열리면 서버 또는 사용자 둘 중 하나가 세션을 닫을 때까지 언제든지 메시지를 보낼 수 있다.
- http 는 클라이언트만 필요한 경우에 요청을 보낼 수 있지만 소켓프로그래밍은 서버역시 클라이언트에 요청을 보낼 수 있으며 계속 연결을 유지하는 연결 지향형 방식이기에 실시간 통신이 필요한 경우 자주 사용한다.
- 단점으로 웹소켓과 http 세션이 서로 다르다. 주기적으로 웹소켓 세션과 서버에 저장하는 http 세션을 동기화하는 모듈을 구현해야 한다.
- 대용량 트래픽에서는 서버가 클라이언트 수만큼 연결을 유지해야 한다는 부담이 존재한다.
- 접속 확인시 HTTP를 사용하고 이 후의 통신에서는 WS 프로토콜을 사용한다. 헤더가 매우 작아 오버헤드가 적다.
- 장시간 접속이 전제되기 때문에 접속한 상태라면 클라이언트나 서버로부터 데이터 송신이 가능하다. 데이터 송신과 수신에 각각 커넥션을 맺을 필요가 없어 하나의 커넥션으로 데이터를 송수신할 수 있다.
웹 소켓이 필요한 경우
- 실시간 양방향 데이터 통신이 필요
- 많은 수의 동시 접속자를 수용
- 브라우저에서 TCP 기반 통신으로 확장
- 개발자에게 사용하기 쉬운 API 형식
- SOA(Service Oriented Architecture)로 확장해야 하는 경우
실시간 통신하는 경우 웹소켓을 사용하는 것이 바람직해 보인다.
하지만 이전에 사용하던 기술들이 어떠한 불편함이 있었는지 어떻게 동작하는지 생각해보는 것이 웹소켓의 필요성을 이해하는데 도움이 될 것 같아 이렇게 정리를 하게 되었다.
웹 소켓은 실시간 서비스에 적합하지만 그 만큼 많은 비용과 코드가 복잡해질 수 있는 어려움이 있기 때문에, 앞서 말했던 기존 방법말고 정말 사용해야 할 것인지에 대한 고민이 필요해보인다.
참고