코딩마을방범대

웹 소켓을 이용한 메시지 전송 프로세스의 이해 본문

💡 백엔드/Java

웹 소켓을 이용한 메시지 전송 프로세스의 이해

신짱구 5세 2023. 7. 13. 15:26
728x90

 

 

 

웹 소켓을 이용한 메시지 전송 (이전포스트)

 

 

코드에서 아래와 같이 SimpMessageHeaderAccessor을 이용하는 메시지 전송 코드를 보았다.

얼추 봤을 때 WebSocket을 사용하는 것이라고 어림짐작만 했지 본격적으로 알아보기는 이번이 처음이다.

 

Map<String, Object> msgMap = new HashMap<String, Object>(){{
	put("키값", "값");
}};

SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(user);
headerAccessor.setLeaveMutable(true);
simpMessagingTemplate.convertAndSendToUser(user, "/queue", msgMap, headerAccessor.getMessageHeaders());

 

 

순차적으로 알아보기 위해 SimpMessageHeaderAccessor 부터 차례대로 알아갈 예정이다!

메소드들 안에도 다양한 메소드들이 호출되어 대충 보기에도 엄청 복잡했다..

 

 

 


 

 

 

 

헤더 세팅

 

예제

SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);

 

 

 

SimpMessageHeaderAccessor 

simple 메시징 프로토콜에서 메시지 헤더 작업을 위한 클래스로 정의되어 있다.

 

create을 들어가보면 연쇄적으로 SimpMessageHeaderAccessor를 리턴하게 되어있다.

create에서 넘겨주는 SimpMessageHeaderAccessor를 들어가보면 아래 코드와 같다

 

protected SimpMessageHeaderAccessor(SimpMessageType messageType,
		@Nullable Map<String, List<String>> externalSourceHeaders) {
	super(externalSourceHeaders);
	Assert.notNull(messageType, "MessageType must not be null");
	setHeader(MESSAGE_TYPE_HEADER, messageType);
	headerInitializer.initHeaders(this);
}

솔직히 별 내용 없는 것 같다. 

그냥 header 값을 세팅해서 넘겨준다.

Assert.notNull()는 그냥 참고용으로 아래에 정의했다!

 

 

 


 

 

Assert.notNull()

Assert 는 메소드에 따른 인자를 검증하고 조건에 맞지않는 경우 Exception을 발생시킨다.
단순히 if문을 줄이는 역할을 하는 것이 아닌 프로젝트에 규칙을 적용하고 재사용한다는 것에 의미가 있다.

notNull 메소드를 살펴보면 아래의 코드와 같다.

public static void notNull(@Nullable Object object, String message) {
	if (object == null) {
		throw new IllegalArgumentException(message);
	}
}

이 메소드의 역할은 인자로 받은 object가 null일 경우 Exception을 발생시켜 인자의 message 값을 보내는 것이다.

 

 

 

 

 

 


 

 

 

 

메시지 전송

 

예제

simpMessagingTemplate.convertAndSendToUser(user, "/queue", msgMap, headerAccessor.getMessageHeaders());

 

 

SimpMessagingTemplate

SimpMessagingTemplate extends AbstractMessageSendingTemplate<String> implements SimpMessageSendingOperations

 

위 예제를 따라가보면 아래 코드의 내용을 확인할 수 있다.

 

 

convertAndSendToUser

 

convertAndSendToUser는 채널에 구독하고 있는 사용자들 중 모두에게가 아닌 특정한 사용자에게 메세지를 보낼 수 있도록 해주는 메소드이다.

 

첫 번째 인자(user)는 java.security에서 제공해주는 웹 통신의 주체에 대한 정보가 담겨있는 Principal 객체를 통해 고유한 세션아이디를 넘겨주면되고(고유한 세션아이디를 추출하는 방법은 이전 포스터 참고),

두 번째 인자(destination)는 수신할 채널이고,

세 번째 인자(payload)는 보내줄 데이터이다.

@Override
public void convertAndSendToUser(String user, String destination, Object payload,
		@Nullable Map<String, Object> headers) throws MessagingException {
	convertAndSendToUser(user, destination, payload, headers, null);
}

 

 

여기서 또 가르키는 convertAndSendToUser를 따라가보면...

 

 

convertAndSendToUser

@Override
public void convertAndSendToUser(String user, String destination, Object payload,
		@Nullable Map<String, Object> headers, @Nullable MessagePostProcessor postProcessor)
		throws MessagingException {
	Assert.notNull(user, "User must not be null");
	user = StringUtils.replace(user, "/", "%2F");
	destination = destination.startsWith("/") ? destination : "/" + destination;
	super.convertAndSend(this.destinationPrefix + user + destination, payload, headers, postProcessor);
}

1. Assert로 user의 null 여부를 체크

2. user 문자열에 "/" 라는 문자가 있을 경우 "%2F"로 변경 (인코딩디코딩 때의 문제 때문인 것 같음)

3. 목적지(destination) 문자열이 "/" 로 시작하지 않는 경우 앞에 "/" 를 붙여줌

 

최종적으로 부모클래스의 convertAndSend라는 메소드로 인수를 넘겨준다.

convertAndSend에서도 별 내용 없이 최종적으로 send라는 메소드를 호출한다.

 

 


 

 

 

send()

@Override
public void send(D destination, Message<?> message) {
	doSend(destination, message);
}

protected abstract void doSend(D destination, Message<?> message);

send()를 따라가보면 send는 doSend를 호출하고 있으며 doSend는 추상화메소드로, 

GenericMessagingTemplate과 SimpMessagingTemplate에서 정의하고 있다.

 

 

우리는 SimpMessageHeaderAccessor를 이용해 header를 세팅했으므로 SimpMessagingTemplate로 살펴보자!

 

doSend

@Override
protected void doSend(String destination, Message<?> message) {
	Assert.notNull(destination, "Destination must not be null");

	SimpMessageHeaderAccessor simpAccessor =
		MessageHeaderAccessor.getAccessor(message, SimpMessageHeaderAccessor.class);

	if (simpAccessor != null) {
		if (simpAccessor.isMutable()) {
			simpAccessor.setDestination(destination);
			simpAccessor.setMessageTypeIfNotSet(SimpMessageType.MESSAGE);
			simpAccessor.setImmutable();
			sendInternal(message);
		return;
		}
		else {
			// Try and keep the original accessor type
			simpAccessor = (SimpMessageHeaderAccessor) MessageHeaderAccessor.getMutableAccessor(message);
			initHeaders(simpAccessor);
		}
	}
	else {
		simpAccessor = SimpMessageHeaderAccessor.wrap(message);
		initHeaders(simpAccessor);
	}

	simpAccessor.setDestination(destination);
	simpAccessor.setMessageTypeIfNotSet(SimpMessageType.MESSAGE);
	message = MessageBuilder.createMessage(message.getPayload(), simpAccessor.getMessageHeaders());
	sendInternal(message);
}

 

 

if문을 확인해보면 isMutable이 true인 경우 바로 메시지를 전송하고,

아닌 경우 header 세팅을 재정비한 후 if문을 빠져나와서 메시지를 전송한다.

 

나의 경우 Mutable을 true로 세팅해줬었기 때문에 바로 sendInternal로 이동할 수 있다.

 

 

send 메소드는 진짜 엄청나게 복잡하기 때문에 여기까지 알아보긴 무리다....

어찌됐든 send는 MessageChannel를 이용해서 복잡한 로직을 수행 후 메시지를 전송한다!

MessageChannel
2개의 클라이언트 사이에서 양방향으로 메시지를 주고 받을 수 있는 메시지 채널을 생성하는 웹API
즉, 1대1 통신일때 사용하는 API

 

 

 

 


 

 

 

 

 

 

💡 TIPS!

 

인수(Argument)와 인자(Parameter)의 차이점

  • 인자는 메소드(함수)를 정의할 때의 매개변수이고,
  • 인수는 메소드(함수)를 호출할 때의 값이다.
// 인자 = Parameter = 매개변수
public void test(String str){}

// 인수 = Argument
test(str)

 

 

 


 

 

알아두면 좋은 메소드

 

1. nullSafeEquals(Object o1, Object o2)
주어진 객체가 동일한지 확인하여 둘 다 null이면 true를 반환하고 하나만 null이면 false를 반환

 

2. instanceof 와 isInstance()의 차이

  • instanceof 는 컴파일타임에 타입 에러를 체크하고,
  • isInstance() 는 런타임에 타입 에러를 체크한다.

 

 

 

 


참고사이트

MessageChannel

Spring boot - Assert 사용 예제

 

 

728x90

'💡 백엔드 > Java' 카테고리의 다른 글

SpringBoot에서 Slack Webhoook 사용하기  (0) 2023.07.25
Optional에 대하여  (0) 2023.07.25
Java의 타이머 (Timer)  (0) 2023.06.22
Guava Cache  (0) 2023.06.20
SpringBoot의 @Qualifier와 @Primary  (0) 2023.06.20