From 2bbfaa9dda81822f01c33e9856a8c29936379ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=88=EC=A7=80?= Date: Wed, 5 Feb 2025 13:10:17 +0900 Subject: [PATCH 1/4] =?UTF-8?q?refactor:=20Redis=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EB=B2=A0=EC=9D=B4=EC=8A=A4=20=EB=85=BC=EB=A6=AC?= =?UTF-8?q?=EC=A0=81=20=EB=B6=84=EB=A6=AC=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../domain/board/service/BoardService.java | 14 ++- .../domain/coupon/service/CouponService.java | 28 ++++-- .../service/NotificationService.java | 21 ++++- .../service/RedisStreamService.java | 15 ++- .../gamemate/global/config/RedisConfig.java | 91 ++++++++++++++++++- 6 files changed, 148 insertions(+), 22 deletions(-) diff --git a/build.gradle b/build.gradle index 342a21a..1bb04d1 100644 --- a/build.gradle +++ b/build.gradle @@ -71,6 +71,7 @@ dependencies { // Redis implementation 'org.springframework.boot:spring-boot-starter-data-redis' + implementation 'org.redisson:redisson-spring-boot-starter:3.27.1' } tasks.named('test') { diff --git a/src/main/java/com/example/gamemate/domain/board/service/BoardService.java b/src/main/java/com/example/gamemate/domain/board/service/BoardService.java index 901eff2..5f9178f 100644 --- a/src/main/java/com/example/gamemate/domain/board/service/BoardService.java +++ b/src/main/java/com/example/gamemate/domain/board/service/BoardService.java @@ -14,11 +14,13 @@ import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -32,15 +34,23 @@ @Slf4j @Service -@RequiredArgsConstructor public class BoardService { private final BoardRepository boardRepository; private final String VIEW_COUNT_KEY = "board:view:"; private final String VIEW_RANKING_KEY = "board:ranking:"; - private final RedisTemplate redisTemplate; + private final StringRedisTemplate redisTemplate; private final HttpServletRequest request; + public BoardService( + BoardRepository boardRepository, + @Qualifier("viewCountRedisTemplate")StringRedisTemplate redisTemplate, + HttpServletRequest request) { + this.boardRepository = boardRepository; + this.redisTemplate = redisTemplate; + this.request = request; + } + /** * 게시글 생성 메서드입니다. * diff --git a/src/main/java/com/example/gamemate/domain/coupon/service/CouponService.java b/src/main/java/com/example/gamemate/domain/coupon/service/CouponService.java index 8cb9c80..6d90825 100644 --- a/src/main/java/com/example/gamemate/domain/coupon/service/CouponService.java +++ b/src/main/java/com/example/gamemate/domain/coupon/service/CouponService.java @@ -12,6 +12,7 @@ import com.example.gamemate.global.constant.ErrorCode; import com.example.gamemate.global.exception.ApiException; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -22,16 +23,21 @@ @Service @Transactional -@RequiredArgsConstructor public class CouponService { + + private static final String COUPON_STOCK_KEY = "coupon:%d:stock"; + private final CouponRepository couponRepository; private final UserCouponRepository userCouponRepository; private final StringRedisTemplate redisTemplate; - private static final String COUPON_STOCK_KEY = "coupon:%d:stock"; - - private String getCouponStockKey(Long couponId) { - return String.format(COUPON_STOCK_KEY, couponId); + public CouponService( + CouponRepository couponRepository, + UserCouponRepository userCouponRepository, + @Qualifier("couponRedisTemplate") StringRedisTemplate redisTemplate) { + this.couponRepository = couponRepository; + this.userCouponRepository = userCouponRepository; + this.redisTemplate = redisTemplate; } public CouponCreateResponseDto createCoupon(CouponCreateRequestDto requestDto, User loginUser) { @@ -59,6 +65,12 @@ public CouponCreateResponseDto createCoupon(CouponCreateRequestDto requestDto, U return new CouponCreateResponseDto(savedCoupon); } + private void validateCouponDates(LocalDateTime startAt, LocalDateTime expiredAt) { + if (startAt.isAfter(expiredAt)) { + throw new ApiException(ErrorCode.INVALID_COUPON_DATE); + } + } + public CouponIssueResponseDto issueCoupon(Long couponId, User loginUser) { Coupon coupon = couponRepository.findById(couponId) .orElseThrow(() -> new ApiException(ErrorCode.COUPON_NOT_FOUND)); @@ -131,9 +143,7 @@ public void useCoupon(Long userCouponId, User loginUser) { userCoupon.updateUsedAt(); } - private void validateCouponDates(LocalDateTime startAt, LocalDateTime expiredAt) { - if (startAt.isAfter(expiredAt)) { - throw new ApiException(ErrorCode.INVALID_COUPON_DATE); - } + private String getCouponStockKey(Long couponId) { + return String.format(COUPON_STOCK_KEY, couponId); } } diff --git a/src/main/java/com/example/gamemate/domain/notification/service/NotificationService.java b/src/main/java/com/example/gamemate/domain/notification/service/NotificationService.java index e413883..518391a 100644 --- a/src/main/java/com/example/gamemate/domain/notification/service/NotificationService.java +++ b/src/main/java/com/example/gamemate/domain/notification/service/NotificationService.java @@ -12,6 +12,8 @@ import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.stream.StreamInfo; import org.springframework.data.redis.connection.stream.StreamRecords; import org.springframework.data.redis.core.RedisTemplate; @@ -25,17 +27,28 @@ * 알림을 처리하는 서비스 클래스입니다. */ @Service -@RequiredArgsConstructor @Slf4j public class NotificationService { + private static final String STREAM_KEY = "notification_stream"; + private static final String GROUP_NAME = "notification-group"; + private static final Long DEFAULT_TIMEOUT = 60L * 1000 * 60; + private final NotificationRepository notificationRepository; private final EmitterRepository emitterRepository; private final RedisStreamService redisStreamService; private final RedisTemplate redisTemplate; - private static final String STREAM_KEY = "notification_stream"; - private static final String GROUP_NAME = "notification-group"; - private static final Long DEFAULT_TIMEOUT = 60L * 1000 * 60; + + public NotificationService( + NotificationRepository notificationRepository, + EmitterRepository emitterRepository, + RedisStreamService redisStreamService, + @Qualifier("notificationRedisTemplate") RedisTemplate redisTemplate) { + this.notificationRepository = notificationRepository; + this.emitterRepository = emitterRepository; + this.redisStreamService = redisStreamService; + this.redisTemplate = redisTemplate; + } /** * 레디스 스트림의 스트림그룹을 생성합니다. diff --git a/src/main/java/com/example/gamemate/domain/notification/service/RedisStreamService.java b/src/main/java/com/example/gamemate/domain/notification/service/RedisStreamService.java index f272e9d..13bf2de 100644 --- a/src/main/java/com/example/gamemate/domain/notification/service/RedisStreamService.java +++ b/src/main/java/com/example/gamemate/domain/notification/service/RedisStreamService.java @@ -5,6 +5,7 @@ import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.stream.*; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; @@ -16,19 +17,27 @@ import java.util.*; @Service -@RequiredArgsConstructor @Slf4j public class RedisStreamService { - private final RedisTemplate redisTemplate; - private final EmitterRepository emitterRepository; private static final String STREAM_KEY = "notification_stream"; private static final String GROUP_NAME = "notification-group"; + private static final String CONSUMER_PREFIX = "consumer"; private static final int BATCH_SIZE = 100; private static final Duration POLL_TIMEOUT = Duration.ofMillis(100); private static final int MAX_STREAM_LENGTH = 1000; + private final RedisTemplate redisTemplate; + private final EmitterRepository emitterRepository; + + public RedisStreamService( + @Qualifier("notificationRedisTemplate") RedisTemplate redisTemplate, + EmitterRepository emitterRepository) { + this.redisTemplate = redisTemplate; + this.emitterRepository = emitterRepository; + } + @PostConstruct public void init() { createStreamGroup(); diff --git a/src/main/java/com/example/gamemate/global/config/RedisConfig.java b/src/main/java/com/example/gamemate/global/config/RedisConfig.java index 59f3729..f07d0c1 100644 --- a/src/main/java/com/example/gamemate/global/config/RedisConfig.java +++ b/src/main/java/com/example/gamemate/global/config/RedisConfig.java @@ -1,25 +1,108 @@ package com.example.gamemate.global.config; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.springframework.beans.factory.annotation.Qualifier; +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.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig { + @Value("${spring.data.redis.host}") + private String redisHost; + + @Value("${spring.data.redis.port}") + private int redisPort; + + // LettuceConnectionFactory를 DB별로 하나씩 생성하여 Bean으로 관리 + // DB 0: 알림 + @Bean + public LettuceConnectionFactory notificationConnectionFactory() { + return createLettuceConnectionFactory(0); + } + + // DB 1: 조회수 @Bean - public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + public LettuceConnectionFactory viewCountConnectionFactory() { + return createLettuceConnectionFactory(1); + } + + // DB 2: 리프레시 토큰 + @Bean + public LettuceConnectionFactory refreshTokenConnectionFactory() { + return createLettuceConnectionFactory(2); + } + + // DB 3: 토큰 블랙리스트 + @Bean + public LettuceConnectionFactory tokenBlacklistConnectionFactory() { + return createLettuceConnectionFactory(3); + } + + // DB 4: 쿠폰 + @Bean + public LettuceConnectionFactory couponConnectionFactory() { + return createLettuceConnectionFactory(4); + } + + // 공통: LettuceConnectionFactory 생성 + private LettuceConnectionFactory createLettuceConnectionFactory(int database) { + RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(); + configuration.setHostName(redisHost); + configuration.setPort(redisPort); + configuration.setDatabase(database); + LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(configuration); + connectionFactory.afterPropertiesSet(); + return connectionFactory; + } + + // 알림 RedisTemplate (DB 0) + @Bean + public RedisTemplate notificationRedisTemplate( + @Qualifier("notificationConnectionFactory") LettuceConnectionFactory connectionFactory) { RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(connectionFactory); - - // String 타입을 위한 직렬화 설정 redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new StringRedisSerializer()); - return redisTemplate; } + + // 조회수 RedisTemplate (DB 1) + @Bean + public StringRedisTemplate viewCountRedisTemplate( + @Qualifier("viewCountConnectionFactory") LettuceConnectionFactory connectionFactory) { + return new StringRedisTemplate(connectionFactory); + } + + // 리프레시 토큰 RedisTemplate (DB 2) + @Bean + public StringRedisTemplate refreshTokenRedisTemplate( + @Qualifier("refreshTokenConnectionFactory") LettuceConnectionFactory connectionFactory) { + return new StringRedisTemplate(connectionFactory); + } + + // 토큰 블랙리스트 RedisTemplate (DB 3) + @Bean + public StringRedisTemplate tokenBlacklistRedisTemplate( + @Qualifier("tokenBlacklistConnectionFactory") LettuceConnectionFactory connectionFactory) { + return new StringRedisTemplate(connectionFactory); + } + + // 쿠폰 RedisTemplate (DB 4) + @Bean + public StringRedisTemplate couponRedisTemplate( + @Qualifier("couponConnectionFactory") LettuceConnectionFactory connectionFactory) { + return new StringRedisTemplate(connectionFactory); + } } \ No newline at end of file From 74dc863a92a306b05fdd8a93f3f0435265bc133f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=88=EC=A7=80?= Date: Wed, 5 Feb 2025 15:14:33 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20RedisConfig=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 - .../service/NotificationService.java | 4 +--- .../service/RedisStreamService.java | 3 +-- .../global/config/CouponDataSynchronizer.java | 21 ++++++++++++++----- .../gamemate/global/config/RedisConfig.java | 14 ++++++------- .../application-noreactive.properties | 1 + src/main/resources/application.properties | 6 +++++- 7 files changed, 31 insertions(+), 19 deletions(-) create mode 100644 src/main/resources/application-noreactive.properties diff --git a/build.gradle b/build.gradle index 1bb04d1..342a21a 100644 --- a/build.gradle +++ b/build.gradle @@ -71,7 +71,6 @@ dependencies { // Redis implementation 'org.springframework.boot:spring-boot-starter-data-redis' - implementation 'org.redisson:redisson-spring-boot-starter:3.27.1' } tasks.named('test') { diff --git a/src/main/java/com/example/gamemate/domain/notification/service/NotificationService.java b/src/main/java/com/example/gamemate/domain/notification/service/NotificationService.java index 518391a..3c162fc 100644 --- a/src/main/java/com/example/gamemate/domain/notification/service/NotificationService.java +++ b/src/main/java/com/example/gamemate/domain/notification/service/NotificationService.java @@ -10,9 +10,7 @@ import com.example.gamemate.global.exception.ApiException; import jakarta.annotation.PostConstruct; import jakarta.transaction.Transactional; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.stream.StreamInfo; import org.springframework.data.redis.connection.stream.StreamRecords; @@ -43,7 +41,7 @@ public NotificationService( NotificationRepository notificationRepository, EmitterRepository emitterRepository, RedisStreamService redisStreamService, - @Qualifier("notificationRedisTemplate") RedisTemplate redisTemplate) { + @Qualifier("redisTemplate") RedisTemplate redisTemplate) { this.notificationRepository = notificationRepository; this.emitterRepository = emitterRepository; this.redisStreamService = redisStreamService; diff --git a/src/main/java/com/example/gamemate/domain/notification/service/RedisStreamService.java b/src/main/java/com/example/gamemate/domain/notification/service/RedisStreamService.java index 13bf2de..d153941 100644 --- a/src/main/java/com/example/gamemate/domain/notification/service/RedisStreamService.java +++ b/src/main/java/com/example/gamemate/domain/notification/service/RedisStreamService.java @@ -3,7 +3,6 @@ import com.example.gamemate.domain.notification.dto.NotificationResponseDto; import com.example.gamemate.domain.notification.repository.EmitterRepository; import jakarta.annotation.PostConstruct; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.stream.*; @@ -32,7 +31,7 @@ public class RedisStreamService { private final EmitterRepository emitterRepository; public RedisStreamService( - @Qualifier("notificationRedisTemplate") RedisTemplate redisTemplate, + @Qualifier("redisTemplate") RedisTemplate redisTemplate, EmitterRepository emitterRepository) { this.redisTemplate = redisTemplate; this.emitterRepository = emitterRepository; diff --git a/src/main/java/com/example/gamemate/global/config/CouponDataSynchronizer.java b/src/main/java/com/example/gamemate/global/config/CouponDataSynchronizer.java index 2e670ec..290dda0 100644 --- a/src/main/java/com/example/gamemate/global/config/CouponDataSynchronizer.java +++ b/src/main/java/com/example/gamemate/global/config/CouponDataSynchronizer.java @@ -4,6 +4,7 @@ import com.example.gamemate.domain.coupon.repository.CouponRepository; import com.example.gamemate.domain.coupon.repository.UserCouponRepository; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; import org.springframework.data.redis.core.StringRedisTemplate; @@ -12,16 +13,21 @@ import java.util.List; @Component -@RequiredArgsConstructor public class CouponDataSynchronizer { + + private static final String COUPON_STOCK_KEY = "coupon:%d:stock"; + private final CouponRepository couponRepository; private final UserCouponRepository userCouponRepository; private final StringRedisTemplate redisTemplate; - private static final String COUPON_STOCK_KEY = "coupon:%d:stock"; - - private String getCouponStockKey(Long couponId) { - return String.format(COUPON_STOCK_KEY, couponId); + public CouponDataSynchronizer( + CouponRepository couponRepository, + UserCouponRepository userCouponRepository, + @Qualifier("couponRedisTemplate") StringRedisTemplate redisTemplate) { + this.couponRepository = couponRepository; + this.userCouponRepository = userCouponRepository; + this.redisTemplate = redisTemplate; } @EventListener(ApplicationReadyEvent.class) @@ -34,4 +40,9 @@ public void syncCouponStock() { String.valueOf(remainingStock)); } } + + + private String getCouponStockKey(Long couponId) { + return String.format(COUPON_STOCK_KEY, couponId); + } } \ No newline at end of file diff --git a/src/main/java/com/example/gamemate/global/config/RedisConfig.java b/src/main/java/com/example/gamemate/global/config/RedisConfig.java index f07d0c1..016c0f2 100644 --- a/src/main/java/com/example/gamemate/global/config/RedisConfig.java +++ b/src/main/java/com/example/gamemate/global/config/RedisConfig.java @@ -1,17 +1,15 @@ package com.example.gamemate.global.config; -import org.redisson.Redisson; -import org.redisson.api.RedissonClient; -import org.redisson.config.Config; import org.springframework.beans.factory.annotation.Qualifier; 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.context.annotation.Primary; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @@ -26,6 +24,7 @@ public class RedisConfig { // LettuceConnectionFactory를 DB별로 하나씩 생성하여 Bean으로 관리 // DB 0: 알림 @Bean + @Primary public LettuceConnectionFactory notificationConnectionFactory() { return createLettuceConnectionFactory(0); } @@ -67,14 +66,15 @@ private LettuceConnectionFactory createLettuceConnectionFactory(int database) { // 알림 RedisTemplate (DB 0) @Bean - public RedisTemplate notificationRedisTemplate( + @Primary + public RedisTemplate redisTemplate( @Qualifier("notificationConnectionFactory") LettuceConnectionFactory connectionFactory) { RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(connectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer()); - redisTemplate.setValueSerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); - redisTemplate.setHashValueSerializer(new StringRedisSerializer()); + redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); return redisTemplate; } diff --git a/src/main/resources/application-noreactive.properties b/src/main/resources/application-noreactive.properties new file mode 100644 index 0000000..29df9aa --- /dev/null +++ b/src/main/resources/application-noreactive.properties @@ -0,0 +1 @@ +spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e9035a0..e85e6f6 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,6 @@ spring.application.name=gamemate -spring.config.import=optional:file:.env[.properties] +spring.config.import=optional:file:.env[.properties],optional:classpath:/application-noreactive.properties spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=${MYSQL_URL} @@ -67,6 +67,10 @@ logging.level.org.springframework.web.servlet=DEBUG logging.level.org.springframework.security.oauth2=TRACE logging.level.org.springframework.web.client=TRACE logging.level.com.example.gamemate=DEBUG +logging.level.org.springframework.data.redis=DEBUG +logging.level.com.example.gamemate.domain.notification.service.RedisStreamService=DEBUG +logging.level.org.springframework.data.redis.core=DEBUG +logging.level.io.lettuce.core=DEBUG # Gemini gemini.api.url=${GEMINI_URL} From 617f424abda2b9fd72a43c3c3eac075182a2e8b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=88=EC=A7=80?= Date: Wed, 5 Feb 2025 18:32:42 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20RedisConfig=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=9E=AC=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gamemate/global/config/RedisConfig.java | 119 ++++++++++-------- src/main/resources/application.properties | 12 +- 2 files changed, 69 insertions(+), 62 deletions(-) diff --git a/src/main/java/com/example/gamemate/global/config/RedisConfig.java b/src/main/java/com/example/gamemate/global/config/RedisConfig.java index ea46df7..1c914a4 100644 --- a/src/main/java/com/example/gamemate/global/config/RedisConfig.java +++ b/src/main/java/com/example/gamemate/global/config/RedisConfig.java @@ -1,10 +1,9 @@ package com.example.gamemate.global.config; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; +import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; @@ -15,94 +14,104 @@ @Configuration public class RedisConfig { - @Value("${spring.data.redis.host}") - private String redisHost; - - @Value("${spring.data.redis.port}") - private int redisPort; + // 기본 RedisConnectionFactory + @Bean + @Primary + public RedisConnectionFactory redisConnectionFactory() { + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); + config.setDatabase(0); + return new LettuceConnectionFactory(config); + } - // LettuceConnectionFactory를 DB별로 하나씩 생성하여 Bean으로 관리 - // DB 0: 알림 + // 기본 RedisTemplate @Bean @Primary - public LettuceConnectionFactory notificationConnectionFactory() { - return createLettuceConnectionFactory(0); + public RedisTemplate redisTemplate() { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(redisConnectionFactory()); + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); + return template; } - // DB 1: 조회수 + // DB 1: 알림 @Bean - public LettuceConnectionFactory viewCountConnectionFactory() { - return createLettuceConnectionFactory(1); + public RedisConnectionFactory notificationConnectionFactory() { + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); + config.setDatabase(1); + return new LettuceConnectionFactory(config); } - // DB 2: 리프레시 토큰 + // DB 2: 조회수 @Bean - public LettuceConnectionFactory refreshTokenConnectionFactory() { - return createLettuceConnectionFactory(2); + public RedisConnectionFactory viewCountConnectionFactory() { + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); + config.setDatabase(2); + return new LettuceConnectionFactory(config); } - // DB 3: 토큰 블랙리스트 + // DB 3: 리프레시 토큰 @Bean - public LettuceConnectionFactory tokenBlacklistConnectionFactory() { - return createLettuceConnectionFactory(3); + public RedisConnectionFactory refreshTokenConnectionFactory() { + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); + config.setDatabase(3); + return new LettuceConnectionFactory(config); } - // DB 4: 쿠폰 + // DB 4: 토큰 블랙리스트 @Bean - public LettuceConnectionFactory couponConnectionFactory() { - return createLettuceConnectionFactory(4); + public RedisConnectionFactory blacklistConnectionFactory() { + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); + config.setDatabase(4); + return new LettuceConnectionFactory(config); } - // 공통: LettuceConnectionFactory 생성 - private LettuceConnectionFactory createLettuceConnectionFactory(int database) { - RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(); - configuration.setHostName(redisHost); - configuration.setPort(redisPort); - configuration.setDatabase(database); - LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(configuration); - connectionFactory.afterPropertiesSet(); - return connectionFactory; + // DB 5: 쿠폰 + @Bean + public RedisConnectionFactory couponConnectionFactory() { + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); + config.setDatabase(5); + return new LettuceConnectionFactory(config); } - // 알림 RedisTemplate (DB 0) + // 알림 RedisTemplate (DB 1) @Bean - @Primary - public RedisTemplate notificationRedisTemplate( - @Qualifier("notificationConnectionFactory") LettuceConnectionFactory connectionFactory) { + public RedisTemplate notificationRedisTemplate() { RedisTemplate redisTemplate = new RedisTemplate<>(); - redisTemplate.setConnectionFactory(connectionFactory); + redisTemplate.setConnectionFactory(notificationConnectionFactory()); + + // String 타입을 위한 직렬화 설정 redisTemplate.setKeySerializer(new StringRedisSerializer()); - redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); + redisTemplate.setValueSerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); - redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class)); + redisTemplate.setHashValueSerializer(new StringRedisSerializer()); + return redisTemplate; } - // 조회수 RedisTemplate (DB 1) + // 조회수 RedisTemplate (DB 2) @Bean - public StringRedisTemplate viewCountRedisTemplate( - @Qualifier("viewCountConnectionFactory") LettuceConnectionFactory connectionFactory) { - return new StringRedisTemplate(connectionFactory); + public StringRedisTemplate viewCountRedisTemplate() { + return new StringRedisTemplate(viewCountConnectionFactory()); } - // 리프레시 토큰 RedisTemplate (DB 2) + // 리프레시 토큰 RedisTemplate (DB 3) @Bean - public StringRedisTemplate refreshTokenRedisTemplate( - @Qualifier("refreshTokenConnectionFactory") LettuceConnectionFactory connectionFactory) { - return new StringRedisTemplate(connectionFactory); + public StringRedisTemplate refreshTokenRedisTemplate() { + return new StringRedisTemplate(refreshTokenConnectionFactory()); } - // 토큰 블랙리스트 RedisTemplate (DB 3) + // 토큰 블랙리스트 RedisTemplate (DB 4) @Bean - public StringRedisTemplate tokenBlacklistRedisTemplate( - @Qualifier("tokenBlacklistConnectionFactory") LettuceConnectionFactory connectionFactory) { - return new StringRedisTemplate(connectionFactory); + public StringRedisTemplate blacklistRedisTemplate() { + return new StringRedisTemplate(blacklistConnectionFactory()); } - // 쿠폰 RedisTemplate (DB 4) + // 쿠폰 RedisTemplate (DB 5) @Bean - public StringRedisTemplate couponRedisTemplate( - @Qualifier("couponConnectionFactory") LettuceConnectionFactory connectionFactory) { - return new StringRedisTemplate(connectionFactory); + public StringRedisTemplate couponRedisTemplate() { + return new StringRedisTemplate(couponConnectionFactory()); } } \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 06c97e8..5556976 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -62,15 +62,13 @@ spring.mail.properties.mail.smtp.starttls.enable=true # DEBUG logging.level.org.springframework.security=DEBUG +logging.level.org.springframework.security.oauth2=TRACE logging.level.org.springframework.web=DEBUG logging.level.org.springframework.web.servlet=DEBUG -logging.level.org.springframework.security.oauth2=TRACE -logging.level.org.springframework.web.client=TRACE +logging.level.org.springframework.web.client=INFO logging.level.com.example.gamemate=DEBUG -logging.level.org.springframework.data.redis=DEBUG -logging.level.com.example.gamemate.domain.notification.service.RedisStreamService=DEBUG -logging.level.org.springframework.data.redis.core=DEBUG -logging.level.io.lettuce.core=DEBUG +logging.level.org.springframework.data.redis=INFO +logging.level.com.example.gamemate.domain.notification.service.RedisStreamService=INFO # Gemini gemini.api.url=${GEMINI_URL} @@ -96,4 +94,4 @@ spring.jpa.properties.jakarta.persistence.lock.timeout=3000 # Redis spring.data.redis.host=${REDIS_HOST} -spring.data.redis.port=6379 +spring.data.redis.port=6379 \ No newline at end of file From 26f3492ceb16535a2210118f3f6c14c51ffb54ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=88=EC=A7=80?= Date: Wed, 5 Feb 2025 18:43:29 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20properties=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-noreactive.properties | 1 - src/main/resources/application.properties | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 src/main/resources/application-noreactive.properties diff --git a/src/main/resources/application-noreactive.properties b/src/main/resources/application-noreactive.properties deleted file mode 100644 index 29df9aa..0000000 --- a/src/main/resources/application-noreactive.properties +++ /dev/null @@ -1 +0,0 @@ -spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f8244b9..8857447 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,6 @@ spring.application.name=gamemate -spring.config.import=optional:file:.env[.properties],optional:classpath:/application-noreactive.properties +spring.config.import=optional:file:.env[.properties] # Environment (prod, dev) spring.profiles.active=dev