서비스 아키텍쳐

2022. 12. 15. 16:40프로젝트/라이어게임

728x90
SMALL

TV에서만 보던 라이어게임을 온라인으로 화상 및 채팅을 통해 구현해보고자 했다.

 

 

# 전체적인 아키텍쳐

 

나는 BackEnd 를 담당하고 있기 때문에 FrontEnd 아키텍쳐에 대한 설명은 생략하도록 한다.

 

# WebRTC

화상/보이스 채팅 기능을 위해 카메라와 마이크 등의 미디어 자원을 통해 실시간으로 소통할 수 있는 기술이 필요했다.

WebRTC는 카메라, 마이크와 같은 미디어 자원들을 활용해 실시간 커뮤니케이션을 할 수 있는 기술이다.

화상통화, 화상공유 등을 구현할 수 있는 오픈소스이며, p2p 방식으로 피어간의 통신을 지원한다.

 

[장점]

  1. Latency(지연시간)가 짧다. 
    인스타라이브, 유튜브라이브, 트위치와 같은 스트리밍 서비스는 대부분 RTMP를 사용하여 실시간으로 스트리밍 한다고 한다.
    WebRTC의 latency는 RTMP보다 낮기 때문에 거의 지연시간이 없는 Real_time 으로 스트리밍 방송을 할 수 있다고 한다.

  2. 별다른 소프트웨어 없이 실시간 소통이 가능하다.
    웹이나 앱으로 스트리밍 서비스를 구현하고 싶을 때, 별도의 플러그인이나 소프트웨어를 설치할 필요가 없다.

  3. 개발 진입장벽이 낮다.
    오픈소스이기때문에 비교적 진입장벽이 낮다.
    (하지만, 애초에 처음 사용하는 기술이라면 진입장벽이 낮다고 하더라도 적응하고 구현하기 힘들 수 있다.)

[단점]

  1. 크로스 브라우징 이슈 (Cross browsing)
    WebRTC는 우리가 흔히 사용하는 Chrome, Firefox와 같은 인터넷 explore 환경 뿐만 아니라 안드로이드, IOS 와 같은 앱 환경에서도 활용할 수 있다.
    하지만 생소한 브라우저나 최신 버전의 환경이 아니라면 유저가 사용이 불가능하다는 단점이 있다.
    (예: Internet Explorer)

  2. STUN / TURN 서버 필요
    p2p (Peer to Peer) 통신을 하기 위해서는 사용자의 IP주소를 알아야하는데, 대부분의 유저들은 방화벽을 사용하기 떄문에 IP 주소를 알아낸다고 하더라도 정상적인 통신이 안될수 있다.
    WebRTC는 이처럼 p2p 통신 방식을 취하고 있는 NAT 환경에서 동작된다.
    NAT 환경과 방화벽 이슈를 극복하고 WebRTC 가 통신하려면 ICE 와 STUN / TURN 서버를 사용할 수 밖에 없다.
    즉, WebRTC를 사용하기 위해서는 부가적인 서버나 자원을 추가로 사용해야한다는 것이다.

    # NAT
    Private IP 를 Public IP 로 1대1 대응시켜 변환하는 장치

    # ICE
    통신하고자 하는 두 단말이 연결될 수 있는 최적의 경로를 찾을 수 있도록 도와주는 프레임워크.
    모든 클라이언트가 사용하는 단말의 각자의 환경에 따라 매우 다양하다. 
    따라서, 단순히 두 단말을 바로 연결하는 것이 아니라 방화벽이 있는 환경에서는 방화벽을 고려해야하고, Public IP가 없다면 주소값을 할당해야하며, Peer 같의 직접 연결을 허용하지 않을 때에는 데이터를 릴레이해야한다.
    ICE 프로세스는 NAT가 통신을 위해 필요한 모든 포트를 열어두고 두 단말간의 엔드 포인트 모두 다 연결할 수 있는 IP주소, 포트에 대한 완전한 정보를 갖게된다.
    결국, 요청하는 클라이언트와 미디어 서버 사이의 연결을 통해 미디어 데이터를 주고 받을 수 있는 것이다.

    # STUN
    해당 Peer 의 Public IP 주소를 보내는 역할을 한다.

    # TURN
    인터넷 망에 위치하여 각 Peer 들이 Private IP 안에서 통신하게 한다.
    각 Peer들이 직접 통신하는 것이 아니라 릴레이 역할을 하는 TURN 서버를 사용하여 경유한다.
    TURN 서버는 이러한 릴레이로부터 IP주소와 포트를 클라이언트가 취득할 수 있는 릴레이 주소를 할당한다.

따라서, WebRTC는 브라우저에서 지원하는 프레임워크로 많이 사용되어 안정성이 검증되어있고, 별도의 플러그인 없이 사용이 가능하여 유저 경험에 유리할 것으로 판단되어 채택하였다.

 

참조) https://gh402.tistory.com/38

 

# OpenVidu

Peer to Peer 기반의 WebRTC로 N:M 화상 채팅을 구현하기 위한 방법은 여러가지가 있는데 그 중 클라이언트와 서버의 부하가 비교적 적고 안정적인 SFU 방식의 구현을 위해서는 미디어 서버가 필요하다.

WebRTC를 직접 구현하여 운영하는 방법이 있다고 하지만 처음 사용하는 기술인 데다가 구현해야하는 시간이 제한적이었기 때문에 관련 라이브러리를 사용하는 것이 효율적이겠다고 판단되었다.

 

Openvidu는 웹과 모바일 환경에서 영상 통화 기능을 쉽게 추가할 수 있도록 하는 오픈소스 플랫폼이다. 애플리케이션에 매우 간편하게 적용할 수 있는 다양한 데모 코드와 기술 스택을 제공하고 있다. WebRTC 미디어 서버를 구현하는 데 소모되는 리소스를 절약해서 빠르게 실시간 통신을 추가할 수 있도록 도와주는 것이 큰 장점이다.

대부분의 코드를 정리된 소스 코드로 제공해주고 있고 화상 채팅 뿐 아니라 메시징, 화면 공유 등 다양한 기능을 지원하여 확장성과 접근성이 좋을 것으로 판단하여 선택하였다.

 

Openvidu는 크게 두 파트로 나눌 수 있다.

  • Openvidu Browser (Front-End)
    • 클라이언트 측에서 사용할 라이브러리
    • 화상 통화를 만들고, 비디오와 오디오를 주고받을 수 있게끔 도와준다
    • Openvidu에서 사용할 수 있는 모든 작업은 Openvidu 브라우저를 통해 관리된다
  • Openvidu Server (Back-End)
    • 서버 측 내용을 처리하는 애플리케이션
    • Openvidu 브라우저에서 작업을 수신하고, 비디오 통화를 설정하고, 관리하는 데 필요한 모든 작업을 수행한다
    • 명시적으로 구현할 필요는 없고, 실행한 뒤에 접근할 수 있는 IP 주소만 알고 있으면 끝

우선적으로 Server 단에서 Openvidu를 구축하고 도메인을 통해 AWS EC2를 활용하여 구축된 Openvidu를 배포하게되면, 배포된 Openvidu를 Browser(Front) 단에서 커스텀하여 사용하는 방식으로 구현하였다.

 

처음 WebRTC를 구현한다는 점과 해당 기술을 spring으로 구현해야하는 상황속에서 자료를 찾기가 매우매우 힘들었고, 있다고 한들 node.js로 구현한 자료들이 대부분이었다.

Openvidu 라는 라이브러리가 존재한다는 것을 알게된 것도 프로젝트를 진행한지 4일정도 되서야 알게되었고, 그제서야 구축하는 데에 있어서 진도는 있었지만 난생 처음 사용해보는 docker와 linux 언어를 사용해야해서 많이 헤매었고, nginx 와 같은 인증서를 발급받는 부분에 있어서도 생소하여 구축하는 데에 시간이 많이 걸리고 어려웠었던 것 같다.

 

 

# Kurento server

WebRTC를 사용하기 위해 필요한 미디어 서버이다.

앞서 WebRTC는 p2p 방식의 NAT 통신 환경에서 통신을 한다고 하였는데, p2p 방식의 통신같은 경우 대규모 스트리밍,

다대다 형식의 스트리밍 서비스를 구현하기에는 무리가 있다.

예를 들어 유튜브 라이브가 p2p 방식이였다면, 다수의 시청자가 진입할 경우 클라이언트가 터져버릴 것이다.

따라서, p2p 보다는 SFU 방식으로 구현할 필요가 있는데, 이를 위해 미디어 서버가 필요한 것이다.

# 이미지 참조 : https://millo-l.github.io/WebRTC-%EA%B5%AC%ED%98%84-%EB%B0%A9%EC%8B%9D-Mesh-SFU-MCU/

 

Mesh 방식은 우리가 알고있는 기본적인 WebRTC 의 p2p 방식으로, 클라이언트간의 직접적인 연결을 통해 실시간성을 보장하지만, 다대다의 연결의 경우, 부하가 많이 걸리기때문에 부적합하다.

MCU 방식은 SFU 방식과 비슷하게 중간에 미디어 서버를 두고 서버에게 자신의 미디어 데이터를 보내면 되지만, 미디어 데이터를 결합하는 과정에서 비용이 많이 든다고 한다.

SFU 방식은 중간에 미디어 서버를 두고 서버에게 자신의 미디어 데이터를 보내기만 하면 되고, 클라이언트가 받는 부하가 줄어든다. 이-번 프로젝트는 최대 8인이 플레이하는 게임이기 때문에 소규모 다대다 구조에 적합한 SFU 방식을 지원하는 kurento 미디어 서버를 채택하였다.

 

Openvidu가 이 Kurento 미디어 서버를 기반의 프레임워크인 점도 kurento 서버를 채택한 큰 이유 중 하나이다.

 

 

# WebSocket (Socket.io)

게임과 채팅 구현을 위해 실시간성을 보장하는 통신 방식이 필수였다.

HTTP에서도 실시간성을 보장하는 반이중 기법들(polling, long polling, streaming)이 존재한다.

polling 방식은 지속적으로 데이터를 요청해야만 통신이 가능하기에 게임과 같은 실시간 통신에는 부적합하다.

또한, 서버 부하가 올라갈 수 있다는 단점이 있어서, 하나의 TCP 연결을 통해 양방향 통신을 제공하는 Websocket을 선택했다.

 

 

# Stomp

Websocket 위에서 동작하는 문자 기반 메세징 프로토콜로써 클라이언트와 서버가 전송할 메세지의 유형, 형식, 내용들을 정의하는 메세지 브로커이다.

Websocket과 동일하게 양방향 통신을 지원한다.

pub/sub 구조의 Websocket 기반 프로토콜로, 구독 주소인 Topic을 생성하게 되면 해당 Topic을 구독한 subscriber 들에게 

publisher 들이 Topic에 메세지를 전달하게 되면 실시간으로 구독자들에게 해당 메세지를 전달하거나 받을 수가 있다.

최대 8명이 동시에 참여하는 실시간성 게임이므로 stomp가 가장 적합하다 생각되어 채택하였다.

 

 

# SockJS

SockJS는 WebSocket 연결 실패 시 HTTP의 반이중 기법들을 사용할 수 있게 한다. 다양한 환경에서 안정적으로 실시간 통신을 가능하게 하기 위해 사용했다.

 

 

# MySQL / RDS

MySQL은 가장 널리 사용되고 있는 관계형 데이터베이스 관리 시스템(RDBMS) 이다.

MySQL은 오픈 소스이며, 다중 사용자와 다중 스레드를 지원한다.

또한, C언어, C++, JAVA, PHP 등 여러 프로그래밍 언어를 위한 다양한 API를 제공하고 있다.

 

MySQL은 유닉스, 리눅스, 윈도우 등 다양한 운영체제에서 사용할 수 있으며, 특히 PHP와 함께 웹 개발에 자주 사용된다.

 

 

# QueryDSL, JPA (Native Query, JPQL)

이전에는 MyBatis를 사용하였는데, MyBatis은 SQL Session 생성/연결, SQL문을 개별로 작성하여 매핑한 후 Mapper(매핑xml)을 사용하여 SQL문을 호출하는 등 길고 반복적인 코드가 필요하다.

 

JPA는 이런 코드의 양을 줄여주고 가독성이 좋게 해준다. 다만 상세한 조건의 데이터를 추출하기에는 제한이 있고, Entity에 대한 repository를 생성/운영해야 하는 단점이 있다.

 

QueryDSL은 JPQL을 Java 코드로 작성할 수 있도록 하는 라이브러리다.

추가적인 클래스 파일을 생성할 필요가 없으며 JPA보다 자세한 조건을 통한 데이터 추출이 가능하다. 또한 동적 쿼리를 지원하여 조회 조건이 동적으로 바뀌는 경우 즉각적으로 반영하여 조회할 수 있다.

 

따라서, 단순히 데이터를 저장하기 위한 용도로는 JPA로 save() 함수를 사용하여 저장하고, 이외의 데이터 조회, 추출, 반영 등을 위한 동작이 필요할 때는 QueryDSL 을 사용하였다.

 

 

# Redis (Kafka)

Redis란 Key, Value 구조의 비정형 데이터를 저장하고 관리하기 위한 오픈 소스 기반의 비관계형 데이터 베이스 관리 시스템 (DBMS)이다.

데이터베이스, 캐시, 메세지 브로커로 사용되며 인메모리 데이터 구조를 가진 저장소이다.

Redis는 이번 프로젝트에서 구현할 채팅 쪽에서만 사용하도록 하였다.

MySql 과 같은 데이터 베이스가 있음에도 불구하고 Redis를 사용하는 이유는 데이터 베이스는 데이터를 물리 디스크에 직접 쓰기 때문에 서버에 문제가 발생하여 다운되더라도 데이터가 손실되지 않기 때문에 프로젝트 DB로써 적합하다. 하지만 매번 디스크에 접근해야 하기 때문에 사용자가 많아질수록 부하가 많아져서 느려질 수 있다.

즉, 사용자가 늘어난다면 데이터 베이스가 과부하 될 수 있기 때문에 캐시 서버를 사용해야하는데,

이 캐시 서버로 이용할 수 있는 것이 Redis이다.

 

캐시는 한번 읽어온 데이터를 임의의 공간에 저장하여 다음에 읽을 때는 빠르게 결괏값을 받을 수 있도록 도와주는 공간이다.

같은 요청이 여러 번 들어오는 경우 매번 데이터 베이스를 거치는 것이 아니라 캐시 서버에서 첫 번째 요청 이후 저장된 결괏값을 바로 내려주기 때문에 DB의 부하를 줄이고 서비스의 속도도 느려지지 않는 장점이 있다.

 

채팅을 할 떄는 단순히 해당 채팅 내용만 Stomp를 통한 Topic을 구독한 구독자들에게 보여지게만 하면 되기 떄문에 이벤트가 저장되는 kafka보다 이벤트를 일일히 저장하지 않고 구독과 발행이 실시간으로 이루어지는 Redis가 적합하다고 생각되어 Redis를 선택하게 되었다.

 

728x90
반응형
LIST

'프로젝트 > 라이어게임' 카테고리의 다른 글

[회원관리] 로그인  (0) 2023.01.03
[회원관리] 회원가입  (0) 2022.12.30
프로젝트 세팅  (0) 2022.12.30
[회원관리] Jwt를 활용한 회원관리 기능 세팅  (0) 2022.12.28