You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 5 Next »


Stateful VS Stateless

REST API (Stateless 방식)

✔ 개념

  • REST API는 Stateless(상태 비유지) 방식으로 동작합니다.
  • 클라이언트 요청(Request)은 항상 독립적으로 처리되며, 서버는 클라이언트의 이전 요청 상태를 기억하지 않습니다.
  • 각 요청은 필요한 모든 정보를 포함해야 하며, 서버는 이를 기반으로 응답을 반환합니다.

✔ 장점

확장성(Scalability) 높음

  • 서버는 클라이언트의 상태를 유지하지 않으므로 로드 밸런싱 및 수평 확장이 용이합니다.
  • 여러 서버에서 요청을 처리해도 문제가 없으며, 서버 간 상태 동기화가 필요하지 않습니다.

단순하고 유지보수 용이

  • 상태를 관리할 필요가 없기 때문에 개발이 단순하고 유지보수가 쉬워집니다.
  • API 요청이 독립적이므로 디버깅과 로깅이 용이합니다.

캐싱 활용 가능

  • HTTP 기반이므로 응답을 캐싱하여 성능을 최적화할 수 있습니다.
  • 클라이언트 측에서 캐시를 활용하면 서버 부하를 줄일 수 있습니다.

❌ 단점

실시간성이 부족함

  • 클라이언트가 변경 사항을 지속적으로 확인하려면 주기적인 폴링이 필요하여 비효율적입니다.
  • WebSocket처럼 서버에서 즉시 데이터를 Push할 수 없습니다.

매 요청마다 인증이 필요

  • Stateless 방식이므로 요청마다 인증 정보를 포함해야 하며, 이를 처리하는 과정에서 성능 오버헤드가 발생할 수 있습니다.
  • JWT, OAuth와 같은 인증 토큰을 사용하여 이 문제를 해결할 수 있지만, 여전히 매 요청마다 인증 검증이 필요합니다.

🔹 WebSocket (Stateful 방식)

✔ 개념

  • WebSocket은 Stateful(상태 유지) 방식으로 동작하며, 클라이언트와 서버 간의 지속적인 연결(Persistent Connection)을 유지합니다.
  • 한 번 연결되면, 양방향 통신(Bi-directional Communication)이 가능하며, 서버는 클라이언트의 상태를 기억하고 필요할 때 데이터를 Push할 수 있습니다.

✔ 장점

실시간 데이터 전송 최적화

  • 서버는 클라이언트의 요청 없이도 데이터를 즉시 전송(Push)할 수 있어, 실시간 애플리케이션(예: 채팅, 주식 거래, 게임)에 적합합니다.

네트워크 오버헤드 감소

  • REST API와 달리, 매 요청마다 새로운 연결을 생성하지 않으므로 네트워크 리소스 사용이 효율적입니다.
  • 연결이 유지되므로 인증 과정을 한 번만 수행하면 됩니다.

양방향 통신 가능

  • REST API는 클라이언트 → 서버 방향의 요청-응답 방식이지만, WebSocket은 서버 → 클라이언트도 가능하여 보다 유연한 데이터 흐름을 제공합니다.

❌ 단점

확장성이 낮음

  • 서버가 각 클라이언트와의 연결을 유지해야 하므로 많은 사용자가 접속할 경우 리소스 부담이 증가합니다.
  • 로드 밸런싱이 어렵고, 클러스터링 시 세션 공유 또는 연결 유지 로직이 추가적으로 필요합니다.

복잡한 상태 관리 필요

  • 연결 상태를 유지해야 하므로, 클라이언트가 예기치 않게 연결 해제되거나 재접속할 경우 상태를 관리하는 추가적인 로직이 필요합니다.
  • 예: 세션 유지, 연결 끊김 감지, 재연결 로직 구현.

브라우저 및 네트워크 제한 사항 존재

  • 방화벽, 프록시 등의 네트워크 환경에서 WebSocket 연결이 차단될 수 있습니다.
  • HTTP 기반보다 보안 및 안정성을 고려한 추가적인 설정이 필요할 수 있습니다.

🔹 어떤 방식을 선택해야 할까?

REST API가 적합한 경우

  • 요청과 응답이 독립적인 경우 (예: CRUD API, 데이터 조회 서비스)
  • 서버 확장성과 유지보수가 중요한 경우
  • 네트워크 및 보안 이슈가 신경 쓰이는 경우

WebSocket이 적합한 경우

  • 실시간성이 중요한 경우 (예: 채팅, 스트리밍, 주식 데이터, 게임)
  • 양방향 통신이 필요한 경우 (예: P2P 연결)
  • 연결 유지가 가능하고 성능 최적화가 필요한 경우



Spring WebFlux의 org.springframework.web.reactive.socket 기반의 웹소켓과 전통적인 Spring MVC 기반 웹소켓을 비교해보겠습니다.


1. 아키텍처적 차이

비교 항목Spring WebFlux (org.springframework.web.reactive.socket)Spring MVC (전통적인 웹소켓)
스레드 모델Non-blocking, 이벤트 기반 (Netty, Undertow 등 지원)Blocking, Thread-per-connection (Tomcat, Jetty, Undertow)
리액티브 스트림 지원Flux<Message>, Mono<Message> 기반@MessageMapping 기반
동시성 처리요청 처리에 많은 스레드를 사용하지 않음 (적은 리소스로 높은 성능)많은 동시 접속 시 스레드가 증가, 성능 저하 가능
서버 요구 사항Netty, Undertow, Jetty 등 리액티브 지원 서버 필요Tomcat, Jetty, Undertow 지원
부하 분산가볍고 빠르며 부하를 효과적으로 처리 가능높은 동접자 수 처리 시 성능 저하 가능

2. 성능 및 확장성

비교 항목Spring WebFlux (org.springframework.web.reactive.socket)Spring MVC (전통적인 웹소켓)
성능대량의 동시 요청 처리에 유리동시 연결이 많아지면 성능 저하
부하 분산비동기 이벤트 기반 처리로 효율적스레드 증가로 인한 오버헤드 발생
리소스 관리적은 스레드로 많은 요청 처리 가능많은 동시 요청 시 스레드 리소스 소모 증가
지연 처리이벤트 기반으로 낮은 지연 시간스레드가 많아지면 응답 지연 증가
  • WebFlux 기반 웹소켓비동기 이벤트 기반 모델이기 때문에 최소한의 리소스로 높은 동시성을 처리할 수 있습니다.
  • 반면 Spring MVC 웹소켓Blocking 모델로서 요청당 하나의 스레드를 할당해야 하므로 동접자가 많아질수록 성능 저하가 발생할 수 있습니다.

3. 프로그래밍 모델 차이

Spring WebFlux 웹소켓 (비동기, 리액티브)

@Component
class ReactiveWebSocketHandler : WebSocketHandler {
    override fun handle(session: WebSocketSession): Mono<Void> {
        val input = session.receive()
            .map { it.payloadAsText }
            .doOnNext { println("Received: $it") }

        val output = Flux.interval(Duration.ofSeconds(1))
            .map { "Server response $it" }
            .map(session::textMessage)

        return session.send(output)
    }
}




Spring MVC 웹소켓 (동기, Blocking)

@Controller
class TraditionalWebSocketHandler {

    @MessageMapping("/message")
    @SendTo("/topic/response")
    fun handleMessage(message: String): String {
        println("Received: $message")
        return "Server response: $message"
    }
}

4. 사용 시 고려 사항

WebFlux 웹소켓이 유리한 경우

  • 대규모 동시 연결 처리 (ex. 실시간 채팅, 금융 데이터 스트리밍)
  • 리소스가 제한적인 환경 (ex. 마이크로서비스)
  • Reactive Stack을 기반으로 애플리케이션을 구축할 경우
  • Netty 기반 서버를 사용할 때 최적화된 성능 제공

Spring MVC 웹소켓이 유리한 경우

  • 기존 MVC 프로젝트와의 통합이 필요할 경우
  • 비교적 적은 연결 수를 처리하는 서비스 (ex. 내부 알림 시스템)
  • 기존의 @Controller 기반 프로그래밍이 익숙한 경우
  • 톰캣(Tomcat)을 사용해야 하는 경우 (WebFlux는 기본적으로 Netty를 사용)

5. 정리

비교 항목Spring WebFlux (org.springframework.web.reactive.socket)Spring MVC (전통적인 웹소켓)
비동기 지원✅ 완전한 비동기 리액티브 모델🚫 동기적 처리
성능✅ 높은 동시성 처리 가능🚫 동접자가 많으면 성능 저하
사용하기 쉬운가?🚫 다소 복잡한 코드 구조✅ 전통적인 MVC 패턴과 유사
서버 호환성🚫 Netty, Undertow 등 필요✅ Tomcat, Jetty 지원
적용 사례✅ 실시간 데이터 스트리밍, 채팅, 알림✅ 단순한 웹소켓 기능

🔥 결론

  • 대규모 동시 연결성능이 중요한 서비스에서는 Spring WebFlux비동기 리액티브 웹소켓이 적합합니다.
  • 기존 Spring MVC 기반 프로젝트와의 호환성이 중요하고, 동접자가 많지 않다면 전통적인 Spring MVC 웹소켓이 더 쉬운 선택이 될 수 있습니다.

🚀 즉, 성능과 확장성이 중요한 경우 WebFlux, 익숙한 개발 방식과 기존 시스템 통합이 중요한 경우 MVC 웹소켓을 선택하는 것이 좋습니다.



🔹 액터 모델을 채택한 메시징/메신저 시스템

1. WhatsApp

  • 기술 스택: Erlang 기반
  • 특징:
    • Erlang의 BEAM VM에서 동작하는 **Erlang OTP(Actor 기반 시스템)**을 활용
    • 수백만 개의 동시 연결을 지원하는 강력한 메시징 처리 능력
    • 고가용성을 위해 분산 시스템으로 설계됨
  • 장점:
    • 낮은 오버헤드와 높은 동시성을 지원
    • 메시지 큐와 액터 기반 모델을 통해 안정적인 메시징 서비스 제공

2. Telegram

  • 기술 스택: Erlang 및 C++
  • 특징:
    • 서버는 Erlang과 C++로 구축되었으며, 내부적으로 액터 모델을 활용하여 비동기 메시지 처리
    • MTProto 프로토콜을 사용하여 효율적인 메시지 전송
    • 분산된 데이터 센터 구조를 통해 성능 최적화
  • 장점:
    • 빠른 메시지 처리 및 낮은 지연 시간
    • 확장성이 뛰어난 아키텍처

3. Microsoft Orleans

  • 기술 스택: .NET 기반 액터 모델 프레임워크
  • 특징:
    • 마이크로소프트에서 개발한 가상 액터(Virtual Actor) 모델을 제공
    • 내부적으로 Skype, Xbox, Teams의 메시징 시스템에 활용
    • 분산 환경에서 동적인 액터 생성을 지원하여 대규모 메시징 시스템 구축 가능
  • 장점:
    • 비동기 메시지 처리를 최적화
    • 클라우드 환경(Azure)에서 강력한 확장성 제공

4. Akka 기반 메시징 시스템

  • 기술 스택: Scala, Java, Kotlin
  • 특징:
    • **Akka (현재 Apache Pekko)**를 활용하여 분산된 메시지 기반 시스템 구축
    • Persistent Actor를 활용한 상태 유지형 메시징 가능
    • Event Sourcing + CQRS 패턴을 적용할 수 있어 메시지 로그 기반의 안정적인 설계 가능
  • 사례:
    • LinkedIn: Akka 기반 메시징 시스템 사용
    • Guardian 뉴스: Akka Streams 기반 스트리밍 서비스 구축
    • Signal(부분적으로 Akka 사용)

5. RabbitMQ (Erlang 기반)

  • 기술 스택: Erlang
  • 특징:
    • 메시지 브로커로 사용되지만, 내부적으로 Erlang의 액터 모델을 활용하여 높은 동시성 처리 가능
    • AMQP 프로토콜을 사용하여 안정적인 메시지 큐 제공
  • 장점:
    • 메시징 시스템에 최적화
    • 확장 가능하며, 분산 아키텍처에서 활용 가능



🚀 분석: 액터 모델과 웹소켓 연동의 장점

이 코드는 Akka (현재 Apache Pekko) 기반 액터 모델을 활용하여 **웹소켓을 관리하는 세션 관리자(SessionManagerActor)**를 구현한 것입니다.
액터 모델이 웹소켓과 연동될 때 가지는 장점은 다음과 같습니다.


🔹 1. 높은 동시성 및 확장성 (Scalability & Concurrency)

  • 문제: 웹소켓 연결은 지속적인 세션을 유지해야 하며, 많은 사용자가 접속할 경우 서버 부담이 증가할 수 있음.
  • 해결: 액터 모델을 사용하면 각 클라이언트의 세션을 독립적인 액터(Actor)로 관리할 수 있음.
  • 효과:
    • 액터는 경량 스레드로 동작하므로 수십만 개의 웹소켓 연결도 효율적으로 처리 가능
    • 세션 관리를 비동기 메시징 기반으로 처리하여 블로킹을 방지
    • 수평 확장(Scale-out)이 용이하여, 클러스터링(Akka Cluster)과 연계하면 더 많은 사용자를 처리 가능

💡 예제 코드 적용:

  • sessions: ConcurrentHashMap<String, WebSocketSession> : 액터 내부에서 웹소켓 세션을 관리
  • 각 명령(UserSessionCommand)이 액터 간 메시지로 전달되어 병렬로 처리됨

🔹 2. 상태(State) 유지 및 장애 복구 (Fault Tolerance)

  • 문제: 일반적인 REST API 기반의 웹소켓 관리 시스템에서는 클라이언트 상태를 별도로 관리해야 하며, 장애 발생 시 복구가 어렵다.
  • 해결: 액터 모델을 활용하면 각 웹소켓 세션을 하나의 액터 단위로 관리하여 상태를 자연스럽게 유지할 수 있음.
  • 효과:
    • 액터 모델 자체가 상태 기반(Stateful) 아키텍처이므로, 세션과 구독 정보를 쉽게 유지
    • 장애 발생 시 해당 액터만 복구하여 서비스 중단을 최소화
    • Akka Persistence 적용 시 세션 및 구독 정보를 이벤트 소싱 기반으로 저장하여 장애 이후에도 빠른 복원 가능

💡 예제 코드 적용:

  • sessions.remove(command.session.id) → 연결이 끊어진 세션을 자동으로 정리
  • topicSubscriptions.computeIfAbsent(command.topic) { mutableSetOf() }.add(command.sessionId) → 액터가 상태를 직접 유지하므로 상태 복원 가능

🔹 3. 비동기 이벤트 기반 처리 (Reactive & Event-driven)

  • 문제: 웹소켓은 실시간 이벤트 처리가 핵심이지만, 기존 REST 기반 시스템에서는 동기적인 요청-응답 구조로 인해 이벤트 처리의 유연성이 떨어짐.
  • 해결: 액터 모델은 **비동기 메시지 패싱(Asynchronous Message Passing)**을 기반으로 동작하며, 웹소켓과 자연스럽게 연계됨.
  • 효과:
    • 클라이언트가 특정 토픽을 구독(Subscribe)하면 액터가 이벤트를 관리하고 비동기적으로 메시지를 Push
    • 대량의 클라이언트에게 동시에 이벤트를 전달 가능 (예: 실시간 채팅, 알림 시스템)
    • REST API 기반의 Polling 없이 서버에서 직접 푸시(Push) 가능

💡 예제 코드 적용:

  • UserSessionCommand.SendMessageToTopic → 특정 토픽을 구독한 모든 세션에 비동기 푸시
  • UserSessionCommand.SendMessageToSession → 특정 세션에 개별적으로 메시지 전송

🔹 4. 트래픽 부하 감소 (Efficient Resource Utilization)

  • 문제: 대규모 웹소켓 연결을 유지하는 것은 서버 리소스 부담이 크며, 특히 HTTP Polling을 사용할 경우 네트워크 오버헤드가 증가함.
  • 해결:
    • 액터 모델은 자체적으로 비동기 메시지 큐 역할을 수행하여 네트워크와 CPU 부하를 최소화
    • 액터 간 직접 메시지 전송이 가능하여 불필요한 HTTP 요청을 제거
  • 효과:
    • 네트워크 오버헤드 최소화 (웹소켓은 한 번 연결되면 지속 유지)
    • 클라이언트가 필요할 때만 메시지를 푸시 받도록 최적화
    • HTTP Polling과 비교하면 서버 부하가 현저히 줄어듦

💡 예제 코드 적용:

  • sendService.sendEventTextMessage → 웹소켓을 통해 바로 클라이언트에 메시지 전송
  • topicSubscriptions → 불필요한 요청 없이 미리 등록된 구독자를 대상으로 메시지를 브로드캐스트

🔹 5. 확장성 있는 토픽 기반 메시징 시스템 (Scalable Pub/Sub Architecture)

  • 문제: 단순한 클라이언트-서버 메시징 모델은 확장성이 떨어지며, 많은 클라이언트가 특정 이벤트를 구독할 경우 관리가 어려움.
  • 해결:
    • 액터 모델은 Publish-Subscribe 패턴을 자연스럽게 지원하며, 토픽(Topic) 기반 메시지 브로드캐스팅이 가능
    • 추가적으로 Kafka, Redis Pub/Sub 등과 연계하면 더욱 확장 가능
  • 효과:
    • 특정 주제(Topic)에 대한 구독/해지를 쉽게 관리
    • 대규모 실시간 방송 시스템(예: 주식 거래, 뉴스 알림, 스포츠 경기 알림)에 적합

💡 예제 코드 적용:

  • onSubscribeToTopic → 클라이언트가 특정 토픽을 구독하면 세션 ID를 저장
  • onSendMessageToTopic → 해당 토픽을 구독한 모든 세션에 메시지를 전송






  • No labels