Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Archives
Today
Total
관리 메뉴

JustDoEat

RabbitMQ에서 Jackson을 이용해서 메시지를 직렬화 하는 방법을 국정조사 마냥 털어보자. 본문

카테고리 없음

RabbitMQ에서 Jackson을 이용해서 메시지를 직렬화 하는 방법을 국정조사 마냥 털어보자.

kingmusung 2024. 9. 28. 21:32

개요

RabbitMQ를 사용하는 도중 RabbitMQ 같은 경우는 메시지를 큐에 넣을 때 메시지를 직렬화해서 값을 넣는다는 것을 알았다. 

반대로 redis를 사용할 때도 Value 값에 Object가 들어갈 때는 JackSon 라이브러리를 사용했던 기억이 났어서 어떻게 의존성을 주입받고 동작하는 건지 알고 싶어서 한번 파해쳐 보았다.

 

추가로 convertAndSend를 했을 때 직렬화를 자동으로 해준다고 하는데 "자동으로 해준다"이게 너무 궁금해서 클래스 간의 상속관계를 통해 알아보는 글입니다!

 

아래 내용은 기본적으로 RabbitMQ의 설정이 되어있다는 전재하에 설명이 되어있습니다.

 

솔직히 메서드들의 연관관계를 다 타고 확인은 하지만 세세한 모든 부분을 전부 알 수는 없습니다, 이미 남들이 잘 만들어놓은 라이브러리를 사용하고 있기 때문입니다 단지.

 

오늘의 궁금증은 메서드들을 타고 올라가면서 어떤 식으로 동작을 하는지에 대한 기록을 담아보았습니다.

 

@Bean
	public Jackson2JsonMessageConverter jsonMessageConverter() {
		return new Jackson2JsonMessageConverter();
	}

 

@Bean
public RabbitTemplate rabbitTemplate() {
	//다른 부분은 과감하게 생략, 컨버터 주입만 보이게 함
    rabbitTemplate.setMessageConverter(jsonMessageConverter());
    return rabbitTemplate;

JackSon이란

 

Jackson은 자바에서 널리 사용되는 JSON처리 라이브러리다, 이름도 비슷하다. 물론 JSON 뿐만이 아니라 xml, yml 파일들도 처리가 가능하다.

 

주기능은 데이터의 직렬화(serializetion)이다. 예시를 들면서 설명을 하겠다.

 

일단 요청이 들어왔을 때, DTO로 데이터를 받아오는 예시를 생각해 보자.

 

대략 위와 같이 요청을 한다고 생각을 했을 때.

@Getter
public class MessageRequest {
	private String content;
	private String sender;
	MessageRequest(){}
	MessageRequest(String content, String sender){
		this.content = content;
		this.sender = sender;

	}
}

 

MessageRequest(DTO) 객체에 맵핑이 되어서 서버단으로 들어올 것이다.

 

MessageRequest messageRequest = new MessageRequest("ㅎㅇ","기무성")

 

 

1차적으로 컨트롤러로 요청이 들어오면  위와 같은 객체가 생성이 될 것이다.

 

이제 컨트롤러에서 RabbitMQ의 Exchange에 메시지를 전달해야 할 것이다.

 

조금만 유연하게 생각을 해보면 MessageRequest라는 객체 자체를 메시지 큐에 바로 넣는다는 건 뭔가 말이 안 되는 거 같다.

 

MessageRequest라는 객체를 JSON형식으로 바꾼 후 직렬화를 해주어야 한다.

{
"content":"ㅎㅇ",
"sender":"기무성"
}

 

근데 위와 같이 "뚝딱"한다고 변환이 되면 너무 좋겠지만. 그걸 쉽게 해 주기 위해 JackSon라이브러리를 사용을 한다.

 

자바 객체를 JSON으로 변환하는 로직을 직접 구현할 필요 없이, ObjectMapper를 사용해 한 줄로 해결할 수 있다!!

 

MessageRequest messageRequest = new MessageRequest("ㅎㅇ","기무성")

ObjectMapper objectMapper = new ObjectMapper(); // 젝슨

String message = objectMapper.writeValueAsString(messageRequest);

 

 

위와 같이 ObjectMapper를 이용해서 객체를 넣어주면 손쉽게 JSON형식의 문자열로 변환이 된다.

 

반대로 JSON형식의 문자열로 변환된 객체를 다시 객체로 돌리기 위해서는

MessageRequest deserializedMessage = objectMapper.readValue(message, MessageRequest.class);

 

 

위와 같이 readValue의 인자값으로 순서대로

 

"JSON형식의 문자열(message)"

 

"바꾸고자 하는 객체타입의 클래스"를 넣어주면 된다.

 

물론 문자열과, 객체타입의 클래스의 포맷이 일치해야 한다. 터무니없는 것을 넣으면 못 알아듣는다.


"꼬리물기"

 

꼬리물기를 하기 앞서 내가 궁금한 객체타입 혹은 클래스에

 

"우클릭"  =>  "이동"  =>  "상위 클래스 또는 인터페이스"

기능을 사용하여 꼬리물기를 하였습니다.


"그래서 넌 뭐가 그렇게 궁금했는데?"

	@MessageMapping("chat.enter.{roomId}")
	public void enterUser(@DestinationVariable("roomId") Long roomId, @Payload MessageRequest message) {
		System.out.println("operation enter");
		System.out.println(message.getContent());
		rabbitTemplate.convertAndSend("chat.exchange", "room." + roomId, message);

	}

 

위는 프런트엔드로부터 메시지를 받을 컨트롤러 부분이다.

 

stompClient.send(`/publish/chat.enter.1`, {}, newMessage); // 컨트롤러 요청

 

 

프런트엔드에서 위와 같이 요청을 하면 newMessage의 내용이 RequestMessage라는 객체에 맵핑이 되어서 들어올 것이다.

 

MessageRequest messageRequest = new MessageRequest("ㅎㅇ","기무성")

 

해당객체가 전달이 되고, RabbitMQ설정파일에서 미리 의존성 설정을 해놓은

 

RabbitTemplate의 메서드를 사용해서 교환기(Exchanger)로 메시지를 전달을 할 것이다.

rabbitTemplate.convertAndSend("chat.exchange", "room." + roomId, message);
// convertAndSend의 메서드의 인자로는 순서대로 교환기의 이름, 라우팅키, 메시지이다

 

뭐가 궁금했냐면, 직렬화를 할 때 Jackson 라이브러리를 보통 많이 쓰는 걸 보았고. 내가 아는 잭슨을 통한 직렬화는

 

MessageRequest messageRequest = new MessageRequest("ㅎㅇ","기무성")

ObjectMapper objectMapper = new ObjectMapper(); // 젝슨

String message = objectMapper.writeValueAsString(messageRequest);

 

이러한 형식인데 어떤 구조로 ObjectMapper의 WriteValueAsString이 호출되는지 궁금했다. 

 

최종적으로 정리하자면 RabbitTemplate의 ConvertAndSend를 쓰면 Jackson 라이브러리의 직렬화 메서드가 호출되는지 궁금한 것이다.

 

 

아래는 의존성이 주입되는 과정(접은 글)

더보기
@Bean
	public Jackson2JsonMessageConverter jsonMessageConverter() {
		return new Jackson2JsonMessageConverter();
	}

 

@Bean
public RabbitTemplate rabbitTemplate() {
	//다른 부분은 과감하게 생략, 컨버터 주입만 보이게 함
    rabbitTemplate.setMessageConverter(jsonMessageConverter());
    return rabbitTemplate;

 

여기까지는 RabbitMQ 설정에 관련된 의존성을 주입. 위 부분은 글의 초점인 메시지 컨버터만 보여줌.

 

스프링이 올라올 때 setMessageConverter에 의해 의존성으로 Jackson 주입완료

 

public void setMessageConverter(MessageConverter messageConverter) {
    this.messageConverter = messageConverter;
}

 

 

 


"꼬꼬 클, 꼬리의 꼬리를 무는 클래스.. "

 

rabbitTemplate.convertAndSend("chat.exchange", "room." + roomId, message);
// convertAndSend의 메서드의 인자로는 순서대로 교환기의 이름, 라우팅키, 메시지이다

 

"컨트롤러에서 교환기로 전달하는 과정에 직렬화 과정이 있겠지.. 꼬리물기 시작"

 

1.RabbitTemplate

 

"convertAndSend를 검색해 볼까?"

 

converAndSend가 너무 많지만, 내가 보낸 형식과 현제 일치하는 건 아래 하나밖에 없다

 

교환기, 라우팅키까지 잘 들어가는데!! Object를 받는 부분에 convertMessageIfNecessary라는 메서드가 있다.

 

하지만 이건 내가 잭슨에서 본. writeValueAsString이라는 메서드가 아니기 때문에 convertMessageIfNecessary라는 메서드가 뭐 하는 녀석인지 한번 더 꼬리를 물겠다.

 

일단 convertMessageIfNecessary 이 녀석도 convertAndSend처럼 RabbitTemplate 안에 있다.

 

 

"찾았다, 엥 getRequiredMessageConverter로 내가 주입한 직렬화기를 가지고 오는 건 이해했어"

 

   private MessageConverter getRequiredMessageConverter() throws IllegalStateException {
        MessageConverter converter = this.getMessageConverter();
                if (converter == null) {
            throw new AmqpIllegalStateException("No 'messageConverter' specified. Check configuration of RabbitTemplate.");
        } else {
            return converter;
        }
    }

 

"음 getMessageConvert를 또 호출해서 내가 설정한 컨버터를 가지고 오는군"

 

"어 근데, JackSon에는. toMessage라는 메서드가 없는데.."

 

"Jackson에는 toMessage라는 메서드는 존재하지 않는데.. 직렬화를 어디서 시키는 거야"

 

getRequiredMessageConverter 메서드의 반환값을 보면

MessageConverter

 

타입이다, 분명 MessageConverter 객체에는 있을 수 있다. "다시 꼬리 물기!"

 

 

2. Jackson2 JsonMessageConverter

 

"여기도 아니야.. 부모 클래스로 가보자"

 

3. AbstractJackson2 MessageConverter

 

"여기도 아니네.. 또 부모클래스로 가보자"

 

4. AbstractMessageConverter

 

"찾았다, toMessage()"

 

"하.. 근데 또 createMessage라는 메서드를 또 호출하네 ㅡㅡ 질리게 한다 찾아보자."

 

3. AbstractJackson2 MessageConverter(숫자 잘 못쓴 거 아님)

 

" 어 근데 아까 봤던 3번째 클래스에 있잖아? "

 

" 와 찾았다.. "


 

소감.

사실 몰라도 구동하고, 기능이 돌아가는 데는 문제가 없다.. 시간낭비라고 생각이 들 수도 있다.

 

비록 코린이이자, 개미똥에 있는 박테리아만큼의 경험으로는. 언젠가 오늘 했던 막일이 나비효과처럼 나에게 유의미한 결과를 가져다줄 수 있다는 것이다.

 

이렇게 메서드를 타고 올라가 보는 경험이 별로 없었기 때문에 생각보다 시간을 오래 잡아먹었다. 하면서 뭔가 조금 성장한 거 같아서 기록을 해보았다.