728x90
반응형

1. Redis docker image pull

$ docker pull redis # redis 이미지 받기
$ docker images # redis 이미지 확인
$ docker run -p 6379:6379 --name some-redis -d redis # redis 시작하기
$ docker ps # redis 실행 확인

 

 

2. 의존성 추가

implementation 'org.springframework.boot:spring-boot-starter-data-redis'

 

 

3. application.properties

#Redis
spring.redis.host= localhost
spring.redis.port= 6379

 

 

Redis Cache 활성화를 위한 @Annotation

  • @EnableCaching
    • SpringBoot에게 캐싱 기능이 필요하다고 전달
    • SpringBoot Starter class에 적용
  • @Cacheable
    • DB에서 애플리케이션으로 데이터를 가져오고 Cache에 저장하는 데 사용
    • DB에서 데이터를 가져오는 메서드에 적용
  • @CachePut
    • DB의 데이터 업데이트가 있을 때 Redis Cache에 데이터를 업데이트
    • DB에서 PUT/PATCH와 같은 업데이트에서 사용
  • @CacheEvict
    • DB의 데이터 삭제가 있을 때 Redis Cache에 데이터를 삭제
    • DB에서 DELETE와 같은 삭제에서 사용

 

4. RedisConfig 파일 생성

/backend/redis/RedisConfig

package web.backend.redis;

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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration
@EnableCaching
public class RedisConfig {

    @Bean
    public CacheManager testCacheManager(RedisConnectionFactory cf) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .entryTtl(Duration.ofMinutes(3L));

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

매니저에 도메인을 Serialize 하게끔 설정해두었기에 도메인에 따로 Serializable를 import 안해줘도 된다.

다만 어노테이션에서 매니저를 호출해야 한다.

 

backend/WebConfig

@Import({AopConfig.class, RedisConfig.class})

 

5. LocalDateTime in Domain

Serialize를 위해 LocalDateTime 타입 필드는 설정을 따로 해줘야 한다.

backend/module/user/User

...

    @CreatedDate
    @Column(name="user_createdat", updatable = false)
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    private LocalDateTime user_creadtedat;

    @LastModifiedDate
    @Column(name="user_updatedat")
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    private LocalDateTime user_updatedat;
    
...

 

6. Service 적용

package web.backend.module.user;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import web.backend.module.user.repository.UserQueryRepository;
import web.backend.module.user.repository.UserSpringJpaRepository;

import java.util.List;

@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class UserService {

    private final UserQueryRepository userQueryRepository;
    private final UserSpringJpaRepository userSpringJPARepository;

    @Cacheable(value = "User", cacheManager = "testCacheManager")
    public List<User> findAll() {
        return userSpringJPARepository.findAll();
    }

    @Cacheable(value = "User", key = "#id", cacheManager = "testCacheManager")
    public User findByIndexId(Long id) {
        return userSpringJPARepository.findById(id).get();
    }

    public String save(User user) {
        userSpringJPARepository.save(user);
        return "ok";
    }

    @CachePut(value = "Order", key = "#id", cacheManager = "testCacheManager")
    public String update(Long id, User user) {
        User userOne = userSpringJPARepository.findById(id).get();
        userOne.changeUser(user.getUserId());
        return "ok";
    }

    @CacheEvict(value = "Order", key = "#id", cacheManager = "testCacheManager")
    public String delete(Long id) {
        userSpringJPARepository.deleteById(id);
        return "ok";
    }


}

 


테스트

1. 데이터 추가

테스트를 위해 WebConfig에 어플리케이션 빌드 시 데이터를 추가하는 로직을 작성해준다.

package web.backend;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import web.backend.aop.AopConfig;
import web.backend.interceptor.LogInterceptor;
import web.backend.module.user.User;
import web.backend.module.user.repository.UserSpringJpaRepository;
import web.backend.redis.RedisConfig;

@Configuration
@Import({AopConfig.class, RedisConfig.class})
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor())
                .order(1)
                .addPathPatterns("/**")
                .excludePathPatterns(
                        "/css/**", "/*.ico"
                        , "/error", "/error-page/**" //오류 페이지 경로
                );
    }

    @Autowired
    UserSpringJpaRepository userSpringJpaRepository;

    @Bean
    public void addUsers() {
        for(int i = 0; i < 100; i++) {
            User user = new User();
            user.changeUser("member" + i);
            userSpringJpaRepository.save(user);
        }
    }

}

 

 

2. Redis Docker Container

Spring Server를 키고 Redis Container에 접속한 후 요청을 보내서 로그를 확인해보자.

$ docker ps    
$ docker exec -it some-redis /bin/bash
$ redis-cli monitor

 

postman으로 요청을 보내보자.

그러면 아래와 같이 캐시가 저장된 것을 확인할 수 있다.

 

postman에도 데이터가 잘 도착했다.

이번엔  key를 지정하고 다시 보내보자. ( id를 파라미터로 보내는 로직 )

 

역시 정상적으로 동작이 잘 되었다.

 

 

3. 성능 확인

 

- 처음 캐시가 사용되기 전 요청

 

- 두번 째 요청

 

 


다만,

아래와 같은 점을 고려해야 한다.

  1. 리스트를 처음에 캐싱한다.
  2. 리스트의 어떤 항목이 생성되거나 수정되거나 삭제된다.
  3. 캐시된 리스트의 데이터가 바뀌어야 하지만 그렇지 못한다.

캐시에 변경사항이 생길 경우 다른 캐시에 영향이 끼친다면 로직을 따로 만들어 주어야 한다. 당장에라도 적용하고 싶으면

findAll()의 @Cacheable 어노테이션은 제거한 상태로 쓰고 나중에 각 테이블의 연관관계를 고려해서 로직을 구성해주자.

728x90
반응형

+ Recent posts