Spring Cache - 코드 실습

jinvicky·2023년 10월 27일
0

Spring & Java

목록 보기
13/23

Overview


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

실습

전체 폴더 구조

1. Config 설정

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 클래스에 붙이는 게 찾기도 더 쉽고 낫다고 생각한다.

2. Entity, Repository 작성

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> {
}

3. Controller 작성

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로 구현한 케이스다.

Reference


https://velog.io/@mooh2jj/Redis-Cacheable-CacheEvict
https://yonguri.tistory.com/82
https://jiwondev.tistory.com/282
https://gngsn.tistory.com/157

profile
Front-End와 Back-End 경험, 지식을 공유합니다.

0개의 댓글