์๋ณธ ํ๋ฆฌํ์คํธ: [Feature][Refactor] ์์ฑ๋ ๊ฒ์๊ธ(post) Redis๋ก ์บ์ฑํ๊ธฐ
์ด์ ์ ์์ฒญ๋ ์ฝ์ง์ ํ์๋๋ฐ... ๊ฐ๊น์ด ๊ณณ์ ๋ชป๋ณด๊ณ cache-session ์๋ฒ๋ฅผ ๋ถ๋ฆฌํ ์๊ฐ๋ถํฐ ํ์๋ค. (๊ฒฐ๋ก ์ ์ผ๋ก ๋ถ๋ฆฌํ๋๊ฒ ์ฅ๊ธฐ์ ์ผ๋ก ๋ง๊ธด ํ์ง๋ง, ์ง๊ธ์ ํ๋ก์ ํธ ๊ท๋ชจ๊ฐ ์๊ธฐ ๋๋ฌธ์ ํฐ ํ์๋ ์๋ค.) ์ฌ๊ธฐ์๋ ์๊ฐ์ ๋ง์ด ์ก์ ๋จน๊ณ ...
๋ ๋์ค๊ฐ ๋ญ์ง ์ ํ!! ๊ฐ์ด ์ค์ง ์์์, ์๋ฃ์ ์์ ๋ง 3์ผ์ ๋ ์ฐพ์๋ค๋ ๊ฒ ๊ฐ๋ค... ํ๋ค์ด... (ใ
ใ
)
ํ์ง๋ง ํ๊ณ ๋๋๊น ๊ป์ง์ ํ๋ ๊นฌ ๋๋... ๋ฟ๋ฏํ๋ค.
- ์ด์ ๋ชฉํ: ์์ฑํ ๊ธ์ ์บ์ฑํด์ผ๊ฒ ๋ค! (๊ธ์ด ์์ฑ๋ ๋๋ง๋ค)
- ํ์ฌ ๋ชฉํ: ์กฐํํ ๊ธ์ ์บ์ฑํด์ผ๊ฒ ๋ค!
์๋ ํธ๋ฌ๋ธ ์ํ 1๋ฒ ์ฐธ๊ณ
dependency {
// serialize
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.3'
}
RedisCacheConfig
๋ฅผ ํตํด @Cacheable
์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ์ ๋, Spring์ด ์๋ Redis๊ฐ ์บ์ฑํ ์ ์๋๋ก ์ค์ ํ๋ค.@Configuration
@EnableCaching
public class RedisCacheConfig {
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory cacheConnectionFactory) {
GenericJackson2JsonRedisSerializer redisSerializer = getGenericJackson2JsonRedisSerializer(); // ๋ฐ๋ก ์๋ ๋ฌธ๋จ์
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.entryTtl(Duration.ofMinutes(10)); // ์บ์ ์ง์ ์๊ฐ
return RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(cacheConnectionFactory)
.cacheDefaults(configuration)
.build();
}
}
private GenericJackson2JsonRedisSerializer getGenericJackson2JsonRedisSerializer() {
PolymorphicTypeValidator typeValidator = BasicPolymorphicTypeValidator
.builder()
.allowIfSubType(Object.class)
.build();
// ObjectMapper ๋ฅผ ์ฌ์ฉํด์ LocalTimeDate๋ฅผ ์ง๋ ฌํ๋ฅผ ํ๊ธฐ ์ํด ๋งคํ
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.activateDefaultTyping(typeValidator, ObjectMapper.DefaultTyping.NON_FINAL);
GenericJackson2JsonRedisSerializer redisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
return redisSerializer;
}
@EnableCaching
์ด๋
ธํ
์ด์
์ ์ถ๊ฐํด์ฃผ์ด์ผ ํ๋ค.@EnableCaching // โ โ
public class KP3COutsourcingProjectApplication { ... }
@Cacheable
์ด๋
ธํ
์ด์
์ ์ถ๊ฐํ๊ณ , ๊ฐ์ ์ค์ ํด์ค๋ค.value
= ์ ์ฅํ value ์ด๋ฆkey
= ์ ์ฅํ key ์ด๋ฆ, #
+{๋ณ์๋ช
}
์ ์กฐํฉํด์ key ๊ฐ์ ๋ณ์๋ช
์ผ๋ก ์ค์ ํด์ค ์ ์๋ค.cacheManager
= RedisCacheConfig์์ ์์ฑํ ์บ์ ๋งค๋์ ๋ฉ์๋/* PostService.java */
// ๊ฒ์๊ธ 1๊ฐ๋ง
@Cacheable(value = "post", key = "#postId", cacheManager = "redisCacheManager")
public PostResponseDto getPost(Long postId) {
Post post = postRepository.findById(postId).orElseThrow(() ->
new NullPointerException("์กด์ฌํ์ง ์๋ ๊ฒ์๊ธ ์
๋๋ค.")
);
return new PostResponseDto(post);
}
PostResponseDto
ํ์
์ผ๋ก ๋ฐ์ดํฐ๊ฐ ๋ด๊ฒจ์ ธ Redis ์บ์ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅ๋๋ค.์ต์ด ์คํ: 135ms
2๋ฒ์งธ ์คํ: 22ms
3๋ฒ์งธ ์คํ: 11ms
LocalDateTime
ํ์
์ง๋ ฌํ ๋ฌธ์ Java 8 date/time type
java.time.LocalDateTime
not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling...
LocalDateTime
ํ์
์ ์ง์ํ์ง ์๋ ๋ฏํ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค. Jackson์ ํด๋น ํ์
์ ์ง๋ ฌํ๋ฅผ ์ง์ํ์ง ์์ผ๋ฉฐ, ์ฒ๋ฆฌํ๊ธฐ ์ํด com.fasterxml.jackson.datatype:jackson-datatype-jsr310
์์กด์ฑ์ ์ถ๊ฐํ๋ผ๋ ๋ป์ด๋ค.dependency {
// serialize
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.3'
}
/* TimeStamped.java */
@CreatedDate
@Column(updatable = false)
@Temporal(TemporalType.TIMESTAMP)
@JsonSerialize(using = LocalDateTimeSerializer.class) // ๏ผ ์ง๋ ฌํ
@JsonDeserialize(using = LocalDateTimeDeserializer.class) // ๏ผ ์ญ์ง๋ ฌํ
@JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm") // ๏ผ ์ ์ฅ ํฌ๋งท ์ค์
private LocalDateTime createdAt;
์ฐ๋ ค๋๋ ์ ...: ์ด๋ ธํ ์ด์ ์ด ๋๋ฌด ๋ง์ด ๋ฌ๋ ค์ ๊ฐ๋ ์ฑ์ ์ข์ง ์๊ฑฐ๋ ์ฑ๋ฅ์์ ๋ฌธ์ ๊ฐ ์๊ธฐ์ง ์์์ง ๊ฑฑ์ ์ด ๋๊ธด ํ๋ค.
imageUrlList
ํธ์ถ ๋ฌธ์ could not write json: failed to lazily initialize a collection: could not initialize proxy - no session
imageUrlList
ํ๋๋ถํฐ๋ ์ ๋๋ก ๋ฐ์ดํฐ๊ฐ ์บ์ฑ๋์ง ์์ ๊ฒ ๊ฐ์ ๋ชจ์ต์ ๋ณด์๋ค.PostResponseDto
๋ด๋ถ, imageUrlList
ํ๋๊ฐ ์ํฐํฐ์ ์ ๋ณด๋ฅผ List ํํ๋ก ๋ด๊ณ ์์๊ธฐ ๋๋ฌธ์ด์๋ค. Redis์๋ ์ํฐํฐ๋ฅผ ๊ทธ๋๋ก ์ ์ฅํ๋ ๊ฒ์ด ๋ถ๊ฐ๋ฅํ๊ณ , ๊ฐ๋ฅํ๋๋ผ๋ ๊ถ์ฅ์ฌํญ์ด ์๋๊ธฐ ๋๋ฌธ์, ์ ์ฅํ๋ ๊ฒ์ ์ง์ํด์ผํ๋ค.@NoArgsConstructor
@AllArgsConstructor
public class PostResponseDto extends ApiResponseDto {
private Long id;
private String username;
private String nickname;
private String content;
private LocalDateTime createdAt;
private List<Post_Image> imageUrlList; // Post_Image ์ํฐํฐ ํด๋์ค๋ฅผ List ํํ๋ก ๋ด๊ณ ์๋ค
private List<PostResponseDto> children;
/* ์์ฑ์ ์ ์ ์๋ต */
}
@NoArgsConstructor
@AllArgsConstructor
public class PostResponseDto extends ApiResponseDto {
private Long id;
private String username;
private String nickname;
private String content;
private LocalDateTime createdAt;
private List<PostImageDto> imageUrlList; // Post_Image ์ํฐํฐ ํด๋์ค์ ์ ๋ณด๋ฅผ ๋ด์ Dto ํด๋์ค๋ฅผ ๋ง๋ค์๋ค
private List<PostResponseDto> children;
/* ์์ฑ์ ์ ์ ์๋ต */
}
PostImageDto
๋ฅผ ์์ฑํ์ฌ Post_Image
์ํฐํฐ ํด๋์ค์ ์ ๋ณด๋ฅผ ๋ด์๋ค.@NoArgsConstructor
@AllArgsConstructor
public class PostImageDto {
Long id;
String imgUrl;
public PostImageDto (Post_Image postImage) {
this.id = postImage.getId();
this.imgUrl = postImage.getImgUrl();
}
}
Post_Image ๊ฐ์ PostImageDto๋ก ์ ์ฅํ ๋๋ ์ด๋ ๊ฒ...
/* PostResponseDto.java */
public PostResponseDto(Post post) {
// ๋ค๋ฅธ ํ๋ ์๋ต
this.imageUrlList = post.getImagetList().stream().map(PostImageDto::new).toList();
}