[Spring Boot] Redis 적용기 - 2 사용자 정의 Class List로 저장 및 호출

박철현·2023년 11월 24일
0

스프링부트

목록 보기
2/8

구현한 개인 프로젝트인 예산 관리 프로젝트에서, 변하지 않는 것이 카테고리 데이터입니다.

하지만 예산을 등록할때, 카테고리를 조회할때 등 카테고리 정보를 가져올때가 있습니다.
이를 효과적으로 개선하기 위해 Redis 캐시를 도입했습니다.

변경 전

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class CategoryService {
	private final CategoryRepository categoryRepository;
	public RsData<List<Category>> getAll() {
		List<Category> allCategories = categoryRepository.findAll();

		if(allCategories.isEmpty()){
			return RsData.of("F-1", "등록된 카테고리가 없습니다.");
		}

		return RsData.of("S-1", "카테고리 목록 조회 성공", allCategories);
	}

	public Category get(Long categoryId) {
		return categoryRepository.findById(categoryId).get();
	}
}
  • Repository에서 findAll 혹은 findById를 통해 DB에 접속하였습니다.
  • 이를 개선하여 Redis 캐시를 활용합니다.

변경 사항 1 - Redis에 사용자 정의 Class 저장하기 위해 Entity 개선

  • Category Entity 변경
    • 사용자 정의 클래스가 Serializable 인터페이스를 구현해야 함
      • Redis는 데이터를 바이트 형태로 저장하므로, 캐시에 저장할 객체는 직렬화가 가능해야 합니다.
      • Serializable 인터페이스를 구현하는 것은 해당 클래스의 인스턴스가 직렬화할 수 있다는 것을 JVM에게 알려주는 역할
        • 직렬화 : 객체를 바이트 스트림(byte stream)으로 변환하는 과정
        • 역직렬화 : 바이트 스트림을 원래의 객체로 복구하는 과정
    • 캐싱된 데이터를 다시 읽어올 때에도 해당 클래스의 인스턴스로 잘 변환될 수 있어야 합니다. 이를 위해선 해당 클래스가 기본 생성자를 가지고 있어야 합니다.
  • 변경된 Category Entity
@Entity
@Getter
@Builder(toBuilder = true)
@AllArgsConstructor
@NoArgsConstructor  // 필수
@QueryEntity
public class Category implements Serializable  // 필수 {
	@Id
	@GeneratedValue(strategy = IDENTITY)
	private Long id;

	@Column(unique = true)
	private String nameE;

	@Column(unique = true)
	private String nameH;
}

변경 사항 2 - Redis에 Category List 저장

  • 변경 전 코드
public RsData<List<Category>> getAll() {
		List<Category> allCategories = categoryRepository.findAll();

		if(allCategories.isEmpty()){
			return RsData.of("F-1", "등록된 카테고리가 없습니다.");
		}

		return RsData.of("S-1", "카테고리 목록 조회 성공", allCategories);
	}
  • findAll()로 항상 카테고리를 전체 조회 했습니다.
  • 하지만 CategoryService에 @Cacheable 어노테이션을 활용한 메서드를 만들어 Redis를 사용하도록 했습니다.
  • 변경 후 코드
	@Cacheable(value = "CategoryList")
	public List<Category> getAllCategoriesByCached() {
		return categoryRepository.findAll();
	}
public RsData<List<Category>> getAll() {
		// 캐시 활용
		List<Category> allCategories = redisService.getAllCategoriesByCached();

		if(allCategories.isEmpty()){
			return RsData.of("F-1", "등록된 카테고리가 없습니다.");
		}

		return RsData.of("S-1", "카테고리 목록 조회 성공", allCategories);
	}
  • 결과
    • 최초 조회
      • DB Select 쿼리 발생
    • 이후 조회
      • DB Select 쿼리 미발생

변경 사항 3 - Redis에 Category 각각 저장

  • 예산 계획 설정, 지출 예산 등록 등에 예산 카테고리 각각을 조회한다.

  • 이 경우에도 DB 조회가 빈번히 발생하나, 카테고리는 변하는 데이터가 아니기에 Redis에 각각 저장한다.

  • 변경 전 코드

public Category get(Long categoryId) {
		return categoryRepository.findById(categoryId).get();
	}
  • DB에 계속 접속하여 가져온다.
  • 하지만 CategoryService에 @Cacheable 어노테이션을 활용한 메서드를 만들어 Redis를 사용하도록 했습니다.
  • 변경 후 코드
public Category get(Long categoryId) {
		// 캐시 활용
		return redisService.getCategoryByCached(categoryId);
 }
  • 결과
    • 예산 지출 내역 저장 API 요청
    • 발생 쿼리 일부
      • Redis에 없는 최초에는 쿼리 발생하나 이후 부터는 카테고리 찾는 쿼리 발생x

프로젝트 중 변할 일이 없는 카테고리 데이터를 캐시화 하여 성능을 향상시킬 수 있었습니다!

profile
비슷한 어려움을 겪는 누군가에게 도움이 되길

0개의 댓글