Merge pull request '로딩 속도를 위해 @Cacheable 적용' (#50) from test into main
Reviewed-on: #50
This commit is contained in:
		| @@ -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 | ||||||
|   | |||||||
| @@ -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() | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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, | ||||||
|   | |||||||
| @@ -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 | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -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 | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -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() |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -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() | ||||||
|   | |||||||
| @@ -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 | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -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 | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -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, | ||||||
|   | |||||||
| @@ -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: | ||||||
|   | |||||||
| @@ -47,3 +47,5 @@ spring: | |||||||
|             hibernate: |             hibernate: | ||||||
|                 show_sql: true |                 show_sql: true | ||||||
|                 format_sql: true |                 format_sql: true | ||||||
|  |     cache: | ||||||
|  |         type: redis | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user