온라인 게임을 생각해 보자. 게임이라 그럴 수 있지만, 몇 분만, 아니 몇 초만 해봐도
이는 무조건 실시간 통신임을 확인할 수 있다.

우리가 흔히 ping이라고 하는 응답 속도가 우리나라 내의 유저끼리는 한 자릿수의 ms정도가 되고,
이 말은 실제 상대방의 무빙(입력)은 우리에게 그 정도의 시간 내에 오기에, 이는 거의 실시간과 동일하다고 볼 수 있다.
그렇지만 이것은 정해진 상대들에게만 입력 정보를 보내주기에 괜찮을 것으로 생각한다.
하지만 한 때 존재했었던 검색 엔진에서의 실시간 검색어 순위나 이런 것은 어떤가?

만약 네이버 실시간 동시접속자 모두에게 fps게임마냥 ms단위로 실시간 정보를 바로바로 주어야 한다면...
아마 네이버는 트래픽을 감당하지 못하고 터지지 않을까?
그렇기 때문에 이들은 실시간이라고 써놓았지만 "실시간 인 척"을 하는 방식으로 우리에게 실시간 검색어 순위를 주었다.
1분 단위로 실시간 검색어 순위가 바뀌도록 말이다.
이 글에서는 웹이 당신에게 실시간 처럼 보이는 방식과, 진짜 실시간으로 통신하는 방식을 소개하고자 한다.
글을 시작하기에 앞서, 실시간에 대해 정확히 얘기하자면... 네트워크에서 실시간이란 없다.
네트워크 기반의 통신은 원천적으로 레이턴시(지연)가 있을 수밖에 없고,
특정 수준까지 최적화 하는 방식으로 "거의 실시간"을 제공할 뿐이다.
폴링(Polling) : 클라이언트(웹 브라우저)에서 서버에 Ajax 통신을 주기적으로 요청한 후,
응답받은 데이터를 화면에 렌더링 하는 방법 결론적으로, 실시간성의 보장은 클라이언트가 갖는다고 볼 수 있다.
폴링의 주기를 서버 측이 데이터를 갱신하는 타이밍과 맞춘다면, 어느 정도의 실시간성이 보장되지만
만약 주기가 조금이라도 엇나간다면 최대 해당 주기만큼 지연된 데이터를 실시간 데이터로 인식할 수 있기 때문이다.
그리고 간단히 구현 가능하나, 주기가 고정되어 있어 트래픽의 낭비가 생길 수 있다.
여기에서 조금 더 나아진 방식이 롱 폴링이다.
롱 폴링(Long Polling) : 클라이언트에서 서버에 요청한 후, 실시간 정보를 반환할 때까지 connection을 그대로 열어 놓는 방식이다.
그렇기 때문에 실시간성의 보장은 서버측이 어느 정도 담당한다고 볼 수 있다.

그리고 서버 측이 진짜로 실시간 데이터를 갱신했을 때마다 클라이언트 쪽으로 실시간으로 보내주는 방식도 있다.
서버 센트 이벤트(Server-Sent Events) : 주식 정보, 날씨 정보 등의 데이터를 업데이트 할 때 사용한다.
서버에서 클라이언트에 단방향으로 제공하는 데이터이다. 양방향 통신이 필요 없지만 실시간 정보의 업데이트는 필요할 때
사용하는 것이다. 이는 데이터의 실시간성 보장을 서버 측이 온전히 담당한다고 할 수 있다.
참고로 자동 재연결 기능이 있어, 연결이 끊어진다고 해도 다시 정보를 보장할 수 있다.
주목할 만한 점은 위의 세 개 방식 모두 HTTP 통신을 기반으로 한다는 것이다.
기본적으로 TCP의 3-way 핸드쉐이크를 통해 연결이 설정된 후 데이터를 주고 받는다.
- 폴링의 경우, 클라이언트가 주기적으로 HTTP 요청을 보낸다. 요청마다 새로운 TCP 연결을 생성할 수도 있고,
연결 재사용(keep-alive)을 사용할 수도 있다. - 롱 폴링의 경우, 클라이언트가 서버에 요청을 보내고, 데이터가 생길 때까지 연결을 유지한다.
데이터가 클라이언트 측에 도착하면 연결이 끊기고 다시 연결을 열어두는 상태로 진행한다. - SSE의 경우, 클라이언트가 서버에 연결을 요청하면, 서버가 해당 연결을 장시간 유지하면서
지속적으로 이벤트를 클라이언트 측으로 푸시한다.
참고로 웹훅(Webhook)이라는 개념도 존재하는데, 이는 서버 간 일회성 HTTP 요청으로 동작한다.
이벤트가 발생하면, 설정된 URL로 HTTP POST 요청을 보내어 알림을 전달한다.
예를 들어서 GihHub에서 푸시 이벤트가 발생하면, 웹훅을 사용하여 CI/CD 서버에 알림을 보내는 방식을 사용한다.
지금까지는 어느 한쪽 방향에서 일방적인 데이터 송신에 대한 실시간성 보장만을 했지만,
다자간 실시간 보장을 하기 위한 방식에는 어떤 것이 있을까? 말 그대로 진짜 실시간성 보장을 위한 통신 방식 말이다.
여기서 사용되는 것이 웹소켓이다.
Web Socket
서버와 클라이언트 사이에 소켓 커넥션을 유지하면서, 양방향 실시간 통신이 가능한 기술이다.
HTTP Handshake를 통해 초기 connection을 만든 후에, WebSocket 프로토콜로 변환하여 데이터를 전송한다.
서버로 HTTP Handshake 요청 후 응답코드 101을 받으면, 이후 ws 또는 wss 프로토콜을 통해서
통신을 계속해서 주고받는 것이다. (ws와 wss는 http와 https의 관계라고 보면 된다.)
Spring Boot에서의 Websocket
Spring Boot에서는 기본적으로 WebSocket과 관련된 라이브러리를 제공해 준다.
Spring Initializr에서 이 라이브러리를 제공해 준다는 것은 다시 말해
현재 Spring Boot 버전에 맞는 WebSocket 라이브러리의 버전을 맞춰서 import 해준다는 것을 의미한다.

Gradle에서는 아래처럼 dependencies에 추가해 주면 된다.
implementation 'org.springframework.boot:spring-boot-starter-websocket'
가장 기본인 텍스트를 통한 채팅의 구현을 위해서 기본적으로 구현 / 상속해야 할 것은 3개이다.
- Interface) WebSocketConfigurer : WebSocket의 설정을 등록한다. Handler와 Origin을 기본적으로 설정한다.
- Class) TextWebSocketHandler : AbstractWebSocketHandler에서 정의한 기초적인 메서드에
현재 채팅을 어떻게 handle 할지 정의할 class로, 우리는 지금 단일 서버에 인메모리 세션들을 관리하므로
WebSocketSession을 관리하는 Map을 설정하고, 서버의 인메모리에서 각각의 연결(세션)들을 처리해야 한다.
이 클래스를 상속해 아래에 나열 한 AbstractWebSocketHandler의 4가지 메서드를 오버라이드 한다.- afterConnectionEstablished : 연결이 생성된 이후 해야 할 훅을 정의
- handleTextMessage : 텍스트 메시지가 생성될 때 해야 할 훅을 정의
- afterConnectionClosed : 특정 사용자가 연결이 종료 됐을 때 해야 할 훅을 정의
- handleTransportError : 소켓 통신 에러 시 해야 할 훅을 정의
- Class) Message : 이건 정해진 것은 아닌데, 웹소켓에서 다룰 Message 객체들을 정의하기 위해서 사용한다.
추가적으로, 각각의 session의 ID는 따로 설정하지 않아도
StandardWebSocketSession 구현체가 UUID로 중복되지 않게 생성한다.
테스트 툴은 Postman을 사용했다.
다른 접속자가 생기면 아래처럼 새롭게 들어왔다고 다른 사용자게에 채팅이 날아오고,

다른 접속자에게 아래처럼 메시지를 보낼 수 있다.
sender의 경우, 아직은 디테일하게 설정하지 않았지만
receiver는 무조건 제대로 설정해야 한다.

그러면 아래처럼 다른 사용자가 메세지를 받는다.

웹소켓에 대한 고도화된 글을 추가로 다룰 예정이다.
추가적으로 캐시를 쓰는 방식 또한 어느 정도의 최신성을 보장하는 방법이라고 할 수 있다.
서버에서 캐시 관련 헤더에 옵션을 달아서 최신성을 유지할 수 있다는 것이다.
예를 들어서 revalidate 옵션이나 max-age=0과 같은 옵션을 달아둔다면
이를 통해서 받아지는 모든 데이터는 매번 서버의 데이터와 맞는지 틀린 지를 확인하기 때문에
어느 정도의 실시간에 가까운 최신 데이터를 받는 것이라고 생각할 수 있다.
실시간 데이터를 캐싱하여 효과적으로 전달하기
Android Real-time Architecture using cache conception
medium.com
웹훅 관련 자료
https://docs.tosspayments.com/resources/glossary/webhook
웹훅(Webhook) | 토스페이먼츠 개발자센터
웹훅이란 데이터가 변경되었을 때 실시간으로 알림을 받을 수 있는 기능입니다. 웹 서비스의 이벤트 데이터를 전달하는 HTTP 기반 콜백 함수입니다.
docs.tosspayments.com
https://brunch.co.kr/@springboot/695
Spring Websocket & STOMP
오랫만에 작성하는 기술블로그 포스팅입니다. 이 글에서는 스프링 부트 기반의 웹소켓 및 STOMP에 대해서 설명합니다. 이 글을 읽기 위해서는 기본적인 HTTP 지식이 있어야 하며, 스프링 프레임워
brunch.co.kr
https://ko.javascript.info/long-polling
롱 폴링
ko.javascript.info
https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
Using server-sent events - Web APIs | MDN
Developing a web application that uses server-sent events is straightforward. You'll need a bit of code on the server to stream events to the front-end, but the client side code works almost identically to websockets in part of handling incoming events. Th
developer.mozilla.org
'연재작 > Network' 카테고리의 다른 글
Image 파일은 서버를 거치지 않아도 된다. (0) | 2024.11.01 |
---|---|
AWS EC2 Private Instance 구축 (2) VPC, 서브넷, IGW, 라우터 테이블 (0) | 2024.10.31 |
GET 요청 시 Body(JSON)를 사용하지 않는 이유 (2) | 2024.10.28 |
AWS EC2 Private Instance 구축 (1) Architecture (0) | 2024.10.26 |
TCP/IP 4계층, OSI 7계층, Handshaking? chatGPT와 정리 (5) | 2024.09.01 |