https://sunro1994.tistory.com/253
이전 글에서는 Redis를 사용한 채팅 기능 구현을 위한 개념에 대해 간단히 알아보았다.
이번에는 WebSocket과 Redis의 Config파일 및 필요한 설정들에 대해 적어보려고 한다.
@EnableWebSocketMessageBroker어노테이션을 적용하여 웹소켓 메세지 브로커에 대한 설정을 적용한다.
통신에 필요한 목적지 설정(configureMessageBroker)에서 목적지 접두사는 /sub, 애플리케이션에서 처리할 메세지는 /pub 으로 접두사를 지정한 것을 확인할 수있다.
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//간단한 메모리 기반 메시지 브로커를 활성화, 브로커의 목적지 접두사는 "/sub"
registry.enableSimpleBroker("/sub");
// 애플리케이션에서 처리할 메시지의 접두사는 "/pub"
registry.setApplicationDestinationPrefixes("/pub");
}
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
registration.setMessageSizeLimit(200 * 1024 * 1024); // 메세지 크기 제한 오류 방지(이 코드가 없으면 byte code를 보낼때 소켓 연결이 끊길 수 있음)
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) { // (5)
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("통신할 주소")
.withSockJS();
}
}
다음은 Redis의 Config파일 설정이다.
1. redis host와 port를 @Value 어노테이션으로 설정값을 불러와 변수로 사용한다.
2. ChannelTopic을 빈으로 등록하여 해당 토픽으로 채팅 내용을 주고 받을 수 있도록 채널을 설정한다.
3. redisTemplate의 지네릭은 각자 타입에 맞게 생성해주길 바란다. 지금은 <Object,Object> 이지만 <String, ChattingRequestDto>와 같은 타입으로 변경하여 설정해야 하는 상황이 발생할 수도 있다.
4. 이 부분에서 삽질을 많이 할 수도있다. messageListenerAdapter 메서드를 보면 "sendMessage"라는 메서드를 ListenerMethod로 설정한다. 이 메서드는 SubScriber전용 클래스를 생성하여 내부 메서드이름을 동일하게 생성해야 한다.
아래 예시코드를 첨부해놨다.
5. 마지막 redisMessageListener 에서는 위에서 설정한 내용들을 담는 컨테이너를 생성하여 빈으로 등록해주었다.
이 과정을 통해서 애플리케이션이 초기화 단계에 아래 내용들을 설정하고 웹소켓을 이용할 준비를 마친다.
@RequiredArgsConstructor
@Service
@Slf4j
public class RedisSubscriber {
private final ObjectMapper objectMapper;
private final SimpMessageSendingOperations messagingTemplate;
//messageListenerAdapter의 매개변수 이름과 동일해야 감지가 가능하다
public void sendMessage(String message) {
try{
ChattingResponseDto requestMessage = objectMapper.readValue(message, ChattingResponseDto.class);
log.info("chattingRequestDto: {}", requestMessage.getContent());
log.info("chattingResponseDto: {}", requestMessage.getImage());
messagingTemplate.convertAndSend("/sub/chat/room/" + requestMessage.getRoomId(), requestMessage);
} catch (Exception e){
log.error(e.getMessage());
}
}
}
위 설명을 기반으로 한 전체 코드
@Configuration
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private String port;
//RedisConnectionFactory 빈을 생성하는 메서드
//Redis서버와의 연결을 설정하고 관리하는데 사용
@Bean
public ChannelTopic channelTopic() {
return new ChannelTopic("chatroom");
}
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(Integer.parseInt(port));
return new LettuceConnectionFactory(redisStandaloneConfiguration);
}
@Bean
public RedisTemplate<Object, Object> redisTemplate() {
RedisTemplate<Object,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
//키를 위한 직렬화 설정
redisTemplate.setKeySerializer(new GenericToStringSerializer<>(Long.class)); // Key를 Long 타입으로 처리
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//값을 위한 직렬화 설정
Jackson2JsonRedisSerializer<ChatRoom> serializer = new Jackson2JsonRedisSerializer<>(ChatRoom.class);
redisTemplate.setValueSerializer(serializer); // Value를 JSON 형태로 직렬화
redisTemplate.setHashValueSerializer(serializer);
return redisTemplate;
}
@Bean
public MessageListenerAdapter messageListenerAdapter(RedisSubscriber subscriber) {
return new MessageListenerAdapter(subscriber, "sendMessage");
}
/**
* redis 에 발행(publish)된 메시지 처리를 위한 리스너 설정
*/
@Bean
public RedisMessageListenerContainer redisMessageListener (
MessageListenerAdapter listenerAdapterChatMessage,
ChannelTopic channelTopic
) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(redisConnectionFactory());
container.addMessageListener(listenerAdapterChatMessage, channelTopic);
return container;
}
}
다음 포스팅에서는 Chat 도메인에 대한 엔티티(ChatRoom) 및 컨트롤러(ChatController), 서비스(pubService, subService, Chatservice)를 올리도록 하겠다.
참고로 나는 채팅방은 MySQL로 저장하고 채팅 내용은 Redis에 저장하며 3일동안만 저장이 가능하도록 설정하였다.
'Redis > Redis 채팅' 카테고리의 다른 글
Redis Pub/Sub을 활용한 채팅 구현의 여정 - Service 레이어 (0) | 2024.12.19 |
---|---|
Redis Pub/Sub을 활용한 채팅 구현의 여정 - Chat Domain(Entity ~ Controller) (0) | 2024.12.08 |
Redis Pub/Sub을 활용한 채팅 구현의 여정 - 개념편 (0) | 2024.11.28 |
[Redis] SpringBoot + Redis Pub/Sub 으로 채팅 구현 하기 (0) | 2024.07.18 |