가자미의 파닥파닥 프로그래밍
스프링 웹 채팅 서버 구현 본문
현제 진행중인 사이드 프로젝트 일명 Fitingle를 진행하던 중 채팅 기능을 개발하고 있습니다. 이 기능을 개발하던 중에 만난 이슈 사항들을 정리하고, 알게된 지식들을 포스팅 해보려고 합니다.
기능 설명
Fitingle 프로젝트는 운동 커뮤니티 사이트입니다. 간략하게 소개하면, 같이 운동하고 싶은 사람들을 이어주고 서로 정보를 공유하며 그룹을 만들어 같이 운동을 할 수 있도록하는 일종의 커뮤니티 입니다. 이 포스팅에서 설명하려고 하는 기능은 채팅을 통하여 서로 정보를 공유하고, 서로 대화를 나눌 수 있는 채팅 기능입니다.
이 채팅 기능의 세부 내용은 아래와 같습니다.
- 채팅방 입장
- 채팅방 나가기
- 메시지 보내기
- 이전 대화내용 불러오기
- 채팅하기
웹 소켓 통신
- 웹 소켓 통신은 웹 어플리케이션을 위한 양방향 통신 기법으로 실시간성을 보장할 수 있는 방식
- 실시간을 보장하는 서비스에 적합하다( ex 게임, 채팅, 실시간 주석 차트, 가상 화폐 거래소)
- HTTP와의 차이점
- HTTP는 단방향 통신(요청이 오면 응답을 한다.), 웹 소켓은 양방향 통신(서로 요청이 가능하다.)
- HTTP는 비연결 지향성 프로토콜, 웹 소켓은 연결 지향성 프로토콜
세션과 HTTP 그리고 소켓
- 세션은 논리적으로 연결된 상태를 말한다. 더 자세히 말하자면, 서로 물리적으로 연결 되어있고(PC에 인터넷 회선이 연결) 상대가 누구인지 알기 시작할 때, 세션이 시작되었다고 한다.
- HTTP는 단방향 통신이다. 그러면 어떻게 세션을 유지?
- 이 세션 유지는 우리 프로젝트를 예로들면 JWT가 해준다.
- JWT에 유저에 대한 정보를 담아서 서로 주고 받으면서 상대가 누구인지를 안다.
- 서버는 유저 정보를 받아서 DB에서 찾아서 맞는 사용자인지 확인한다.
- 웹 소켓 통신은 어떻게?
- 웹 소켓 통신의 세션은 HTTP처럼 사용하지 않고 소켓 자체의 세션 아이디를 가지고 세션을 유지시킨다.
- 우리가 개발을 할때 사용하는 소켓 라이브러리는 운영체제가 관리하는 소켓을 컨트롤 하는 것이다.
- 소켓라이브러리를 사용하여 소켓을 설정해 주면 해당 프로그램에는 소켓의 ID가 할당 된다.
- 서로 소켓을 사용하여 통신할때, 보내는 데이터에 소켓 ID를 넣어서 보내고 서버는 이 소켓 ID를 세션으로 유지 한다.
웹 소켓을 이용한 채팅
- 위에서 말했다 싶이 웹 소켓을 이용하면 채팅을 구현할 수 있다.
- 클라이언트가 서버에 통신 연결을 시도한다.
- 서버는 클라이언트에서 연결 요청을 확인하고, 소켓ID를 세션으로 사용하여 저장해둔다.
- 클라이언트가 메시지를 보내면 서버는 저장해둔 세션에 모두 메시지를 보낸다.
- 하지만 웹 소켓만을 이용하면 우리가 생각하는 채팅을 만들기에는 한계가 존재한다.
- 우리가 메시지의 형식을 다 정의 해줘야한다. 즉, 메시지의 구조를 정의해야한다는 것이다.
- 에러 메시지의 작성, 에러이면 어떤 에러인지, 예외가 무엇인지, 메시지 전송에 실패 했을 때, 원인이 무엇인지 등등 여러가지 상항에 모두 개발이 되어야한다.
- 다중 채팅방 구조에는 알맞지 않다.
- 채팅방 하나에서 나와 여러명이 채팅하는 구조이면 웹 소켓만을 이용하여 구현이 가능하다. 하지만 N개의 채팅방에서 M명씩 있다고 가정한다면?
- 서버는 채팅방을 각각 만들고, 각 채팅방 마다 세션을 만들어서 유지시켜야 한다. 그렇기 때문에 NxM만큼의 컴퓨팅 자원이 소모되어 서버의 성능이 저하 될 수 있다.
- 서버뿐만 아니라 프론트 입장에서도 난감해진다. NxM만큼의 데이터를 처리해야하는데 무조건 지연이 생길 수 밖에 없다.
- 지연이 생긴다는 것은 실시간 처리가 안된다는 것이다. 이것을 해결하기 위한 방법으로 한가지가 있을 수 있다. → 메시지를 받아 저장하고 있는 큐와 저장하고 있는 큐에서 하나씩 꺼내어 처리하는 큐를 따로 두는 것이다.
- 하지만 위의 방법은 프론트에서 처리 해야할 것이 많아진다. (ex. 각 큐의 관리, 스레드의 처리 등등)
- 우리가 메시지의 형식을 다 정의 해줘야한다. 즉, 메시지의 구조를 정의해야한다는 것이다.
STOMP
- 위에서 말한 다중 채팅에서 나타나는 문제들을 해결할 수 있는 하나의 프로토콜이다.
- STOMP는 Simple Text Oriented Message Protocol의 약자로, 메시지 전송을 효율적으로 하기 위한 프로토콜
COMMAND
header1:value1
header2:value2
Body^@
- 위는 STOMP 프로토콜의 형식으로 우리가 아까 고민했던, 메시지 형식을 만들 필요가 없다.
- 커멘드, 헤더, 바디의 형태로 메시지가 오간다.
https://velog.io/@msung99/웹소켓과-STOMP를-통한-실시간-통신-이해하기 참조 이미지
- 우리는 어떤 새로운 프로토콜을 사용할 때는 무조건 어떤식의 흐름이 발생하는지 이해해야한다. STOMP 프로토콜의 흐름은 아래와 같다.
- 먼저, STOMP 프로토콜에는 발신자와 구독자가 존재한다. 이를 publisher, subscriber라고 한다.
- subsciber는 /topic이라는 경로를 구독하는 구독자이다.
- 발신자는 두 가지 방법으로 /topic을 구독하고 있는 subsciber에게 데이터를 전달 할 수 있다.
- 어떤 데이터의 가공이 필요 없이 바로 subsciber에 전송할 때는 /topic이라는 헤더를 detination헤더로 넣어서 브로커에 전달하면 메시지를 송신할 수 잇다.
- 데이터의 가공이 필요할 경우 /app이라는 주소로 메시지를 보내면 서버에 메시지가 전달되고, 서버에서 데이터를 가공하고 난 후에 브로커에게 다시 /topic를 구독하고 있는 구독자에게 전송 할 수 있다.
브로커와 메시지 큐
브로커
- 사실상 STOMP 구조에서 메시지를 전달하는 모든 기능은 브로커를 통해 이뤄진다.
- 정확히 브로커는 Message 브로커라고 불린다.
- 브로커는 말 그대로 중간 상인 역할을 하는 것과 같다. publicsher와 subsciber의 중간에서 메시지를 주고 받게 해주는 역할을 해주는 어떤 프로그램이다.(Redis 같은 놈임)
- 그럼 이 브로커는 어떻게 메시지들을 처리 하는 것일까?
- 여기에 위에서 채팅을 구현할 때 실시간 처리를 위해 큐를 사용해야한 다는 것이 들어간다. 내가 생각한 모든 기능을 브로커가 대신해주는 것이다.
- 송신자는 메시지 브로커에게 메시지를 전송하고, 큐에 잘 들어 갔는지 확인만 한다.
- 브로커는 수신자에게 알림을 보내고, 알림이 도착했을 때 수신자는 폴링을 한다.
- 브로커의 핵심 기능
- 비동기 커뮤니케이션
- 시스템이 비동기로 소통할 수 있도록 돕는다.
- 송신자와 수신자가 서로의 응답 메시지를 기다릴 필요 없이 서로 독립적으로 운영될 수 있도록 한다.
- 디커플링
- 브로커는 어플리케이션과 독립된 컴포넌트이다. 그래서 어플리케이션이 바뀌더라도 브로커는 바뀌지 않아도 된다.
- 메시지 라우팅 및 필터링
- 라우팅 룰, 패턴, 메시지 프로퍼티 등을 제공하여 적절하게 메시지를 라우팅 할 수 있다.
- 영속성과 신뢰성
- 시스템 장애 시에도 메시지 브로커는 자신이 받았던 메시지를 보관한다.
- 다양한 Qos(Quality of Service)를 제공하여, 적어도, 한번, 정확히 한번등 옵션을 제공한다.
- 스케일링
- 메시지의 크기를 조절하여 많은 양의 메시지 부하를 처리할 수 있다.
- 로드밸런싱과 장애 내성
- 소비자들에게 균일하게 메시지를 나눠 주어 부하의 균형을 유지한다.
- 소비자가 메시지를 받는 것에 실패한 경우에 인지가 가능하고, 다른 대체 소비자에게 메시지를 보내는 것도 가능하다.
- 비동기 커뮤니케이션
메시지 큐
- 사실 메시지큐에 대한 설명은 이미 끝났다.
- 메시지 큐 == 브로커 이기 때문이다.
- 우리가 집고 넘어가야할 것은 이 브로커를 스프링에서 기본적으로 제공하는 브로커를 사용하면 In Memory 브로커를 사용한다는 것이다.
- 즉, 서버 어플리케이션의 자원을 잡아 먹는 다는 것이다. 이는 서버의 성능의 부하를 줄 수가 있다.
- 그래서 외부 브로커를 사용한다. 여기서 외부라는 말은 서버 PC 외부에 있는 어떤것이 아니라, 서버 어플리케이션 외부 프로그램을 말한다.
- 대표적으로 아래와 같은 것들이 있다.
- RabbitMQ - 오픈 소스 라이브러리고 많이 쓰임 나머지는 잘 모르겟음…
- Apache Kafka
- Amazone Simple Queue Service
- Google Cloud Pub/Sub
Reference
https://zamezzz.tistory.com/319
https://yoo-dev.tistory.com/51
https://velog.io/@msung99/웹소켓과-STOMP를-통한-실시간-통신-이해하기
https://withseungryu.tistory.com/137
https://etloveguitar.tistory.com/61
https://jake-seo-dev.tistory.com/467
'Spring' 카테고리의 다른 글
MVC 패턴 (0) | 2024.01.11 |
---|---|
AWS EC2에 RabbitMQ 설치 (0) | 2024.01.08 |
Fittingle 인가 처리 수정 (0) | 2023.12.27 |
서블릿과 JSP (0) | 2023.12.22 |
스프링에 대해서... (0) | 2023.11.27 |