현재 개발자 사이드 프로젝트 팀 매칭을 하고
매칭한 팀끼리 멘토링 받을 수 있는 플랫폼을 제작하고 있습니다
- 개발 배경 -
1차 배포를 하고 지인들에게 내가 만든 서비스를 피드백 받았다. 피드백 결과 기존 방식인 카카오톡 오픈채팅방으로는 팀 매칭이 힘들다는 평가를 받았다.
우리끼리도 어느정도 예상하고 있었던 문제점이기에 채팅 기능을 구현하기로 결정하였다.
- 채팅의 특성 -
1. 채팅은 일반 게시물 보다 쌓이는 데이터 양(Insert)이 많다.
2. 채팅은 Update, Delete 연산을 할 일이 없다.
3. 채팅은 프라이버시와 보안이 중요하다.
4. 채팅은 새로고침(요청)을 하지 않아도 실시간으로 주고 받을수 있어야 한다.
- DB 선정 -
개요
채팅 메세지는 기존의 데이터보다 데이터 양이 많았고, 이를 따로 RDB의 테이블로 관리를 하기엔 매번 Join 해오는 IO 비용이 크다고 판단하였다. 그렇다고 사용하던 DB의 스펙을 높이자니 채팅 데이터 양이 얼마나 들어 올지 예측이 안되었기 때문에 채팅 메세지 적재를 따로 하는게 맞다고 생각이 들었다.
NoSQL 선정 (채팅의 특성 1,2 고려)
결론적으로 NoSQL을 사용하기로 결정 하였는데 그 이유는 다음과 같다.
1. Update, Delete 연산이 적게 발생 하기 때문에 NoSQL은 ACID를 덜 만족하지만, 상대적으로 데이터 정합성이 덜 훼손된다.
2. RDB와 비교하여 높은 읽기/쓰기 성능 (데이터가 많아져도 RDB에 비해 느려지지 않음)
NoSQL 중 온디맨드 DynamoDB 선정 (비용 고려)
NoSQL에도 여러 종류가 있었고 각각의 DB는 본인만의 특성을 가지고 있었다.
그래서 각 DB를 비교 분석 하였다.
Apache Cassandra
장점:
1. DB가 여러대가 한대 처럼 작동하여서 확장성이 좋다.
2. 노드 간 자동 복구와 다중 데이터 센터 지원으로 높은 가용성을 제공.
3. 대용량 데이터일 경우 mongoDB보다 성능 good
단점:
1. 일관성 수준을 설정해야 하며, 강력한 일관성을 요구할 경우 성능 저하가 발생할 수 있다.
2. 클러스터 관리와 운영이 복잡할 수 있다.
3. 쓰기 성능은 뛰어나지만, 읽기 성능이 상대적으로 낮을 수 있다.
MongoDB
장점:
1. 스키마리스 구조를 통해 데이터 모델링이 유연하다.
2. 복잡한 쿼리와 인덱스를 지원하여 다양한 데이터 처리가 가능하다.
3. 수평 확장을 지원하여 대규모 데이터를 효율적으로 처리할 수 있습니다.
단점:
1. 인덱스와 데이터를 메모리에 로드하기 때문에 메모리 사용량이 많다.
2. 기본적으로 eventual consistency 모델을 사용하므로, 강력한 일관성이 요구되는 경우 적합하지 않다.
HBase
장점:
1. 분산 아키텍처를 통해 대규모 데이터를 효율적으로 저장하고 처리할 수 있다.
2. 읽기와 쓰기의 일관성 제공
3. Hadoop 에코시스템과의 통합이 용이하여 대규모 데이터 분석에 유리합니다.
단점:
1. 클러스터 설정과 관리가 복잡하며, 전문 지식이 필요.
2. 성능을 최적화하기 위해 많은 튜닝이 필요할 수 있습니다.
DynamoDB
장점:
1. 인프라 관리가 필요 없으며, 자동으로 확장되고 고가용성을 제공합니다.
2. key-value 형태 이므로 READ 속도가 빠르다. (10ms 이하의 읽기 및 쓰기 성능)
3. 요청 수에 따라 원활하게 확장되기 때문에 비용 효율적이고 IO 작업을 원활하게 지원한다.
4. 다양한 데이터 구조를 지원하며, 스키마가 없는 유연한 데이터 모델을 제공합니다.
단점:
1. 사용량에 따라 비용이 발생하므로, 트래픽이 많은 애플리케이션에서는 비용이 증가할 수 있습니다.
2. 복잡한 쿼리 작업에 제한이 있으며, 특정 사용 사례에서는 불편할 수 있습니다.
3. AWS 환경에 종속적이어서 다른 클라우드 플랫폼으로의 마이그레이션이 어렵습니다.
더 많은 NoSQL DB(Redis 등)가 많았지만 이렇게 4가지가 채팅에 적합해 보였다.
그리고 우리 서비스에 어떤 DB가 잘 맞을지 생각해 보았다.
MongoDB vs casandra
아직 대용량 채팅 데이터가 들어올 만큼 서비스 규모가 크지 않기 때문에 casandra의 장점을 활용하지 못할것이라 판단하여 mongoDB 채택
HBase vs DynamoDB
Hadoop을 사용하지 않을 예정이며, 사용량에 따라 비용이 책정되는 DynamoDB가 초기 트래픽이 적을때는 비용 측면에서 장점이 있을 것이라 판단하여 DynamoDB 채택
MongoDB vs DynamoDB
현재 AWS 환경으로 인프라가 구성되어 있고 초기 트래픽이 적지만 서비스가 어떻게 발전할지 예상이 불가하기에 자동으로 확장되어 트래픽이 튀어도 장애가 발생하지 않을 DynamoDB 채택
결론
DynamoDB를 온디맨드 방식으로 사용하도록 결정 하였으며 이유는 정리하면 아래와 같다.
1. 현재 인프라가 AWS 환경으로 구축이 되어 DynamoDB를 사용 할 경우 AWS의 다른 인프라와 연동이 좋다. (ex. DynamoDB의 데이터를 주기적으로 S3에 백업)
2. DynamoDB 온디맨드 방식은 쓰기/읽기 연산 양에 비례하여 비용이 산정 되기에 트래픽이 적은 서비스 초기의 경우 저렴하게 사용 가능
https://aws.amazon.com/ko/dynamodb/pricing/?refid=fa2d6ba3-df80-4d24-a453-bf30ad163af9
3. 서비스 초기의 경우 트래픽이 적지만 어떻게 증가 할지 예상이 불가능함. 그러므로 요청량에 따라 자동으로 확장되는 DynamoDB를 사용하면 장애를 방지 할수 있음
- 메세지 전송 방식 선정 : 웹 소캣 -
기존에 개발하던 http 방식으로 채팅을 구현한다면 채팅을 한번 보내고 새로고침을 해주어야 한다.
이는 불편하다.
그래서 새로고침을 하지 않고도 메세지를 주고 받을수 있는 방식을 찾아보았다.
폴링, 롱폴링, Streaming, WebSocket 기법들이 있다.
폴링
폴링은 클라이언트가 일정한 시간 간격으로 서버에 새로운 메시지가 있는지 확인하는 기법이다.
채팅 구현 방식에는 부적격하다고 판단했고 그 이유는 다음과 같다.
1. 폴링은 서버에 반복적으로 요청을 보내기 때문에 네트워크 트래픽이 증가하고, 서버에 불필요한 부하를 줌
2. 폴링 간격 동안 새로운 메시지가 도착해도 클라이언트가 이를 확인할 때까지 지연이 발생함
롱폴링
롱폴링은 클라이언트가 서버에 요청을 보내고 서버에서 새로운 데이터가 있을 때까지 연결을 유지하는 기법입니다.
채팅 구현 방식에는 부적격하다고 판단했고 그 이유는 다음과 같다.
1. 연결을 오랫동안 유지하기 때문에 서버의 연결 수가 많아지면 리소스를 많이 소비
2. 많은 양의 메시지가 쏟아질 경우 Polling과 같다.
Streaming
스트리밍은 서버와 클라이언트 간에 지속적인 연결을 통해 데이터를 실시간으로 전송하는 기법입니다.
그나마 채팅 기능에 적합하였지만 아래의 이유로 웹소캣 방식을 채택 하였다.
위 3가지 방법들은 HTTP를 통해 통신하기 때문에 Request, Response 둘다 Header가 불필요하게 크다
웹 소캣 방식을 사용하여 채팅을 구현하기로 하였다.
- 메세지 전송 방식 선정 : STOMP, SockJS -
메세지 브로커 선정 : SimpleBroker
1대1 채팅 뿐만 아니라 단체 채팅의 수요도 고려 했기 때문에 메세지 브로커를 사용하기로 하였고 다양한 메세지 브로커(Kafka, RabitMQ등)가 있었지만 아직 서비스 초기 단계이며 서비스의 메인이 채팅 기능이 아니기 때문에 외부 메세지 브로커를 사용하기엔 오버 엔지니어링이라고 판단했다.
그래서 Spring 내부에서 제공하는 SimpleBroker를 사용하였다.
메세지 브로커를 사용하기 위해선 pub-sub 구조를 알아야 하는데 한국어로 해석하면 발행-구독 구조이다.
쉽게 말해서 문자를 보낸 사람이 문자를 "발행"하면 메세지 브로커가 발행자에게 문자를 중간에서 받아서 구독한 사람에게 뿌려주는 방식으로 동작하는 것이다.
아래 구조를 보면 SimpleBroker가 메세지 브로커 역할을 하고 있는데 구독한 사람들에게 메세지를 뿌린다.
STOMP 사용
그리고 웹 소캣 위에 STOMP(Simple Text Oriented Message Protocol) 을 사용하기로 하였다.
STOMP는 클라이언트와 메시지 브로커 간의 메시지 전송을 표준화하는 텍스트 기반 프로토콜으로 이를 사용하면 메시지의 형식을 정의할 수 있어, 클라이언트와 서버 간의 통신에서 일관성을 유지할수 있다.
쉽게 말해 웹소캣 위에서 사용하는 데이터의 형식 같은 것이다.
CONNECT 프레임
클라이언트가 메시지 브로커에 연결할 때 사용하는 프레임
CONNECT
accept-version:1.2
host:stomp.github.org
login:guest
passcode:guest
^@
SUBSCRIBE 프레임
클라이언트가 특정 목적지(예: 채널, 큐)를 구독할 때 사용하는 프레임
SUBSCRIBE
id:sub-0
destination:/topic/chat
^@
SEND 프레임
클라이언트가 메시지를 전송할 때 사용하는 프레임
SEND
destination:/topic/chat
Hello, World!
^@
MESSAGE 프레임
메시지 브로커가 클라이언트에게 메시지를 보낼 때 사용하는 프레임
MESSAGE
subscription:sub-0
message-id:007
destination:/topic/chat
Hello, World!
^@
DISCONNECT 프레임
클라이언트가 연결을 종료할 때 사용하는 프레임
MESSAGE
subscription:sub-0
message-id:007
destination:/topic/chat
Hello, World!
^@
추가로 웹 소캣은 http5 이후에 나온 기술이라
옛날 구버전의 브라우저를 사용하는 유저는 대응 할 수가 없었다.
그래서 이러한 문제점을 해결해주는 socketJS를 사용하기로 하였다.
socketJS는 구버전의 브라우저에서는 웹소캣 방식이 아닌 폴링, streaming과 같은 방식으로
마치 웹소캣처럼 보이도록 동작하게 된다.
'기술적 고민 > Side Match' 카테고리의 다른 글
[SW마에스트로] 프로젝트의 로그 관리하기 (Sentry) (0) | 2023.11.12 |
---|---|
[SW마에스트로] DB가 2개일 경우 생기는 오류 처리 (0) | 2023.10.24 |
[SW마에스트로] Base64로 인코딩된 사진 받아서 S3에 업로드하기 (0) | 2023.10.07 |
[SW마에스트로] 팀원-팀원 상호 평가 API를 만들면서 하는 고민 (0) | 2023.09.12 |
[SW마에스트로] 프로젝트 중 설계한 예외 처리 아키텍쳐 (0) | 2023.08.29 |