Spring Caching 기능을 H2 DB와 Redis를 사용해서 실습해보았다.
이 글을 읽기 앞서, 이전 포스팅을 보고 오는 것을 추천한다.
https://velog.io/@jinvicky/Redis-설치-및-CRUD
https://velog.io/@jinvicky/Spring-Cache-Test
https://velog.io/@jinvicky/Spring-Cache-Redis-저장-구조
Windows + Spring Boot + Gradle
전체 폴더 구조
application.properties
그대로 붙여 넣기
# set h2 properties
spring.h2.console.enabled=true
# ?? ?? ?? ?? (true-?? ??, false-?? ??)
spring.datasource.generate-unique-name=false
# /h2-console? ????? ??? ?? ??
spring.h2.console.path=/h2-console
spring.datasouce.url=jdbc:h2:mem:testdb //h2-console 접속 시 jdbc url에 넣을 값
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa //사용자 이름 변경 가능
spring.datasource.password=1234 //비밀번호 변경 가능
#jpa
spring.jpa.hibernate.ddl-auto=create
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
spring.jpa.database=h2
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
#logging
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate.sql=DEBUG
logging.level.jpa=DEBUG
server.port=8083
application.yaml
Redis를 설치했다는 가정 하에 추가한다.
개인적으로 중간에 data 키워드를 끼우지 않으니 빨간줄 에러가 났었다.
spring:
data:
redis:
port: 6379
host: localhost
build.gradle
H2, Redis 관련 설정을 꼭 확인하고 추가하자.
...
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
H2는 휘발성이기 때문에 테스트용으로 많이 사용한다. 재시작시 데이터 등이 날라간다.
RedisConfig.java
package com.jv.h2test.config;
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.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
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;
import java.util.HashMap;
import java.util.Map;
@EnableCaching
@Configuration
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String redisHost;
@Value("${spring.data.redis.port}")
private int redisPort;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(new RedisStandaloneConfiguration(redisHost, redisPort));
}
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext
.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext
.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory)
.cacheDefaults(redisCacheConfiguration)
.build();
}
}
@EnableCaching 어노테이션을 붙이는 곳이다. Application 클래스에 붙이는 블로그도 있는데 Config 파일에 붙여도 동작한다. 그렇다면 위치상 Config 클래스에 붙이는 게 찾기도 더 쉽고 낫다고 생각한다.
TbUser.java
package com.jv.h2test.domain;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@Table
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class TbUser {
@Id
@Column(length = 50, nullable = false)
private String userId;
@Column(length = 50, nullable = false)
private String desc;
}
TbUserRepository.java
package com.jv.h2test.repository;
import com.jv.h2test.domain.TbUser;
import org.springframework.data.jpa.repository.JpaRepository;
public interface TbUserRepository extends JpaRepository<TbUser, String> {
}
package com.jv.h2test.controller;
import com.jv.h2test.domain.TbUser;
import com.jv.h2test.repository.TbUserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;
@RestController
@RequiredArgsConstructor
@Slf4j
public class UserController {
private final TbUserRepository userRepository;
@PostMapping("/insert") // 사용자 추가
public String createUser(@RequestBody TbUser ipnutUser) {
userRepository.save(ipnutUser);
return "success";
}
@Cacheable(value="userList", key="#userId")
@GetMapping("/users/{userId}") //사용자 조회
public TbUser userList(@PathVariable String userId) {
try {
TbUser user = userRepository.findById(userId).orElse(null);
return user;
} catch (Exception e) {
e.printStackTrace();
return new TbUser();
}
}
@CacheEvict(value="userList", key="#user.userId") //캐시를 삭제할 경우 이 한 줄을 추가한다.
@PostMapping("/update/user/cache")
public String updateUserAndCash (@RequestBody TbUser user) {
TbUser currentUser = userRepository.findById(user.getUserId()).orElse(null);
if(currentUser != null) {
currentUser.setDesc(user.getDesc());
userRepository.deleteById(user.getUserId());
userRepository.save(currentUser);
return "update ok";
}
return "user not found";
}
}
@EnableCaching을 이용하는 방법을 캐시 추상화라고 한다.
스프링 캐시 추상화 어노테이션들은 CacheManager 를 통해 캐시를 사용한다.
이번 경우에는 CacheManager 인터페이스를 Redis로 구현한 케이스다.
https://velog.io/@mooh2jj/Redis-Cacheable-CacheEvict
https://yonguri.tistory.com/82
https://jiwondev.tistory.com/282
https://gngsn.tistory.com/157