Merge pull request '로딩 속도를 위해 @Cacheable 적용' (#50) from test into main

Reviewed-on: #50
This commit is contained in:
klaus 2023-10-17 09:31:08 +00:00
commit 4aff0111aa
13 changed files with 106 additions and 29 deletions

View File

@ -1,5 +1,9 @@
package kr.co.vividnext.sodalive.common 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 java.time.LocalDateTime
import javax.persistence.GeneratedValue import javax.persistence.GeneratedValue
import javax.persistence.GenerationType import javax.persistence.GenerationType
@ -14,7 +18,12 @@ abstract class BaseEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null var id: Long? = null
@JsonSerialize(using = LocalDateTimeSerializer::class)
@JsonDeserialize(using = LocalDateTimeDeserializer::class)
var createdAt: LocalDateTime? = null var createdAt: LocalDateTime? = null
@JsonSerialize(using = LocalDateTimeSerializer::class)
@JsonDeserialize(using = LocalDateTimeDeserializer::class)
var updatedAt: LocalDateTime? = null var updatedAt: LocalDateTime? = null
@PrePersist @PrePersist

View File

@ -1,14 +1,22 @@
package kr.co.vividnext.sodalive.configs package kr.co.vividnext.sodalive.configs
import org.springframework.beans.factory.annotation.Value import org.springframework.beans.factory.annotation.Value
import org.springframework.cache.annotation.EnableCaching
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.cache.RedisCacheConfiguration
import org.springframework.data.redis.cache.RedisCacheManager
import org.springframework.data.redis.connection.RedisConnectionFactory import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
import org.springframework.data.redis.core.RedisTemplate import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories 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 @Configuration
@EnableCaching
@EnableRedisRepositories @EnableRedisRepositories
class RedisConfig( class RedisConfig(
@Value("\${spring.redis.host}") @Value("\${spring.redis.host}")
@ -27,4 +35,31 @@ class RedisConfig(
redisTemplate.setConnectionFactory(redisConnectionFactory()) redisTemplate.setConnectionFactory(redisConnectionFactory())
return redisTemplate 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()
}
} }

View File

@ -42,7 +42,7 @@ data class AudioContent(
var content: String? = null var content: String? = null
var coverImage: String? = null var coverImage: String? = null
@OneToOne(fetch = FetchType.LAZY) @OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "theme_id", nullable = false) @JoinColumn(name = "theme_id", nullable = false)
var theme: AudioContentTheme? = null var theme: AudioContentTheme? = null

View File

@ -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.content.theme.QAudioContentTheme.audioContentTheme
import kr.co.vividnext.sodalive.event.QEvent.event import kr.co.vividnext.sodalive.event.QEvent.event
import kr.co.vividnext.sodalive.member.QMember.member import kr.co.vividnext.sodalive.member.QMember.member
import org.springframework.cache.annotation.Cacheable
import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
import java.time.LocalDateTime import java.time.LocalDateTime
@ -336,6 +337,10 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
.fetch() .fetch()
} }
@Cacheable(
value = ["getNewContentUploadCreatorList"],
cacheManager = "cacheManager"
)
override fun getNewContentUploadCreatorList( override fun getNewContentUploadCreatorList(
cloudfrontHost: String, cloudfrontHost: String,
isAdult: Boolean isAdult: Boolean
@ -371,6 +376,10 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
.toList() .toList()
} }
@Cacheable(
value = ["getAudioContentMainBannerList"],
cacheManager = "cacheManager"
)
override fun getAudioContentMainBannerList(isAdult: Boolean): List<AudioContentBanner> { override fun getAudioContentMainBannerList(isAdult: Boolean): List<AudioContentBanner> {
var where = audioContentBanner.isActive.isTrue var where = audioContentBanner.isActive.isTrue
@ -387,6 +396,10 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
.fetch() .fetch()
} }
@Cacheable(
value = ["getAudioContentCurations"],
cacheManager = "cacheManager"
)
override fun getAudioContentCurations(isAdult: Boolean): List<AudioContentCuration> { override fun getAudioContentCurations(isAdult: Boolean): List<AudioContentCuration> {
var where = audioContentCuration.isActive.isTrue var where = audioContentCuration.isActive.isTrue
@ -438,6 +451,10 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
.fetch() .fetch()
} }
@Cacheable(
value = ["weekLivedCache"],
cacheManager = "cacheManager"
)
override fun getAudioContentRanking( override fun getAudioContentRanking(
cloudfrontHost: String, cloudfrontHost: String,
isAdult: Boolean, isAdult: Boolean,

View File

@ -1,5 +1,6 @@
package kr.co.vividnext.sodalive.content.main package kr.co.vividnext.sodalive.content.main
import com.fasterxml.jackson.annotation.JsonProperty
import com.querydsl.core.annotations.QueryProjection import com.querydsl.core.annotations.QueryProjection
data class GetAudioContentRanking( data class GetAudioContentRanking(
@ -9,12 +10,12 @@ data class GetAudioContentRanking(
) )
data class GetAudioContentRankingItem @QueryProjection constructor( data class GetAudioContentRankingItem @QueryProjection constructor(
val contentId: Long, @JsonProperty("contentId") val contentId: Long,
val title: String, @JsonProperty("title") val title: String,
val coverImageUrl: String, @JsonProperty("coverImageUrl") val coverImageUrl: String,
val themeStr: String, @JsonProperty("themeStr") val themeStr: String,
val price: Int, @JsonProperty("price") val price: Int,
val duration: String, @JsonProperty("duration") val duration: String,
val creatorId: Long, @JsonProperty("creatorId") val creatorId: Long,
val creatorNickname: String @JsonProperty("creatorNickname") val creatorNickname: String
) )

View File

@ -1,9 +1,10 @@
package kr.co.vividnext.sodalive.content.main package kr.co.vividnext.sodalive.content.main
import com.fasterxml.jackson.annotation.JsonProperty
import com.querydsl.core.annotations.QueryProjection import com.querydsl.core.annotations.QueryProjection
data class GetNewContentUploadCreator @QueryProjection constructor( data class GetNewContentUploadCreator @QueryProjection constructor(
val creatorId: Long, @JsonProperty("creatorId") val creatorId: Long,
val creatorNickname: String, @JsonProperty("creatorNickname") val creatorNickname: String,
val creatorProfileImageUrl: String @JsonProperty("creatorProfileImageUrl") val creatorProfileImageUrl: String
) )

View File

@ -1,10 +1,8 @@
package kr.co.vividnext.sodalive.content.main.curation package kr.co.vividnext.sodalive.content.main.curation
import kr.co.vividnext.sodalive.common.BaseEntity import kr.co.vividnext.sodalive.common.BaseEntity
import kr.co.vividnext.sodalive.content.AudioContent
import javax.persistence.Column import javax.persistence.Column
import javax.persistence.Entity import javax.persistence.Entity
import javax.persistence.OneToMany
import javax.persistence.Table import javax.persistence.Table
@Entity @Entity
@ -20,7 +18,4 @@ data class AudioContentCuration(
var isActive: Boolean = true, var isActive: Boolean = true,
@Column(nullable = false) @Column(nullable = false)
var orders: Int = 1 var orders: Int = 1
) : BaseEntity() { ) : BaseEntity()
@OneToMany(mappedBy = "curation")
val audioContents: MutableList<AudioContent> = mutableListOf()
}

View File

@ -5,6 +5,7 @@ import kr.co.vividnext.sodalive.aws.s3.S3Uploader
import kr.co.vividnext.sodalive.common.SodaException import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.utils.generateFileName import kr.co.vividnext.sodalive.utils.generateFileName
import org.springframework.beans.factory.annotation.Value import org.springframework.beans.factory.annotation.Value
import org.springframework.cache.annotation.Cacheable
import org.springframework.data.repository.findByIdOrNull import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional import org.springframework.transaction.annotation.Transactional
@ -20,6 +21,10 @@ class EventService(
@Value("\${cloud.aws.cloud-front.host}") @Value("\${cloud.aws.cloud-front.host}")
private val cloudFrontHost: String private val cloudFrontHost: String
) { ) {
@Cacheable(
value = ["getEventList"],
cacheManager = "cacheManager"
)
fun getEventList(): GetEventResponse { fun getEventList(): GetEventResponse {
val eventList = repository.getEventList() val eventList = repository.getEventList()
.asSequence() .asSequence()

View File

@ -1,18 +1,21 @@
package kr.co.vividnext.sodalive.event package kr.co.vividnext.sodalive.event
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonProperty
import com.querydsl.core.annotations.QueryProjection import com.querydsl.core.annotations.QueryProjection
data class GetEventResponse( data class GetEventResponse(
val totalCount: Int, @JsonProperty("totalCount") val totalCount: Int,
val eventList: List<EventItem> @JsonProperty("eventList") val eventList: List<EventItem>
) )
@JsonIgnoreProperties(ignoreUnknown = true)
data class EventItem @QueryProjection constructor( data class EventItem @QueryProjection constructor(
val id: Long, @JsonProperty("id") val id: Long,
val title: String? = null, @JsonProperty("title") val title: String? = null,
var thumbnailImageUrl: String, @JsonProperty("thumbnailImageUrl") var thumbnailImageUrl: String,
var detailImageUrl: String? = null, @JsonProperty("detailImageUrl") var detailImageUrl: String? = null,
var popupImageUrl: String? = null, @JsonProperty("popupImageUrl") var popupImageUrl: String? = null,
val link: String? = null, @JsonProperty("link") val link: String? = null,
val isPopup: Boolean @JsonProperty("isPopup") val isPopup: Boolean
) )

View File

@ -1,6 +1,8 @@
package kr.co.vividnext.sodalive.live.recommend package kr.co.vividnext.sodalive.live.recommend
import com.fasterxml.jackson.annotation.JsonProperty
data class GetRecommendLiveResponse( data class GetRecommendLiveResponse(
val imageUrl: String, @JsonProperty("imageUrl") val imageUrl: String,
val creatorId: Long @JsonProperty("creatorId") val creatorId: Long
) )

View File

@ -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.QMember.member
import kr.co.vividnext.sodalive.member.following.QCreatorFollowing.creatorFollowing import kr.co.vividnext.sodalive.member.following.QCreatorFollowing.creatorFollowing
import org.springframework.beans.factory.annotation.Value import org.springframework.beans.factory.annotation.Value
import org.springframework.cache.annotation.Cacheable
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
import java.time.LocalDateTime import java.time.LocalDateTime
@ -19,6 +20,10 @@ class LiveRecommendRepository(
@Value("\${cloud.aws.cloud-front.host}") @Value("\${cloud.aws.cloud-front.host}")
private val cloudFrontHost: String private val cloudFrontHost: String
) { ) {
@Cacheable(
value = ["getRecommendLive"],
cacheManager = "cacheManager"
)
fun getRecommendLive( fun getRecommendLive(
memberId: Long, memberId: Long,
isBlocked: (Long) -> Boolean, isBlocked: (Long) -> Boolean,

View File

@ -73,6 +73,8 @@ spring:
multipart: multipart:
max-file-size: 1024MB max-file-size: 1024MB
max-request-size: 1024MB max-request-size: 1024MB
cache:
type: redis
--- ---
spring: spring:
config: config:

View File

@ -47,3 +47,5 @@ spring:
hibernate: hibernate:
show_sql: true show_sql: true
format_sql: true format_sql: true
cache:
type: redis