Redis 에 hashtagId와 userId를 String, Object 타입으로 삽입하고 읽어오는 과정에서 캐스팅 오류가 발생했다.
java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String
기존 코드는 다음과 같다.
private final RedisTemplate<String, Object> redisTemplate;
public void set(Long hashtagId, Long userId) {
String key = "hashtagId:" + hashtagId.toString();
redisTemplate.opsForList().rightPush(key, userId);
}
public List<Follow> getAllFollows() {
Set<String> keys = redisTemplate.keys("hashtagId:*");
List<Follow> follows = new ArrayList<>();
if (keys != null) {
for (String key : keys) {
List<Object> userIds = redisTemplate.opsForList().range(key, 0, -1);
if (userIds != null) {
for (Object userId : userIds) {
Long hashtagId = Long.parseLong(key.split(":")[1]);
User user = userRepository.findById((Long)userId)
.orElseThrow(() -> new IllegalArgumentException("해당 사용자를 찾을 수 없습니다."));
Hashtag hashtag = hashtagRepository.findById(hashtagId)
.orElseThrow(() -> new IllegalArgumentException("해당 해시태그 찾을 수 없습니다. "));
Follow follow = new Follow(user, hashtag);
follows.add(follow);
}
}
}
}
return follows;
}
원인은 userId를 바로 (Long)으로 캐스팅하려 해서 발생한 문제였다.
Redis 는 기본적으로 데이터를 문자열로 취급해서 내부적으로 바이트 배열로 변환되어 저장된다. 따라서 Long 으로 저장해도 String으로 저장된다.

따라서 읽어올 때 String 으로 읽어온 뒤 원하는 데이터로 캐스팅해줘야 한다.
public void set(Long hashtagId, Long userId) {
String key = "hashtagId:" + hashtagId.toString();
redisTemplate.opsForList().rightPush(key, userId);
}
public List<Follow> getAllFollows() {
Set<String> keys = redisTemplate.keys("hashtagId:*");
List<Follow> follows = new ArrayList<>();
if (keys != null) {
for (String key : keys) {
List<Object> userIds = redisTemplate.opsForList().range(key, 0, -1);
if (userIds != null) {
for (Object userId : userIds) {
Long hashtagId = Long.parseLong(key.split(":")[1]);
Long userIdLong = Long.parseLong(String.valueOf(userId));
User user = userRepository.findById(userIdLong)
.orElseThrow(() -> new IllegalArgumentException("해당 사용자를 찾을 수 없습니다."));
Hashtag hashtag = hashtagRepository.findById(hashtagId)
.orElseThrow(() -> new IllegalArgumentException("해당 해시태그 찾을 수 없습니다. "));
Follow follow = new Follow(user, hashtag);
follows.add(follow);
}
}
}
}
return follows;
}
오류 수정하는데 되게 오래 걸렸는데.. 찾아보는 과정에서 Redis는 직렬화 문제와 밀접하다는 것을 알게 되었다. 나도 String으로 저장했을 때 값이 "/"1"/"과 같이 저장되는 것을 확인할 수 있었다. 앞서 말한대로 Redis의 모든 데이터는 byte 코드로 저장되는 것과 연관관계가 있다.
Redis에는 직렬화를 지원해주는 인터페이스가 많은데 그 중 StringRedisSerializer이 보편적으로 많이 사용된다.
StringRedisSerializer는 String 값을 그대로 저장하는 Serializer로, 직접 인코딩/디코딩을 해줘야 하는 번거로움이 있지만 class 타입을 지정할 필요가 없고, redisTemplate을 여러 쓰레드에서 접근해도 serializer 문제가 발생하지 않아 가장 무난하다.