본문 바로가기

Spring

Spring +STOMP+SOCKJS 채팅 구현(STOMP 작동방식)

STOMP

 

stomp는 http에 모델링된 frame 기반의 단순한 메세징 프로토콜이다. 

frame기반이라고 했는데 frame의 구조는 다음과 같다

COMMAND
header1 : value1
header2 : value2

Body^@

 

 

STOMP - client

클라이언트는 메세지를 보내기 위해 SEND 명령을 사용하거나 수신 메세지에 관심을 표현하기 위해 SUBSCRIBE 명령을 사용할 수 있다. SEND, SUBSCRIBE 같은 명령을 사용하려면 destination 이라는 헤더를 필요로 하는데 destination 헤더는 어디에 메세지를 전송할지 그리고 어디에서 메세지를 구독할지를 알려주는 헤더이다.

 

 위의 로그는 필자가 직접 채팅을 구현했을때 콘솔에 로그를 찍어본건데 실제로 클라이언트가 SUBSCRIBE 메소드를 사용하면 destination 이라는 헤더로 어디로 메세지를 보낼지 명시해서 보내야한다는 것을 알 수 있다.

 

이때 destination 의 값은 어떤 문자열이든 될 수 있다. 즉, 서버에서 어떠한 규칙으로 정하냐에 따라 무한한 방식이 있을 수 있다는 것이다. 하지만 일반적으로 "topic/.../.../" 주소는 publish-subscribe 방식의 one to many 일때 사용하고

"queue/.../"은 point to point 방식의 one to one 일때 사용된다.

 

STOMP destination 에 대해 더 알아야 할 것이 있는데 바로 STOMP는 여러 prefix를 통해 흐름을 결정하는데 자주쓰인prefix는 아래와 같다

 

 

  • /app은 메세지를 처리할 수 있도록 서버측의 annotated method 로 흐르도록 하기 위한 라우팅이다

  • /topic , /queue 는 broker로 흐르도록하기 위한 라우팅이다

 

 

브로커에 대한 개념이 나왔는데 아래 그림을 보면 방금 설명이 더 잘 이해 될 것이다.

위의 그림을 보면 클라이언트단에서 SEND 메세지를 쓸때 prefix를 주목하자

 

1.  클라이언트에서 destination에 /app 이라는 prefix를 주었을때 흐름

 

해당 request는 @messagemapping된 스프링 컨트롤러로 흘러가고 컨트롤러에서 메세지를 수신한 후 여러 작업들을 처리한 후에 /topic이라는 prefix를 통해 브로커에게 전달하면 브로커는  STOMP MESSAGE 메소드를 이용해서 특정 토픽을 구독하는 구독자들에게 reponse를 보낸다.

 

2.  클라이언트에서 destination에 /topic이라는 prefix를 주었을때 흐름

 

매핑된 스프링 컨트롤러를 안거치고 브로커에게 직접 접근하겠다는 뜻인데 주로 클라이언트가 subscribe를 할때 이 prefix를 사용하는 것으로 보인다. 계속 설명하지만  /topic 이라는 prefix를 쓰면 브로커에게 직접 전달되는데 이 경우 브로커가 직접 받아서 subscriber들 관리를 하는 것 같다.

 

 

 

STOMP - server

 

이제 서버에서 STOMP를 사용하는 방식에 대해서 알아보자.

 

stomp 서버는 모든 구독자에게 message를 broadcasting 하기 위해 MESSAGE 명령을 사용할 수 있다.

 

 

이것도 필자가 구현한 서버에서 클라이언트 쪽으로 메세지를 보냈을때 콘솔에 출력한 예시인데 헤더 부분에 명시된 여러 정보들 중 subscription-id 헤더를 주목해보자.

 

아까 위에서 클라이언트의 id와 일치하는 것을 알 수 있다. STOMP에서는 서버의 모든 메세지는 특정 클라이언트 구독에 응답하여야 하고 서버 메세지의 subscription-id 의 헤더는 클라이언트 구독의 id헤더와 일치해야한다.

 

 

코드를 통해 클라이언트와 서버가 어떻게 통신하는지 그 과정을 알아보자

 

1. Connection

 

connect를 하는 클라이언트단의 js 코드이다. 처음 소켓을 생성할때 /users라는 이름으로 만드는데 서버쪽의 브로커는 저 주소를 어떻게 알고 connect를 받게 되는 것일까. 바로 socket configuration 자바 코드에서 정의해주는 것이다.

 

 

위의 브로커 configuration 코드에서 registerStompEndpoints를 보면 Endpoint를 설정해주는데 이 endpoint를 /users라고 설정해주었기 때문에 위의 클라이언트가 connect 보낸 것을 받을 수 있게 된다.

 

 

2. SUBSCRIBE

 

위의 클라이언트 코드에서 stompClient.subscribe 메소드의 인자로 /topic/a 를 주었는데 위에서 설명했듯이 /topic은 브로커에게 흐름을 넘기는 prefix일 뿐이고 뒤에 a를 구독하는 상태이다. 

 

3. SEND

 

클라이언트에서 서버로 STOMP SEND메소드를 구현한 코드

자세한 내용은 아래 주석을 확인하자

그렇다면 서버에서는 어떻게 받고 클라이언트에게 메세지를 보내줄까

아래의 코드를 보자

클라이언트의 SEND 메소드의 첫번째 인자에서 매핑한대로 컨트롤러가 매핑되어 있는 것을 볼 수 있다. 단 /app은 빠져있는데 이것은 위에 configuration코드에서 /app이 spring으로 흘러가도록 설정해두었기 때문에 생략할 수 있다.

 

여기서 중요한것은 함수의 마지막줄에 destination이다.

STOMP는 topic, subscribe 관계가 핵심철학이다. 즉 이 컨트롤러는 "/topic" prefix를 이용해서 명시적으로 브로커에게 알려주었고 그 뒤에 a라는 topic을 전달해주었다.  그러면 브로커는 a 토픽을 구독하고 있는 모든 클라이언트들에게 메세지를 전달하게 된다. 다시 말해 컨트롤러가 STOMP destination을 리턴했고 브로커는 이 destination을 구독하고 있는 구독자들에게 메세지를 전달하게 된다.

 

마지막으로 서버측에서 보낸 response를 콘솔에 찍어보면 아래와 같다.

 

 

 

 

 

 

 

 

 

 

 

 

'Spring' 카테고리의 다른 글

Spring과 AWS S3 연동 파일 업로드  (0) 2020.01.04
Spring maven 프로젝트 생성  (0) 2019.08.27