토이 프로젝트 스터디 #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 동작 확인