fix: redis 설정수정 및 Event 발행 포맷 수정

This commit is contained in:
djeon 2025-10-26 00:15:10 +09:00
parent 31e47ee551
commit 3c7ea9d013
7 changed files with 178 additions and 2122 deletions

View File

@ -18,6 +18,9 @@ task printEnv {
} }
dependencies { dependencies {
// Module dependencies
implementation project(':notification')
// WebSocket // WebSocket
implementation 'org.springframework.boot:spring-boot-starter-websocket' implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'org.springframework.boot:spring-boot-starter-reactor-netty' implementation 'org.springframework.boot:spring-boot-starter-reactor-netty'

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -2,71 +2,18 @@ package com.unicorn.hgzero.meeting.infra.cache;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/** /**
* Redis 캐시 설정 * Redis 캐시 설정
*
* RedisConnectionFactory와 RedisTemplate은 RedisConfig에서 정의됨
*/ */
@Configuration @Configuration
@Slf4j @Slf4j
public class CacheConfig { public class CacheConfig {
@Value("${spring.data.redis.host:localhost}")
private String redisHost;
@Value("${spring.data.redis.port:6379}")
private int redisPort;
@Value("${spring.data.redis.password:}")
private String redisPassword;
@Value("${spring.data.redis.database:1}")
private int database;
/**
* Redis 연결 팩토리
*/
@Bean
public RedisConnectionFactory redisConnectionFactory() {
var factory = new LettuceConnectionFactory(redisHost, redisPort);
factory.setDatabase(database);
// 비밀번호가 설정된 경우에만 적용
if (redisPassword != null && !redisPassword.isEmpty()) {
factory.setPassword(redisPassword);
log.info("Redis 연결 설정 - host: {}, port: {}, database: {}, password: ****", redisHost, redisPort, database);
} else {
log.info("Redis 연결 설정 - host: {}, port: {}, database: {}", redisHost, redisPort, database);
}
return factory;
}
/**
* Redis 템플릿
*/
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, String> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// String 직렬화 설정
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new StringRedisSerializer());
template.afterPropertiesSet();
log.info("Redis 템플릿 설정 완료");
return template;
}
/** /**
* JSON 직렬화용 ObjectMapper * JSON 직렬화용 ObjectMapper
*/ */

View File

@ -0,0 +1,129 @@
package com.unicorn.hgzero.meeting.infra.config;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.ReadFrom;
import io.lettuce.core.SocketOptions;
import io.lettuce.core.TimeoutOptions;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
/**
* Redis 설정
* Master-Replica 구조에서 읽기/쓰기 분리 지원
*
* ReadFrom.MASTER 설정으로 모든 읽기/쓰기를 Master에서 수행
* Replica 연결 자동으로 Master로 리다이렉트 시도
*/
@Configuration
@Slf4j
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String redisHost;
@Value("${spring.data.redis.port}")
private int redisPort;
@Value("${spring.data.redis.password}")
private String redisPassword;
@Value("${spring.data.redis.database:0}")
private int redisDatabase;
/**
* Lettuce 클라이언트 설정
* - ReadFrom.MASTER: 모든 읽기/쓰기를 Master에서 수행
* - AutoReconnect: 연결 끊김 자동 재연결
* - DisconnectedBehavior.REJECT_COMMANDS: 연결 끊김 명령 거부
*/
@Bean
public LettuceClientConfiguration lettuceClientConfiguration() {
// 소켓 옵션 설정
SocketOptions socketOptions = SocketOptions.builder()
.connectTimeout(Duration.ofSeconds(10))
.keepAlive(true)
.build();
// 타임아웃 옵션 설정
TimeoutOptions timeoutOptions = TimeoutOptions.builder()
.fixedTimeout(Duration.ofSeconds(10))
.build();
// 클라이언트 옵션 설정
ClientOptions clientOptions = ClientOptions.builder()
.socketOptions(socketOptions)
.timeoutOptions(timeoutOptions)
.autoReconnect(true)
.disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
.build();
// Lettuce 클라이언트 설정
// ReadFrom.MASTER: 모든 작업을 Master에서 수행
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.clientOptions(clientOptions)
.readFrom(ReadFrom.MASTER) // 모든 읽기/쓰기를 Master에서 수행
.commandTimeout(Duration.ofSeconds(10))
.build();
log.info("Redis Lettuce Client 설정 완료 - ReadFrom: MASTER (모든 작업 Master에서 수행)");
return clientConfig;
}
/**
* LettuceConnectionFactory 설정
* Standalone 설정과 Lettuce Client 설정 결합
*/
@Bean
public LettuceConnectionFactory redisConnectionFactory(LettuceClientConfiguration lettuceClientConfiguration) {
// Standalone 설정
RedisStandaloneConfiguration standaloneConfig = new RedisStandaloneConfiguration();
standaloneConfig.setHostName(redisHost);
standaloneConfig.setPort(redisPort);
standaloneConfig.setPassword(redisPassword);
standaloneConfig.setDatabase(redisDatabase);
// LettuceConnectionFactory 생성
LettuceConnectionFactory factory = new LettuceConnectionFactory(standaloneConfig, lettuceClientConfiguration);
log.info("LettuceConnectionFactory 설정 완료 - Host: {}:{}, Database: {}",
redisHost, redisPort, redisDatabase);
return factory;
}
/**
* RedisTemplate 설정
* String Serializer 사용
*/
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, String> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// String Serializer 사용
StringRedisSerializer stringSerializer = new StringRedisSerializer();
template.setKeySerializer(stringSerializer);
template.setValueSerializer(stringSerializer);
template.setHashKeySerializer(stringSerializer);
template.setHashValueSerializer(stringSerializer);
template.afterPropertiesSet();
log.info("RedisTemplate 설정 완료");
return template;
}
}

View File

@ -58,7 +58,7 @@ public class EventHubPublisher implements EventPublisher {
@Override @Override
public void publishNotificationRequest(NotificationRequestEvent event) { public void publishNotificationRequest(NotificationRequestEvent event) {
publishEvent(event, event.getRecipientId(), publishEvent(event, event.getRecipientEmail(),
EventHubConstants.TOPIC_NOTIFICATION, EventHubConstants.TOPIC_NOTIFICATION,
EventHubConstants.EVENT_TYPE_NOTIFICATION_REQUEST); EventHubConstants.EVENT_TYPE_NOTIFICATION_REQUEST);
} }

View File

@ -40,7 +40,7 @@ public class NoOpEventPublisher implements EventPublisher {
@Override @Override
public void publishNotificationRequest(NotificationRequestEvent event) { public void publishNotificationRequest(NotificationRequestEvent event) {
log.debug("[NoOp] Notification request event: {}", event.getRecipientId()); log.debug("[NoOp] Notification request event: {}", event.getRecipientEmail());
} }
@Override @Override