Skip to content

Latest commit

 

History

History
272 lines (183 loc) · 14.2 KB

이론.md

File metadata and controls

272 lines (183 loc) · 14.2 KB

실시간 위치 공유 (이론)

image

서론

‘그룹별 실시간 위치 공유’라는 기능을 구현하기 위해 프로젝트에서 사용했던 기술은 STOMP였다. 고백하자면 STOMP를 선택하고 사용한 이유에 대해 명확한 근거가 부족했다. 당시 ‘단순히 많이 사용하고 Spring에서의 사용이 좋다’였다. 프로젝트가 끝나고 고찰을 하는 과정에서 실시간 통신에 대해 깊이 있게 탐구해보기로 했다.

실시간 통신을 위해서……

우선 일반적인 요청 - 응답 모델실시간 통신의 차이점을 짚고 넘어가자

요청 - 응답 모델

전통적인 클라이언트 - 서버 모델에서 동작하며 HTTP 프로토콜을 기반으로 동작한다.

  • 클라이언트는 서버에 요청(Request)하고 서버는 이에 대한 응답(Response)를 제공한다.
  • 특징
    • 비연결성
    • 단방향성

비연결성 (Stateless)

각 요청은 독립적이며 , 이전 요청에 대한 상태를 유지하지 않는다. 즉 (Request - Response)가 종료되면 클라이언트와 서버간의 연결은 유지되지 않는다.

단방향 통신

클라이언트 - 서버간의 역할이 확실하며 서버는 단순히 클라이언트의 요청에 대한 응답의 매개체 역할을 수행한다.

요청 - 응답 모델이 실시간 통신에 부적합한 이유

앞서 언급한 비연결성과 단방향 통신이 실시간 통신에 부적합한 이유이다. 실시간 통신이 수행되는 동안 양 측은 서로의 상태를 지속적으로 유지해야 한다. 즉 연결이 지속적이어야 한다. 또한 서로의 데이터를 수신하기 위해 양방향성 통신이 제공되어야 한다.

image

실시간 통신의 방법

실시간 통신을 구현하기 위한 방법은 다양하다. 대표적인 방법인 Polling, Long Polling, SSE와WebSocket에 대해 알아보자

Polling


일정한 주기를 가지고 클라이언트가 서버와 응답을 주고 받는 방식

  • 예를 들어 3초의 주기를 가지고 있다면 3초마다 새로운 데이터를 계속해서 얻어오는 방식이다.

Polling에 대한 중요 특징

  • Polling의 정의를 곱씹어본다면 중요한 것을 알 수 있다. 바로 ‘주기’라는 것이다.
  • ‘일정 주기 마다 데이터를 주고 받는다’는 것은 다른 말로 연결이 유지된다는게 아니라는 것이다 !!!!!!
  • 즉 요청마다 연결을 다시 생성하고 그 과정이 반복된다는 것이다 → 오버헤드의 위험성

Polling의 주기

  • 주기가 짧다면 서버에 더 자주 요청을 보내며, 실시간에 가까운 데이터 갱신을 목표
  • 주기가 길다면 서버에 덜 자주 요청을 보내며, 서버 부하를 줄이면서도 데이터를 정기적으로 갱신

image

즉 실시간 통신을 위해 Polling 방법을 사용한다는 것은 주기를 짧게 설정해서 지속적인 통신이 이루어지는 것처럼 보이게 한다는 것이다. 하지만 이 과정에서 지속적으로 연결을 만들고 종료하는 과정이 반복되므로 선호되는 방법은 아니다.

Long Polling


Polling 방식의 단점을 보완하기 위한 방법으로서 서버의 연결 시간을 좀 더 길게 유지하여 데이터를 주고받는 방식이다.

  • 즉 클라이언트가 서버에 데이터를 요청했을 때 바로 반환하는 것이 아닌 서버와의 접속 시간을 길게 설정하여 서버에서 이벤트가 발생하면 데이터를 반환받도록 하는 방식이다.

image

Polling VS Long Polling

image

Long Polling : 완벽 대체가 가능할까?

Long Polling을 통해 서버에서 이벤트가 발생할때까지 기다렸다가 통신을 통해 기존 Polling의 무자비한 통신을 줄일 수 있다. 하지만 완벽한 대체라고 보기엔 몇 가지를 고려해봐야 한다.

  • 이벤트 발생의 주기
    • 이벤트 발생의 주기가 짧다면 지속적으로 서버 클라이언트 사이의 연결과 통신이 발생하게 된다. 즉 이벤트가 자주 발생한다면 polling 방식과 큰 차이가 없다는 것이다. (오히려 성능이 더 떨어질 수 있다)
  • 다수의 사용자에게 동시에 이벤트 발생 시 ?
    • 모든 클라이언트들이 동시에 요청을 보내 서버에 부담 발생

부록 : SSE ( Server-Sents-Events )


실시간 통신을 구현하는 여러가지 방법 중 SSE라는 방법이 있다. SSE를 한마디로 요약하자면 서버에서만 지속적으로 클라이언트에게 데이터를 전송하겠다는 것이다.

  • 앞서 다룬 Polling과 Long Polling의 단점으로는 클라이언트가 요청할때마다 연결이 생성되고 종료된다는 것이다.
    • Connection을 요청마다 만들고 종료하는 것은 지속해서 통신이 발생할 경우 큰 오버헤드로 이어질 수 있다.
  • 실시간 통신을 위해 클라이언트에서 한번의 요청으로 연결을 생성하고 지속적으로 통신할 수 있다면 이러한 문제를 해결할 수 있다. (궁극적인 실시간 통신을 가능하게 한다)

SSE의 경우 한번의 요청으로 Connection을 생성하고 Server에서 event가 발생할 때마다 클라이언트에게 데이터를 전송한다.

단방향 통신

SSE의 가장 큰 특징이다. SSE는 서버 측에서의 단방향 통신만을 지원한다.

image

TEXT_EVENT_STREAM

SSE를 사용할 경우 Media Type은 TEXT_EVENT_STREAM이다.

  • 위의 값을 헤더를 통해 지정해야 한다. 위 값이 의미하는 것은 한번의 요청을 받고 연결을 끊지 않고 지속한다는 것이다.

왜 부록일까?

사실 SSE는 실시간 통신의 방법 중 하나로 설명하였지만 구현하고자 하는 ‘실시간 위치 공유’와는 맞지 않는 방법이다. 그 이유는 가장 큰 특징인 **‘서버에서 단방향 통신’**이다.

  • 실시간 위치 공유를 위해서는 클라이언트가 자신의 위치를 서버 측에 지속적으로 전송해야 한다. 서버는 클라이언트로부터 전송받은 위치를 다른 사용자에게 전송해야 한다.
  • 즉 위의 로직을 SSE로 구현하기 위해서는 아래 2가지 동작이 계속해서 같이 수행되어야 한다는 것이다.
    • SSE 커넥션에서 서버가 데이터를 전송하는 API
    • 클라이언트가 자신의 위치를 서버에 전송하는 API

WebSocket


앞서 살펴본 Polling은 단순히 주기를 통해 실시간으로 데이터를 주고 받는 것처럼 보이게 하는 것이었다. 그렇다면 지속적으로 연결을 유지하여 통신하는 방법은 뭐가 있을까? 바로 WebSocket이다.

  • 클라이언트와 서버가 전이중(Full-Duplex) 채널을 통해 통신

동작 과정

WebSocket을 통한 통신 과정은 크게 연결 - 통신 - 연결 종료 3단계로 구분할 수 있다.

연결

WebSocket 연결 단계에 있어 가장 중요한 점은 HTTP 위에서 동작한다는 것이다. ( 정확히는 HTTP 기반의 2-hand shake 과정을 거친다. )

  1. 클라이언트 Request
  • 클라이언트는 HTTP 기반의 요청 메시지를 전송한다. 메시지를 요약하자면 ‘앞으로의 통신을 WebSocket 프로토콜 위에서 동작하도록 하자’ 라는 것이다.

    GET /chat HTTP/1.1
    Host: server.example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Sec-WebSocket-Version: 13
    
  • Upgrade : websocket : 클라이언트가 WebSocket 연결을 요청

  • Sec-WebSocket-Key : WebSocket 프로토콜을 위한 키로서 랜덤한 값이 사용된다.

  1. 서버 Response
  • 서버는 클라이언트의 요청에 대해 HTTP 응답을 반환

    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
    

Sec-WebSocket-Accept : 클라이언트가 보낸 Key 값을 공개 키로 암호화한 값이다. 해당 메시지를 클라이언트에서 받았을 때 정상적인 값이라면 연결이 제대로 성공했다는 것이다.

통신

서버와 클라이언트 간의 WebSocket 연결이 성공했다면 이제 전이중 통신이 가능하다.

  • 서버와 클라이언트 간에 전송되는 메시지는 일반적인 HTTP 메시지 형식보다 가벼운 구조이다.
  • 텍스트 형식이 아닌 데이터 프레임 형식이다.
  • 웹 소켓의 메시지는 헤더 + 데이터 로 구분된다.
    • 웹 소켓의 데이터는 텍스트 or 바이너리 데이터가 가능하다.

장점

  • 매번 connection을 생성하지 않고 한번의 connection 생성으로 통신이 가능 → 오버헤드가 적다.
  • 클라이언트와 서버 간의 양방향 통신 가능

WebSocket : 완벽한 방법일까?

목표했던 ‘그룹원들 간의 위치 정보를 실시간으로 공유’하는데 있어 WebSocket은 적절한 방법이다. 연결이 지속되고 양방향 통신이 가능하기 때문이다. 하지만 WebSocket에도 아쉬운 부분은 있다.

데이터 형식의 한계

  • WebSocket은 기본적으로 텍스트 or 바이너리 데이터 형식만을 지원한다. 그렇기 때문에 주고 받는 데이터가 간단한 ‘채팅’이라면 효과적인 기능일 수 있다. 하지만 데이터 형식이 조금만 복잡해진다면 데이터를 parsing하는 과정이 매 통신마다 수행되어야 한다.

그룹별 처리

  • 위치가 공유됨에 있어 동일한 그룹 내에서만 데이터가 이동되어야 한다. 즉 그룹 별로 다른 처리가 이루어져야 하는데 WebSocket은 그룹 관리에 있어 복잡하다.
    • 그룹 정보를 별도로 관리해야 한다.
    • 그룹 별 데이터 전송 + 수신 로직을 별도로 구현해야 한다.

STOMP


image

Simple Text Oriented Messaging Protocol

직역하자면 ‘간단한 메시지 전송을 위한 프로토콜‘

  • STOMP는 일반적으로 서브 프로토콜로서 WebSocket 위에서 동작한다.
  • STOMP의 특징
    • 메시지 브로커의 사용
    • pub - sub 방식

메시지 브로커

비동기적 메시지 전송을 위한 중간 미들웨어

  • 메시지 브로커를 사용하는 것은 일반적인 통신 방식과 차이점이 있다.
    • WebSocket을 사용할 경우 Server와 클라이언트 사이에서 서로 직접 메시지를 전송한다.
    • 하지만 중간단에 메시지 브로커를 배치할 경우 메시지 브로커가 각 사용자에게 메시지를 전송한다. (느슨한 결합성)

image

Pub / Sub

발행 구독 패턴으로서 비동기 메시징 패러다임이다.

image

  • 발행자특정 채널로 메시지를 전달하고 해당 채널의 구독자만 메시지를 수신한다.
    • 발행자는 특정 수신자를 대상으로 전송하는 것이 아닌 특정 토픽(채널)을 대상으로 전송한다.
    • 즉 발행자와 수신자는 서로에 대한 정보 없이도 통신이 가능하다.
  • 1 : N 관계에 있어 다중 소비자를 대상으로 메시지 전달이 가능하다.
    • 일반적으로 모든 구독자가 메시지를 수신할 때까지 메시지를 보관해야 한다.

통신 과정

image

  1. subscriber는 특정 토픽에 대해 구독 요청을 보낸다. ( Subscribing )
  2. publisher는 특정 토픽에 메시지를 발행한다. ( Publishing )
  • 이 때 보낸 메시지는 Message Broker에 저장된다.
  • 클라이언트는 자신이 구독한 토픽에 메시지를 전송받는다.

STOMP의 장점

  1. Message Brocker 사용으로 구독 - 발행 패턴 사용을 통해 메시지를 브로드캐스팅 가능
  2. Spring Framework 위에서 적용이 쉽다
  • Spring에서 단순한 WebSocket을 사용할 때에는 WebSocketHandler를 직접 구현하고 적용해야 한다.
  • 하지만 STOMP를 사용할 경우 @Controller를 통해 쉽게 사용할 수 있다.

One More : Message Brocker

STOMP는 기본적으로 내장되어 있는 SimpleBrocker를 지원한다. 하지만 보통 STOMP를 사용할 때 외부 메시지 브로커를 추가해 사용한다.

  • 데이터가 많아진다면, 내장되어있는 SimpleBroker는 철저하게 Spring Boot가 실행되는 곳의 메모리를 잡아먹는다.
  • 이를 해결하기 위해 보통 외부 메시지 큐(RabbitMQ)를 많이 사용한다.

결론 : STOMP 선택의 이유


‘그룹별 실시간 위치 공유’ 기능을 선택하는데 있어 중요하게 고려해야 하는 것은 2가지이다.

  1. 그룹의 멤버들간의 지속적 실시간 통신
  2. 그룹간 통신 관리
  • 1을 고려했을 때 매번 통신을 위해 연결을 생성하고 종료하는 Polling과 Long Polling은 오버헤드가 심하다.
  • 그렇다면 WebSocket과 STOMP가 1을 만족하는 기술인데 이중 pub - sub 구조를 통해 그룹에 대한 구독을 통해 그룹간 통신을 효과적으로 관리할 수 있는 STOMP가 효율적인 방법일 수 있다.