redis 메시지 직렬화 및 역직렬화 문제(2)

SaGo_MunGcci·2022년 10월 6일
2

스프링

목록 보기
29/31

Trouble Shooting

  • 문제
    1. 채팅메시지를 레디스에 저장시 저장이 안되고 아래와 같은 오류가 발생

      채팅메시지를 Redis에 저장 시 저장이 안되는 오류 발생

      오류내용 : org.springframework.data.redis.serializer.SerializationException: Cannot serialize;

    2. 채팅메시지를 레디스에 저장후 1:1 채팅방 및 경매 게시글 채팅방에서 나갔다 들어왔을때 아래의 오류 내용이 발생

      채팅메시지 Redis 저장 후 1:1 채팅방에 입장후 퇴장 시 오류 발생

      오류내용 : Java 8 date/time type java.time.LocalDateTime not supported by default

  • 원인 파악
    1. 위의 오류 내용(org.springframework.data.redis.serializer.SerializationException: Cannot serialize;)을 구글링해서 알아본 결과 레디스에 저장 및 조회 할 시 직렬화와 역직렬화가 필요한데 이 기능이 정상적으로 작동하지 않는 것으로 확인. —> 직렬화의 문제

      Redis에 저장 및 조회 시 직렬화/역직렬화가 필요한데 제대로 작동 안하는 문제

      1. 판단한 이유는 Encache, Redis, memcached 등의 라이브러리 시스템에서 캐시 할 부분을 자바 직렬화된 데이터를 저장해서 사용하는데, 특히 나의 경우에 바로 우리가 사용할 데이터를 redis에 저장하는 부분에서 위와 같은 오류가 발생 했기 때문이다.
      2. 참고 자료 : https://steady-coding.tistory.com/576
    • 발생한 오류 부분
//redis 에 메세지 저장하기
@Transactional
public ChatMessage save(ChatMessage chatMessage) {
    //chatMessageDto 를 redis 에 저장하기 위하여 직렬화 한다.
	
		//===================================================================
		//===================================================================			
		// 이부분에서 위와 같은 오류가 발생함.
		redisTemplate.setValueSerializer(
		new Jackson2JsonRedisSerializer<>(ChatMessage.class));
		//===================================================================
		//===================================================================

    String roomId = chatMessage.getRoomId();

    //redis에 저장되어있는 리스트를 가져와, 새로 받아온 chatmessageDto를 더하여 다시 저장한다.
    List<ChatMessage> chatMessageList = opsHashChatMessage.get(CHAT_MESSAGE, roomId);

    //가져온 List가 null일때 새로운 리스트를 만든다 == 처음에 메세지를 저장할경우 리스트가 없기때문에.
    if (chatMessageList == null) {
        chatMessageList = new ArrayList<>();
    }
    chatMessageList.add(chatMessage);
    //redis 의 hashes 자료구조
    //key : CHAT_MESSAGE , filed : roomId, value : chatMessageList
    opsHashChatMessage.put(CHAT_MESSAGE, roomId, chatMessageList);
    System.out.println();
    redisTemplate.expire(CHAT_MESSAGE,24, TimeUnit.HOURS);

    System.out.println("===========================ChatMessage save 시간 :"
												+ chatMessage.getCreatedAt());

    return chatMessage;
}
  1. 레디스로 채팅방별 채팅메시지를 저장후 불러올때 Java 8 date/time type java.time.LocalDateTime not supported by default가 오류가 발생 —> 역직렬화의 문제
    1. 오류메시지를 통해 redis에서 저장된 데이터의 LocalDateTime에 문제가 발생했다고 파악함
    2. redis에서 채팅메시지 목록을 불러올때의 문제이니까 역직렬화하면서 발생하는 문제라고 파악함.
    3. 따라서 채팅메시지에 생성시간과 수정시간을 기록하는 TimestampedChat이라는 클래스에서 제대로 역직렬화를 수행을 못한다고 판단함.
  • 선택지
    1. 1번의 문제에 관한 선택지

      1. 내가 코드를 잘못 입력했을 가능성(코드에 직렬화를 제대로 하지 못함.)

        —> 충분히 가능성이 있음 이번에 처음 레디스를 사용하면서 직렬화 및 역직렬화를 공부했기 때문임

        b. 잭슨 자체의 문제점.

      —> 라이브러리가 오류의 원인일 가능성은 현저히 떨어짐.

    2. 2번 문제에 관한 선택지

      1. LocalDateTime을 사용하는 클래스에 역직렬화를 제대로 적용
      2. db에 있는 생성시간을 메시지처럼 저장해서 채팅 기록 시간으로 사용
  • 의사결정
    1. 선택지 1번에 관한 의사결정 —> 자바의 직렬화와 역직렬화를 공부하기 시작함.
      1. 직렬화시 조건에 해당 클래스에 java.io.Serializable 인터페이스를 구현해하는 조건을 충족하지 못했던 것을 알게됨.
      2. 역직렬화를 하기 위해서 serialVersionUID또한 필요하다는 것을 알게되었다.
      3. 적용한 코드
//buld.gradle

//자바 직렬화 및 역직렬화 문제 해결 패키지
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
implementation 'com.fasterxml.jackson.core:jackson-databind'
@Getter
@Entity
public class ChatMessage extends TimestampedChat implements Serializable {
	
    private static final long serialVersionUID = 6494678977089006639L;
		...
}
  1. 선택지 2번에 관한 의사결정 —> LocalDateTime을 사용하는 클래스에 역직렬화를 제대로 적용하기로 함
    1. 참고 자료 : https://crazy-horse.tistory.com/51
    2. 코드 적용
@Getter
@Setter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class TimestampedChat implements Serializable {

    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @CreatedDate
    private LocalDateTime createdAt;

    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @LastModifiedDate
    private LocalDateTime modifiedAt;
}
  • 결과

    1. 정상적으로 레디스에 저장이 되었다.

    2. 정상적으로 시간을 포함한 이전 채팅기록이 출력되었다.

{
    "statusCode": 200,
    "msg": "OK",
    "data": [
        {
            "createdAt": "2022-10-04T13:03:03.8935017",
            "modifiedAt": "2022-10-04T13:03:03.8935017",
            "id": 2,
            "roomId": "f137251e-161a-43fd-9fbc-ac3068618354",
            "roomName": "경매1번방",
            "type": "TALK",
            "sender": "위키미키",
            "message": "ㅎㅇㅎㅇ",
            "nickName": "위키미키",
            "profileImgUrl": null,
            "createdAtString": "2022-10-04 13:03:03"
        },
        {
            "createdAt": "2022-10-04T13:03:05.1414266",
            "modifiedAt": "2022-10-04T13:03:05.1414266",
            "id": 3,
            "roomId": "f137251e-161a-43fd-9fbc-ac3068618354",
            "roomName": "경매1번방",
            "type": "TALK",
            "sender": "위키미키",
            "message": "네넹",
            "nickName": "위키미키",
            "profileImgUrl": null,
            "createdAtString": "2022-10-04 13:03:05"
        },
        {
            "createdAt": "2022-10-04T13:03:06.0023846",
            "modifiedAt": "2022-10-04T13:03:06.0023846",
            "id": 4,
            "roomId": "f137251e-161a-43fd-9fbc-ac3068618354",
            "roomName": "경매1번방",
            "type": "TALK",
            "sender": "위키미키",
            "message": "ㅎㅎ",
            "nickName": "위키미키",
            "profileImgUrl": null,
            "createdAtString": "2022-10-04 13:03:06"
        }
      
    ]
}


Retrospection

  • 이 오류를 해결한 이후 멘토님이 조금더 redis를 공부해보라고 말씀하셨다.

  • 직렬화 역직렬화시 serialVersionUID가 왜 필요한지 모르고 사용하면 안된다고 말씀하셨다.

  • redis를 사용하면 좋은 부분은 데이터가 자주 바뀌지 않으면서 조회해올 데이터가 용량이 클때 효과적이라고 말씀해 주셨다.

  • 미처 이런 부분에서 사용할줄은 정말 몰랐고 앞으로 공부후에 따로 정리를 해두어야 겠다고 생각했다.



profile
이리저리 생각만 많은 사고뭉치입니다.

0개의 댓글