diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/common/BaseEntity.kt b/src/main/kotlin/kr/co/vividnext/sodalive/common/BaseEntity.kt index bf2b11d..622dd1b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/common/BaseEntity.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/common/BaseEntity.kt @@ -1,5 +1,9 @@ package kr.co.vividnext.sodalive.common +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer import java.time.LocalDateTime import javax.persistence.GeneratedValue import javax.persistence.GenerationType @@ -14,7 +18,12 @@ abstract class BaseEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long? = null + @JsonSerialize(using = LocalDateTimeSerializer::class) + @JsonDeserialize(using = LocalDateTimeDeserializer::class) var createdAt: LocalDateTime? = null + + @JsonSerialize(using = LocalDateTimeSerializer::class) + @JsonDeserialize(using = LocalDateTimeDeserializer::class) var updatedAt: LocalDateTime? = null @PrePersist diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/configs/RedisConfig.kt b/src/main/kotlin/kr/co/vividnext/sodalive/configs/RedisConfig.kt index 22480ee..f309011 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/configs/RedisConfig.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/configs/RedisConfig.kt @@ -1,14 +1,22 @@ package kr.co.vividnext.sodalive.configs import org.springframework.beans.factory.annotation.Value +import org.springframework.cache.annotation.EnableCaching import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.data.redis.cache.RedisCacheConfiguration +import org.springframework.data.redis.cache.RedisCacheManager 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.repository.configuration.EnableRedisRepositories +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer +import org.springframework.data.redis.serializer.RedisSerializationContext +import org.springframework.data.redis.serializer.StringRedisSerializer +import java.time.Duration @Configuration +@EnableCaching @EnableRedisRepositories class RedisConfig( @Value("\${spring.redis.host}") @@ -27,4 +35,31 @@ class RedisConfig( redisTemplate.setConnectionFactory(redisConnectionFactory()) return redisTemplate } + + @Bean + fun cacheManager(connectionFactory: RedisConnectionFactory): RedisCacheManager { + val defaultConfig = RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofHours(1)) // Default TTL + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer())) + .serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer( + GenericJackson2JsonRedisSerializer() + ) + ) + + val weekLivedCacheConfig = RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofDays(3)) + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer())) + .serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer( + GenericJackson2JsonRedisSerializer() + ) + ) + + return RedisCacheManager.RedisCacheManagerBuilder + .fromConnectionFactory(connectionFactory) + .cacheDefaults(defaultConfig) + .withCacheConfiguration("weekLivedCache", weekLivedCacheConfig) + .build() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContent.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContent.kt index 116d8b4..2a622f0 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContent.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContent.kt @@ -42,7 +42,7 @@ data class AudioContent( var content: String? = null var coverImage: String? = null - @OneToOne(fetch = FetchType.LAZY) + @OneToOne(fetch = FetchType.EAGER) @JoinColumn(name = "theme_id", nullable = false) var theme: AudioContentTheme? = null diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt index 038f732..862f272 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt @@ -18,6 +18,7 @@ import kr.co.vividnext.sodalive.content.order.QOrder.order import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme import kr.co.vividnext.sodalive.event.QEvent.event import kr.co.vividnext.sodalive.member.QMember.member +import org.springframework.cache.annotation.Cacheable import org.springframework.data.jpa.repository.JpaRepository import org.springframework.stereotype.Repository import java.time.LocalDateTime @@ -336,6 +337,10 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) .fetch() } + @Cacheable( + value = ["getNewContentUploadCreatorList"], + cacheManager = "cacheManager" + ) override fun getNewContentUploadCreatorList( cloudfrontHost: String, isAdult: Boolean @@ -371,6 +376,10 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) .toList() } + @Cacheable( + value = ["getAudioContentMainBannerList"], + cacheManager = "cacheManager" + ) override fun getAudioContentMainBannerList(isAdult: Boolean): List { var where = audioContentBanner.isActive.isTrue @@ -387,6 +396,10 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) .fetch() } + @Cacheable( + value = ["getAudioContentCurations"], + cacheManager = "cacheManager" + ) override fun getAudioContentCurations(isAdult: Boolean): List { var where = audioContentCuration.isActive.isTrue @@ -438,6 +451,10 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) .fetch() } + @Cacheable( + value = ["weekLivedCache"], + cacheManager = "cacheManager" + ) override fun getAudioContentRanking( cloudfrontHost: String, isAdult: Boolean, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentRanking.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentRanking.kt index 1d9dbdf..3209e71 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentRanking.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentRanking.kt @@ -1,5 +1,6 @@ package kr.co.vividnext.sodalive.content.main +import com.fasterxml.jackson.annotation.JsonProperty import com.querydsl.core.annotations.QueryProjection data class GetAudioContentRanking( @@ -9,12 +10,12 @@ data class GetAudioContentRanking( ) data class GetAudioContentRankingItem @QueryProjection constructor( - val contentId: Long, - val title: String, - val coverImageUrl: String, - val themeStr: String, - val price: Int, - val duration: String, - val creatorId: Long, - val creatorNickname: String + @JsonProperty("contentId") val contentId: Long, + @JsonProperty("title") val title: String, + @JsonProperty("coverImageUrl") val coverImageUrl: String, + @JsonProperty("themeStr") val themeStr: String, + @JsonProperty("price") val price: Int, + @JsonProperty("duration") val duration: String, + @JsonProperty("creatorId") val creatorId: Long, + @JsonProperty("creatorNickname") val creatorNickname: String ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetNewContentUploadCreator.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetNewContentUploadCreator.kt index 55a50a7..a53d1d3 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetNewContentUploadCreator.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetNewContentUploadCreator.kt @@ -1,9 +1,10 @@ package kr.co.vividnext.sodalive.content.main +import com.fasterxml.jackson.annotation.JsonProperty import com.querydsl.core.annotations.QueryProjection data class GetNewContentUploadCreator @QueryProjection constructor( - val creatorId: Long, - val creatorNickname: String, - val creatorProfileImageUrl: String + @JsonProperty("creatorId") val creatorId: Long, + @JsonProperty("creatorNickname") val creatorNickname: String, + @JsonProperty("creatorProfileImageUrl") val creatorProfileImageUrl: String ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCuration.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCuration.kt index 83ec820..d5955cf 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCuration.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCuration.kt @@ -1,10 +1,8 @@ package kr.co.vividnext.sodalive.content.main.curation import kr.co.vividnext.sodalive.common.BaseEntity -import kr.co.vividnext.sodalive.content.AudioContent import javax.persistence.Column import javax.persistence.Entity -import javax.persistence.OneToMany import javax.persistence.Table @Entity @@ -20,7 +18,4 @@ data class AudioContentCuration( var isActive: Boolean = true, @Column(nullable = false) var orders: Int = 1 -) : BaseEntity() { - @OneToMany(mappedBy = "curation") - val audioContents: MutableList = mutableListOf() -} +) : BaseEntity() diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/event/EventService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/event/EventService.kt index e6ddd74..2237d7b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/event/EventService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/event/EventService.kt @@ -5,6 +5,7 @@ import kr.co.vividnext.sodalive.aws.s3.S3Uploader import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.utils.generateFileName import org.springframework.beans.factory.annotation.Value +import org.springframework.cache.annotation.Cacheable import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -20,6 +21,10 @@ class EventService( @Value("\${cloud.aws.cloud-front.host}") private val cloudFrontHost: String ) { + @Cacheable( + value = ["getEventList"], + cacheManager = "cacheManager" + ) fun getEventList(): GetEventResponse { val eventList = repository.getEventList() .asSequence() diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/event/GetEventResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/event/GetEventResponse.kt index 5a0b6ee..4e3b711 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/event/GetEventResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/event/GetEventResponse.kt @@ -1,18 +1,21 @@ package kr.co.vividnext.sodalive.event +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonProperty import com.querydsl.core.annotations.QueryProjection data class GetEventResponse( - val totalCount: Int, - val eventList: List + @JsonProperty("totalCount") val totalCount: Int, + @JsonProperty("eventList") val eventList: List ) +@JsonIgnoreProperties(ignoreUnknown = true) data class EventItem @QueryProjection constructor( - val id: Long, - val title: String? = null, - var thumbnailImageUrl: String, - var detailImageUrl: String? = null, - var popupImageUrl: String? = null, - val link: String? = null, - val isPopup: Boolean + @JsonProperty("id") val id: Long, + @JsonProperty("title") val title: String? = null, + @JsonProperty("thumbnailImageUrl") var thumbnailImageUrl: String, + @JsonProperty("detailImageUrl") var detailImageUrl: String? = null, + @JsonProperty("popupImageUrl") var popupImageUrl: String? = null, + @JsonProperty("link") val link: String? = null, + @JsonProperty("isPopup") val isPopup: Boolean ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/recommend/GetRecommendLiveResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/recommend/GetRecommendLiveResponse.kt index 97f21fe..2b7e2f8 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/recommend/GetRecommendLiveResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/recommend/GetRecommendLiveResponse.kt @@ -1,6 +1,8 @@ package kr.co.vividnext.sodalive.live.recommend +import com.fasterxml.jackson.annotation.JsonProperty + data class GetRecommendLiveResponse( - val imageUrl: String, - val creatorId: Long + @JsonProperty("imageUrl") val imageUrl: String, + @JsonProperty("creatorId") val creatorId: Long ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/recommend/LiveRecommendRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/recommend/LiveRecommendRepository.kt index e6ece32..b65eecb 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/recommend/LiveRecommendRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/recommend/LiveRecommendRepository.kt @@ -9,6 +9,7 @@ import kr.co.vividnext.sodalive.member.MemberRole import kr.co.vividnext.sodalive.member.QMember.member import kr.co.vividnext.sodalive.member.following.QCreatorFollowing.creatorFollowing import org.springframework.beans.factory.annotation.Value +import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Repository import java.time.LocalDateTime @@ -19,6 +20,10 @@ class LiveRecommendRepository( @Value("\${cloud.aws.cloud-front.host}") private val cloudFrontHost: String ) { + @Cacheable( + value = ["getRecommendLive"], + cacheManager = "cacheManager" + ) fun getRecommendLive( memberId: Long, isBlocked: (Long) -> Boolean, diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4dd8bb8..3be6e79 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -73,6 +73,8 @@ spring: multipart: max-file-size: 1024MB max-request-size: 1024MB + cache: + type: redis --- spring: config: diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index c831d44..6639ad7 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -47,3 +47,5 @@ spring: hibernate: show_sql: true format_sql: true + cache: + type: redis