토이 프로젝트 스터디 #11

appti·2022년 6월 21일
0

토이 프로젝트 스터디 #11

  • 스터디 진행 날짜 : 6/21
  • 스터디 작업 날짜 : 6/18 ~ 6/21

토이 프로젝트 진행 사항

  • 카테고리 관련 코드 작성
    • RDB에서 계층형 구조를 적용하기 위해 Adjacency List 방식 사용
  • Redis를 활용한 Cache 적용

내용

Repository

public interface CategoryRepository extends JpaRepository<Category, Long> {

    @Query("SELECT c FROM Category c LEFT JOIN c.parent p ORDER BY p.id ASC NULLS FIRST, c.id ASC")
    List<Category> findAllOrderByParentIdAsc();

    Optional<Category> findByName(String name);
}
  • findAllOrderByParentIdAsc()
    • 부모 카테고리가 먼저 조회될 수 있도록 @Query로 직접 SQL 입력
    • NULL FIRST : 자식이 없는 부모 카테고리 먼저 조회

Service

@Service
@Transactional
@RequiredArgsConstructor
public class CategoryService {

    private final CategoryRepository categoryRepository;

    @Transactional(readOnly = true)
    @Cacheable(value = "category", key="'categories'")
    public List<NestedCategoryResponse> findAll() {
        List<Category> categories = categoryRepository.findAllOrderByParentIdAsc();
        return CategoryResponse.fromEntity(categories);
    }

    @CacheEvict(value = "category", key="'categories'", allEntries = true)
    public void save(CreateCategoryRequest request) {
        categoryRepository.save(
                CreateCategoryRequest.toEntity(
                        request.getName(),
                        Optional.ofNullable(request.getParentId())
                                .map(id -> categoryRepository.findById(id).orElseThrow(CategoryNotFoundException::new))
                                .orElse(null))
        );
    }

    public void delete(Long id) {
        if (!categoryRepository.existsById(id)) {
            throw new CategoryNotFoundException();
        }
        categoryRepository.deleteById(id);
    }
}
  • 카테고리의 경우 계층형 구조를 표현하기 위해 Adjacency List 사용
    • 가장 간단하지만 성능이 제일 좋지 않은 방식
    • 카테고리의 경우 비교적 변경되지 않으며, 조회의 경우 Redis를 활용해 Cache로 처리하면 된다고 판단
  • findAll()
    • @Cacheable을 통해 Redis에 저장된 값이 있으면 해당 값을 조회
  • save()
    • @CacheEvict를 통해 카테고리의 변경 사항을 Cache에 반영하기 위해 기존 값 삭제

Controller

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/categories")
public class CategoryController {

    private final CategoryService categoryService;

    @GetMapping()
    public ResponseEntity findAllCategories() {
        return new ResponseEntity(FindCategoryResponseConverter.convert(categoryService.findAll()), HttpStatus.OK);
    }

    @PostMapping()
    public ResponseEntity addCategory(@Validated @RequestBody CreateCategoryRequest request) {
        categoryService.save(request);
        return new ResponseEntity(HttpStatus.CREATED);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity deleteCategory(@PathVariable Long id) {
        categoryService.delete(id);
        return new ResponseEntity(HttpStatus.OK);
    }
}

Cache 동작 확인

  • Cache 적용 전 : 54

  • Cache 적용 후 : 6
profile
안녕하세요

0개의 댓글