구글에 '스프링 채팅 기능'을 검색하면 웹소켓을 활용해 스프링 채팅 기능을 구현했다는 글이 수십페이지가 검색된다. 이 사람들은 왜 기본적인 통신 방법인 HTTP를 사용하지 않고 웹소켓이라는 기술을 선택해서 채팅 기능을 구현했을까. 웹소켓을 사용하는 것이 어떤 이점이 있을까. 이를 알아보면 웹소켓을 사용해서 채팅 기능을 구현하는 당위성이 생길 것이다.
TCP VS UDP
웹소켓이고 HTTP고 알아보기 전에 TCP와 UDP를 먼저 알아보자. 그래도 기본적인 통신 프로토콜은 알고 진행하는게 낫지 않겠는가?
TCP, UDP는 모두 데이터 송신을 위한 통신 프로토콜이다. 둘의 가장 큰 차이는 우리가 통신하고 있다는 걸 알고있냐, 모르냐의 차이라고 할 수 있다. 위의 표에서 확인할 수 있듯이, TCP는 연결형 서비스이며 UDP는 비연결형 서비스이다. 즉, TCP는 우리가 통신한다는 사실을 알고 있는(Stateful) 프로토콜이고, UDP는 우리가 통신한다는 사실을 모르는(Stateless) 프로토콜임을 알 수 있다.
그런데 통신하는 지를 모르면 어떻게 통신할까? 그냥 서로 요청을 보내고, 이에 응답을 보내고 만다. 상대방이 내 요청을 받았는지는 모른다. 그렇다. UDP는 그냥 데이터를 던지고, 그 다음은 모른다. 진짜 모른다. 보내는 데이터 크기가 커서 이를 여러 패킷으로 나눠 잘라서 보낸다고 해보자. UDP 방식에서는 그 중에 패킷 loss가 발생해도 모른다. 진짜 모른다. 즉 신뢰성이 낮다. 당연히 수신이 됐는지도 확인하지 않는다. 그냥 답변을 여기로 보내주세요~ 하는 정도의 정보를 담고 서버에 요청을 보낼 뿐이다.
반면 TCP는 서로가 서로를 알고있다. 내가 클라이언트가 서버에 어떤 요청을 보내면, 핸드쉐이킹이라는 과정을 통해서 서로 연결이 잘 됐는지를 먼저 확인한다. 그 다음 정보를 데이터를 주고받는다. 데이터를 주고 받을 때도 데이터를 받았다는 ACK 시그널을 서로 보내기 때문에, 패킷의 순서가 뒤바뀌거나 패킷이 손실되었을 때 다시 데이터를 보내 신뢰성을 높인다. 위에서 언급한 수신 여부 확인이 ACK이다.
이렇게 보고 나니, 뭔가 HTTP는 UDP와 비슷해보이고 웹소켓은 TCP와 비슷해보이지 않는가? 요청을 한 번 보내면, 그에 해당하는 응답을 한 번 받는 HTTP와 채팅이라는 서로 연결되어이었야하는 기능을 구현하기 위해 사용하는 웹소켓은 차이가 있어보인다. 그러나 아쉽게도 HTTP/2 까지는 TCP 기반의 프로토콜을 유지하고 있다. 어쨋든 요청 - 응답 간에 데이터 손실이 있으면 안되니깐 말이다. HTTP/2기반으로 만들어진 HTTPS도 당연히 TCP기반의 프로토콜이다. HTTP/3부터는 UDP기반(QUIC)의 프로토콜로 전환된다고 한다.
HTTP 기반의 통신
그래서, HTTP는 어떻게 통신할까? HTTP에는 어떤 단점이 있기 때문에 실시간성을 보장해야하는 채팅에서 사용하지 않는 것일까?
HTTP는 기본적으로 단방향 통신이다. 클라이언트가 일방적으로 서버에 먼저 요청을 보내고, 서버는 이에 해당하는 응답을 보내는 형태의 통신 프로토콜이다. 뭐 여기까지는 오케이다. 채팅을 해도 요청을 보내면 그에 대한 응답을 답변하면 되니까. 문제는 HTTP는 한 번의 요청 - 응답 세션이 끝나면 그대로 TCP연결을 종료해버린다는 것이다. 한 번의 요청이 끝나면, 다음 요청을 보낼 때 또 핸드쉐이킹하고, 나는 누군데 너는 누구니를 계속 반복해야한다. 여기까지도 뭐 하드웨어적인 발전으로 어떻게 처리한다고 해보자. 더 큰 문제는 일단 연결이 끊어졌기 때문에, 상대가 뭘 보내도 알 수가 없다는 것이다. 예를 들어, 당근에서 서로 대화를 하는데 상대가 채팅을 보냈는지 실시간으로 알 수 없다고 해보자. 나는 채팅을 확인하려면 계속 새로고침을 해야한다. 사실상 채팅이 아니라 그냥 이메일이라고 보는 것이 옳은 수준이 되버린다.
그러면 HTTP기반에서는 실시간 채팅이 아예 불가능할까? 그건 또 아니다. 우리는 HTTP 기반으로도 네이트온이나 버디버디같은 추억의 메신저를 잘만 사용했다. 새로고침을 누르거나, 채팅방을 새로 들어가지 않고도 말이다. 이건 어떻게 가능했을까?
- Polling / Long Polling
HTTP를 사용하는 개발자들도 손 놓고 있지는 않는다. 먼저 Polling이라는 기법을 이용해 실시간 통신을 흉내냈다. Polling은 요청을 특정 시간 단위로 무한히 보내는 방식이다. 같은 요청을 특정 시간 단위로 계속 보내니 통신이 계속 이어져있는 것처럼 보이고 실시간으로 응답을 받는 것처럼 보인다. 그러나 실제로는 서버의 미친 노동량을 기반으로 하는 행위이며, 당연히 요청을 한 번 보낼 때마다 필요한 엄청나게 긴 요청문을 보내야하고 핸드 쉐이킹 과정도 거쳐야하며 서버에서 같은 응답을 계속 반복하니 그만큼 자원이 낭비된다. 또한 실시간이라고 보기도 어려운게 요청을 보낼 때마다 응답을 받는 형태이기 때문에 타이밍이 안맞으면 그만큼 시간차가 발생한다.
Polling 방식을 개선한 것이 Long Polling 방식인데, Polling이 일정 시간마다 계속 요청을 보낸다면 Long Polling은 연결이 끊어질 때 다시 요청을 보내는 방식이다. 그러니 당연히 Polling보다는 서버에 부담이 적게 간다. 요청이 적으니까. 그러나 여전히 고질적인 문제인 무한히 보내는 요청이라는 문제를 해결하지는 못한다. 좀 더 자세한 내용은 링크를 참조해보자.
- Streaming
HTTP Streaming 기법은 애플에서 처음 고안한 프로토콜이다. HTTP요청이 들어오면 이에 해당하는 데이터를 잘게 쪼개서 끝까지 보내주는 방식인데, 양방향 통신이 되는 것 같지만 서버 -> 클라이언트로의 단방향 통신으로 통신 형태가 뒤집힐 뿐이다.
기본적으로 큰 크기의 데이터를 요청하면 해당 데이터를 잘게 나눠 계속 보내주는 방식이다보니 다음 요청을 보내기 어렵고, 앞서 언급한 지나치게 큰 헤더가 계속 넘어간다는 문제를 해결하지 못한다. 또한 큰 데이터를 잘게 나눠 계속 보내준다는 점에서 유추할 수 있듯, 이 방식은 말 그대로 스트리밍 방식에만 국한되어있다. 비디오나 음악같은 끊이지 않는 것들 말이다. 실시간 채팅에서는 쌍방간 요청 - 응답이 계속 이루어져야하는데 Streaming 방식으로는 이를 해결하기 어렵다.좀 더 자세한 내용은 링크를 참조하자.
웹소켓 기반의 통신
이런 문제들을 해결하기 위해 웹소켓 기반의 실시간 양방향 통신을 할 수 있다. 일단은 HTTP요청을 한 번은 보내야한다. 어쨋든 웹소켓을 사용하기 위해서는 클라이언트 - 서버 모두에서 웹 소켓 방식을 지원해야하고, 기본적인 요청은 HTTP로 이루어지기 때문에, 웹소켓이라는 특이한 프로토콜을 사용하겠다는 요청을 보내는 것이다.
웹소켓을 사용하기 위한 핸드 쉐이킹 과정에는 HTTP Upgrade 헤더를 사용한다. 프로토콜을 웹 소켓으로 업그레이드 할 것을 요청하는 것이다. 서버에서 이러한 요청을 받고 성공적으로 웹소켓으로 프로토콜을 업그레이드 하면, HTTP 프로토콜은 시간이 지나면서 닫히고 양방향 통신이 가능한 웹소켓만 남는다.
웹소켓 통신은 프레임이라는 단위로 구성되어있는데, 프레임 구조에 관한 자세한 설명은 해당 영상을 참조하면 좋을 것 같다. 웹소켓이 TCP 기반으로 구성된 프로토콜이기 때문에, 연결을 끊겠다는 요청이 들어오지 않는 이상 서버 - 클라이언트 간의 양방향 통신은 계속해서 이어진다. 그러나 그 과정에서 불필요한 요청이나 불필요한 응답이 없기 때문에 HTTP를 사용한 실시간 통신이 지닌 문제를 해결할 수 있다. 연결을 끊자는 프레임을 클라이언트가 요청 보내면 그 때 연결을 끊으면 된다.
그래서 웹소켓은 신인가? 당연히 아니다. 일단 클라이언트와의 연결을 계속해서 유지해야 하기 때문에 그만큼 서버에 부하가 걸린다. 또한 정직한 종료 요청이 아닌 다른 요청으로 인해 연결이 끊어졌을 때 발생하는 예외 상황을 처리해주어야 한다. 마지막으로 웹소켓은 HTML5 부터 사용이 가능하기 때문에 이전 기술로 구현된 브라우저나 서비스에서는 웹소켓을 사용할 수 없다. 그러나 이러한 단점들을 상쇄시켜줄 수 있는 기술들도 당연히 존재하며, 오늘은 웹소켓을 왜 쓰는지만 알아보고, 이는 추후 다시 다뤄보자.
이러한 단점들을 고려하더라도, HTTP기반의 통신보다 웹소켓을 사용하는 것이 낫다. 예외 상황은 개발자가 알아서 처리하면 되고, 최근 사용되는 브라우저는 거의 모두 HTML5 이상의 기술을 사용하기 때문이다. 그나마 걸리는 점은 서버에 걸리는 부하인데, 계속해서 요청이 들어오는 상황과 비교하면 이게 낫다.
이러한 이유로 웹소켓을 사용한 실시간 채팅 기능을 구현하는 서비스들이 많으며, 마찬가지로 실시간성이 중요한 게임이나 주가변동 같은 부분에도 웹소켓을 사용한다고 한다. 이번에 프로젝트를 진행하면서 나도 마찬가지로 웹소켓을 사용한 실시간 채팅 기능을 구현해 볼 예정이다. 왜 쓰는지는 알고 써야하니까 알아봤는데, 꽤 재밌는 시간이었다. 간만에 네트워크 관련한 내용도 좀 뒤져보고, 기능 구현에 기본을 더해주는 시간이 아니었나싶다.
'Programming > Study' 카테고리의 다른 글
일대다 단방향 연관관계를 피하고 싶어서 (0) | 2024.02.21 |
---|---|
DB Entity -> Domain Entity로의 전환은 어느 계층에서 담당하는 것이 옳을까 (2) | 2024.02.14 |
JWT 이대로 괜찮은가? (0) | 2024.01.02 |
계층별로 분리해서 DTO를 사용하기로 한 이유와 결과 (0) | 2023.12.13 |
헥사고날 아키텍처 얕게 공부한 뒤 느낀점 - 기본이나 잘하라 (1) | 2023.12.11 |
댓글