Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 견종 목록 조회시 로컬 캐시 적용 #533

Merged
merged 4 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-cache'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spring-web에서 기본적으로 제공해주는 캐시 라이브러리(에를 들면 @EnabalCaching) 도 있는걸로 알고있는데 이 의존성은 뭔가요??

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wonyongChoi05

spring-web 모듈에는 캐싱과 직접적으로 관련된 기능이 포함되어 있지 않습니다. 이 모듈은 주로 웹 애플리케이션 개발과 관련된 클래스와 인터페이스, 예를 들면 MVC 패턴 구현, RESTful 웹 서비스 생성, 웹 클라이언트 기능 등을 포함합니다. 하지만, 캐시 저장소나 매니저를 수동으로 구성해야합니다. 예를 들어, EhCache나 Redis와 같은 캐시 저장소를 사용하기 위해서는 해당 저장소에 대한 의존성을 직접 추가하고 적절한 구성을 제공해야 합니다.

라고합니..


implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
Expand Down Expand Up @@ -70,6 +71,8 @@ dependencies {
implementation 'software.amazon.awssdk:s3:2.20.121'
implementation 'software.amazon.awssdk:url-connection-client:2.20.121'

implementation 'com.github.ben-manes.caffeine:caffeine'
Copy link
Collaborator

@wonyongChoi05 wonyongChoi05 Nov 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가비의 설명에 덧붙이자면 아래와 같은 Cache Storage의 종류가 있습니다!

  • ConcurrentMapCache: JDK의 ConcurrentHashMap을 사용해 구현한 캐시
  • SimpleCache: 기본적으로 제공하는 캐시가 없어 사용할 캐시를 직접 등록하여 사용하기 위한 캐시
  • EhCache: 자바에서 유명한 캐시 프레임워크 중 하나인 EhCache를 지원하는 캐시
  • CompositeCache: 1개 이상의 캐시 매니저를 사용하도록 지원해주는 Mixed 캐시
  • CaffeineCache: Java 8로 Guava 캐시를 재작성한 Caffeine 캐시를 사용하는 캐시
  • JCache: JSR-107 기반의 캐시를 사용하는 캐시


testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.rest-assured:rest-assured:5.3.1'

Expand Down
13 changes: 13 additions & 0 deletions backend/src/main/java/zipgo/admin/application/AdminService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import zipgo.admin.dto.BrandCreateRequest;
Expand All @@ -11,6 +12,10 @@
import zipgo.admin.dto.PrimaryIngredientCreateRequest;
import zipgo.brand.domain.Brand;
import zipgo.brand.domain.repository.BrandRepository;
import zipgo.pet.domain.Breed;
import zipgo.pet.domain.PetSize;
import zipgo.pet.domain.repository.BreedRepository;
import zipgo.pet.domain.repository.PetSizeRepository;
import zipgo.petfood.domain.Functionality;
import zipgo.petfood.domain.PetFood;
import zipgo.petfood.domain.PetFoodFunctionality;
Expand All @@ -31,6 +36,8 @@ public class AdminService {
private final FunctionalityRepository functionalityRepository;
private final PrimaryIngredientRepository primaryIngredientRepository;
private final PetFoodRepository petFoodRepository;
private final BreedRepository breedRepository;
private final PetSizeRepository petSizeRepository;

public Long createBrand(BrandCreateRequest request, String imageUrl) {
Brand brand = request.toEntity(imageUrl);
Expand Down Expand Up @@ -149,4 +156,10 @@ public void deletePetFood(Long petFoodId) {
petFoodRepository.deleteById(petFoodId);
}

@CacheEvict(cacheNames = "breeds", key = "'allBreeds'")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 어노테이션을 붙이지 않은 메서드에서만 캐싱이 되는건가요??
++ cacheNames랑 key는 각각 무슨 역할인지 궁금합니다!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 애노테이션을 붙이면 정보에 해당하는 캐시를 무효화하는 것이라 이해했습니당. cacheNames 는 캐시의 이름입니다. 키랑 다른 개념이요

캐시 매니저 빈을 생성할 때 캐시 이름을 등록하더라구요.

@Bean
public CacheManager cacheManager() {
    SimpleCacheManager cacheManager = new SimpleCacheManager();
    cacheManager.setCaches(caches());
    return cacheManager;
}

private List<CaffeineCache> caches() {
    return Arrays.stream(CacheType.values())
            .map(cacheType -> new CaffeineCache(cacheType.getName(), cache(cacheType))) // 요기
            .toList();
}
  • 가비가 정의한 캐시타입
@Getter
@AllArgsConstructor
public enum CacheType {

    BREEDS("breeds", 1),
    ;

    private final String name;
    private final int maxSize;

}

public Long createBreed(Long petSizeId, String breedName) {
PetSize petSize = petSizeRepository.getReferenceById(petSizeId);
Breed breed = breedRepository.save(Breed.builder().name(breedName).petSize(petSize).build());
return breed.getId();
}
Comment on lines +160 to +164
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏻👍🏻👍🏻

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package zipgo.admin.dto;

public record BreedCreateRequest(
Long petSizeId,
String breedName
) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import zipgo.admin.application.AdminService;
import zipgo.admin.dto.BrandCreateRequest;
import zipgo.admin.dto.BrandSelectResponse;
import zipgo.admin.dto.BreedCreateRequest;
import zipgo.admin.dto.FunctionalityCreateRequest;
import zipgo.admin.dto.FunctionalitySelectResponse;
import zipgo.admin.dto.PetFoodCreateRequest;
Expand Down Expand Up @@ -127,4 +128,12 @@ public ResponseEntity<Void> deletePetFood(@PathVariable Long petFoodId) {
return ResponseEntity.noContent().build();
}

@PostMapping("/breeds")
public ResponseEntity<Long> createBreeds(
@RequestBody BreedCreateRequest breedCreateRequest
) {
Long breedId = adminService.createBreed(breedCreateRequest.petSizeId(), breedCreateRequest.breedName());
return ResponseEntity.created(URI.create("/breeds/" + breedId)).build();
}

}
16 changes: 16 additions & 0 deletions backend/src/main/java/zipgo/common/cache/CacheType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package zipgo.common.cache;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum CacheType {

BREEDS("breeds", 1),
;

private final String name;
private final int maxSize;

}
39 changes: 39 additions & 0 deletions backend/src/main/java/zipgo/common/config/CacheConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package zipgo.common.config;

import java.util.Arrays;
import java.util.List;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import zipgo.common.cache.CacheType;

@EnableCaching
@Configuration
public class CacheConfig {

@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(caches());
return cacheManager;
}

private List<CaffeineCache> caches() {
return Arrays.stream(CacheType.values())
.map(cacheType -> new CaffeineCache(cacheType.getName(), cache(cacheType)))
.toList();
}

private Cache<Object, Object> cache(CacheType cacheType) {
return Caffeine.newBuilder()
.maximumSize(cacheType.getMaxSize())
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public Pet readPet(Long petId) {
}

public List<Breed> readBreeds() {
Breeds breeds = Breeds.from(breedRepository.findAll());
Breeds breeds = Breeds.from(breedRepository.findAllBreeds());
return breeds.getOrderedBreeds();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@

import java.util.List;
import java.util.Optional;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.jpa.repository.JpaRepository;
import zipgo.pet.domain.Breed;
import zipgo.pet.domain.PetSize;
import zipgo.pet.exception.BreedsNotFoundException;

public interface BreedRepository extends JpaRepository<Breed, Long> {

@Cacheable(cacheNames = "breeds", key = "'allBreeds'")
default List<Breed> findAllBreeds() {
return findAll();
}

Optional<Breed> findByPetSizeAndName(PetSize petSize, String name);

default Breed getByPetSizeAndName(PetSize petSize, String name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public GetReviewMetadataResponse getReviewMetadata() {
}

private List<Metadata> findAllBreeds() {
Breeds breeds = Breeds.from(breedRepository.findAll());
Breeds breeds = Breeds.from(breedRepository.findAllBreeds());
return breeds.getOrderedBreeds().stream()
.map(breed -> new Metadata(breed.getId(), breed.getName()))
.toList();
Expand Down