Skip to content

Commit

Permalink
Merge pull request #210 from gamemuncheol/perf/209
Browse files Browse the repository at this point in the history
Cacheable Annotation 사용해서 Post Detail Caching 하는 방식으로 변경
  • Loading branch information
rookedsysc authored Nov 3, 2024
2 parents 715ec17 + f2f97d6 commit f3f7e29
Show file tree
Hide file tree
Showing 16 changed files with 134 additions and 54 deletions.
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ dependencies {
// flyway
implementation 'org.flywaydb:flyway-core'
implementation 'org.flywaydb:flyway-mysql'

// Jackson for JSON processing
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.16.1'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
}

tasks.named('test') {
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/gamemoonchul/GamemoonchulApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.retry.annotation.EnableRetry;

@EnableRetry
@EnableCaching
@EnableJpaAuditing
@SpringBootApplication
public class GamemoonchulApplication {
Expand Down
25 changes: 0 additions & 25 deletions src/main/java/com/gamemoonchul/application/PostCacheService.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,12 @@
public class PostOpenApiService {
private final PostRepository postRepository;
private final CommentService commentService;
private final RedisPostDetailRepository redisPostDetailRepository;

public RedisPostDetail getPostDetails(Long postId, Long requestMemberId) {
Optional<RedisPostDetail> optionalPostDetail = redisPostDetailRepository.findRedisPostDetailById(postId);
RedisPostDetail redisPostDetail;
Post post = postRepository.searchByPostId(postId).orElseThrow(() -> new BadRequestException(PostStatus.POST_NOT_FOUND));
post.viewCountUp();

if (optionalPostDetail.isEmpty()) {
Post post = postRepository.searchByPostId(postId).orElseThrow(() -> new BadRequestException(PostStatus.POST_NOT_FOUND));
post.viewCountUp();

redisPostDetail = redisPostDetailRepository.save(PostConverter.toCache(post));
} else {
redisPostDetail = optionalPostDetail.get();
}
RedisPostDetail redisPostDetail = PostConverter.toCache(post);

List<CommentResponse> comments = commentService.searchByPostId(redisPostDetail.getId(), requestMemberId).stream()
.map(CommentConverter::toResponse).toList(); // 변경 자주 일어남, 캐싱 X
Expand Down
7 changes: 4 additions & 3 deletions src/main/java/com/gamemoonchul/common/ObjectMapperConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
Expand All @@ -25,11 +26,11 @@ public ObjectMapper objectMapper() {

// 비어있는 Bean을 만들 때
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
// 날짜 관련 직렬화 설정 해제
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// // 날짜 관련 직렬화 설정 해제
// objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

// 스네이크 케이스
// objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategies.SnakeCaseStrategy());
objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategies.SnakeCaseStrategy());

return objectMapper;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.gamemoonchul.common.constants;

public class RedisKeyConstant {
public static String postCountKey(Long postId) {
return "post:count:" + postId;
}
}
38 changes: 38 additions & 0 deletions src/main/java/com/gamemoonchul/config/RedisCacheManagerConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.gamemoonchul.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gamemoonchul.domain.entity.Post;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.CacheManager;
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.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration
@EnableCaching
@RequiredArgsConstructor
public class RedisCacheManagerConfig {
private final ObjectMapper objectMapper;

@Bean
public CacheManager postCacheManager(RedisConnectionFactory cf) {
RedisSerializer<Post> valueSerializer = new Jackson2JsonRedisSerializer<>(objectMapper.getTypeFactory().constructType(Post.class));

RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer)) // Value Serializer 변경
.entryTtl(Duration.ofMinutes(30L)); // 캐시 수명 30분

return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(cf).cacheDefaults(redisCacheConfiguration).build();
}

}
41 changes: 30 additions & 11 deletions src/main/java/com/gamemoonchul/config/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -1,48 +1,67 @@
package com.gamemoonchul.config;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gamemoonchul.domain.entity.Member;
import com.gamemoonchul.domain.entity.Post;
import com.gamemoonchul.infrastructure.web.dto.response.PostMainPageResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.CacheManager;
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.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.util.List;

@Configuration
@RequiredArgsConstructor
public class RedisConfig {

private final ObjectMapper objectMapper;

@Bean
RedisTemplate<String, Member> userRedisTemplate(RedisConnectionFactory connectionFactory) {
var template = new RedisTemplate<String, Member>();
public RedisTemplate<String, Integer> stringIntegerRedisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Integer> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);

// Key Serializer 설정
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new Jackson2JsonRedisSerializer<>(objectMapper, Member.class));

// Value Serializer 설정
template.setValueSerializer(new GenericToStringSerializer<>(Integer.class));

return template;
}

/**
* Redis Cacheable에서 사용함
*
* @param connectionFactory
* @return redisTemplate
*/
@Bean
public RedisTemplate<String, List<PostMainPageResponse>> postDetailRedisTemplate(RedisConnectionFactory connectionFactory, ObjectMapper objectMapper) {
RedisTemplate<String, List<PostMainPageResponse>> template = new RedisTemplate<>();
public RedisTemplate<String, Post> postRedisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Post> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);

// Key Serializer 설정
template.setKeySerializer(new GenericToStringSerializer<>(String.class));
template.setKeySerializer(new StringRedisSerializer());

// Value Serializer 설정
Jackson2JsonRedisSerializer<List<PostMainPageResponse>> serializer =
new Jackson2JsonRedisSerializer<>(objectMapper.getTypeFactory().constructCollectionType(List.class, PostMainPageResponse.class));
Jackson2JsonRedisSerializer<Post> serializer =
new Jackson2JsonRedisSerializer<>(objectMapper.getTypeFactory().constructType(Post.class));
template.setValueSerializer(serializer);

template.setValueSerializer(serializer);
template.setValueSerializer(serializer);

template.afterPropertiesSet();
return template;
}
}
2 changes: 2 additions & 0 deletions src/main/java/com/gamemoonchul/domain/entity/Comment.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.gamemoonchul.domain.entity;


import com.fasterxml.jackson.annotation.JsonBackReference;
import com.gamemoonchul.domain.entity.base.BaseTimeEntity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
Expand All @@ -27,6 +28,7 @@ public class Comment extends BaseTimeEntity {
@JoinColumn(nullable = false, name = "member_id")
private Member member;

@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(nullable = false, name = "post_id")
private Post post;
Expand Down
17 changes: 15 additions & 2 deletions src/main/java/com/gamemoonchul/domain/entity/Member.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package com.gamemoonchul.domain.entity;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;
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 com.gamemoonchul.config.oauth.user.OAuth2Provider;
import com.gamemoonchul.domain.entity.base.BaseTimeEntity;
import com.gamemoonchul.domain.enums.MemberRole;
Expand All @@ -20,8 +27,8 @@
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "member", uniqueConstraints = {
@UniqueConstraint(columnNames = "nickname"
), @UniqueConstraint(columnNames = {"provider", "identifier"})
@UniqueConstraint(columnNames = "nickname"
), @UniqueConstraint(columnNames = {"provider", "identifier"})
})
public class Member extends BaseTimeEntity {
@Id
Expand Down Expand Up @@ -54,18 +61,23 @@ public class Member extends BaseTimeEntity {

@Setter
@Column(name = "privacy_agreed_at")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime privacyAgreedAt;

@Column(nullable = false)
private Double score;

@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime birth;

@Enumerated(EnumType.STRING)
@Setter
@Column(nullable = false, columnDefinition = "varchar(255)")
private MemberRole role;

@JsonIgnore
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Post> posts;

Expand All @@ -89,6 +101,7 @@ public Member updateNickname(String nickname) {
return this;
}

@JsonIgnore
public String getRoleKey() {
return this.role.getKey();
}
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/com/gamemoonchul/domain/entity/Post.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package com.gamemoonchul.domain.entity;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.gamemoonchul.application.converter.JsonStringListConverter;
import com.gamemoonchul.domain.entity.base.BaseTimeEntity;
import jakarta.persistence.*;
import lombok.*;
import lombok.experimental.SuperBuilder;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

Expand All @@ -20,7 +25,7 @@
@Index(name = "idx_view_count", columnList = "view_count DESC"),
@Index(name = "idx_member_id", columnList = "member_id")
})
public class Post extends BaseTimeEntity {
public class Post extends BaseTimeEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Expand All @@ -29,9 +34,11 @@ public class Post extends BaseTimeEntity {
@JoinColumn(name = "member_id")
private Member member;

@JsonManagedReference
@OneToMany(fetch = FetchType.LAZY, mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<VoteOptions> voteOptions;

@JsonManagedReference
@OneToMany(fetch = FetchType.LAZY, mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments;

Expand Down Expand Up @@ -86,6 +93,7 @@ public void addVoteOptions(List<VoteOptions> voteOptions) {
this.voteOptions.addAll(voteOptions);
}

@JsonIgnore
public Double getMinVoteRatio() {
int totalVoteCount = voteOptions.stream()
.mapToInt(voteOption -> voteOption.getVotes()
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/gamemoonchul/domain/entity/VoteOptions.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.gamemoonchul.domain.entity;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.gamemoonchul.domain.entity.riot.MatchUser;
import jakarta.persistence.*;
import lombok.*;
Expand All @@ -19,6 +20,8 @@ public class VoteOptions {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id")
private Post post;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.gamemoonchul.domain.entity.base;

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 jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
Expand All @@ -11,6 +15,7 @@
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.io.Serializable;
import java.time.LocalDateTime;

@Data
Expand All @@ -19,12 +24,16 @@
@AllArgsConstructor
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseTimeEntity {
public class BaseTimeEntity implements Serializable {
@CreatedDate
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@Column(name = "created_at")
private LocalDateTime createdAt;

@LastModifiedDate
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}
Expand Down
Loading

0 comments on commit f3f7e29

Please sign in to comment.