• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

캐시를 포함한 Service Layer 테스트 가이드

23.09.24 15:29 작성 23.09.24 15:30 수정 조회수 208

1

안녕하세요. 우빈님!

캐시를 처음 적용해 보아서 캐시를 포함한 비지니스 로직을 어떻게 작성하는 것이 좋을까? 에 대한 고민이 있어 글 남깁니다...!

 

 

 

AS IS

@Transactional
public UosRestaurantMenuResponse getUosRestaurantMenu(UosRestaurantInput input) {

    // 학식 조회
    UosRestaurant findUosRestaurant = uosRestaurantRepository.findByCrawlingDateAndRestaurantNameAndMealType(input.getDate(),
                    input.getRestaurantName(), input.getMealType())
            .orElseThrow(() -> new UosRestaurantMenuException(UosRestaurantMenuException.NOT_FOUND_MENU));

    // 조회수 증가
    findUosRestaurant.increaseView();

    return UosRestaurantMenuResponse.of(findUosRestaurant);
}

 

TO BE

@Transactional
public UosRestaurantMenuResponse getUosRestaurantMenu(UosRestaurantInput input) {

    // 캐시에서 학식 조회
    Optional<CacheUosRestaurant> cacheUosRestaurant = cacheUosRestaurantRepository
            .findById(CacheUosRestaurant.createId(input));

    // 캐시에 학식이 존재하면
    if(cacheUosRestaurant.isPresent()) {
        // 조회수 증가
        cacheUosRestaurant.get().increaseView();
        CacheUosRestaurant saveCacheRestaurant = cacheUosRestaurantRepository.save(cacheUosRestaurant.get());

        return UosRestaurantMenuResponse.of(saveCacheRestaurant);
    }

    // 학식 조회
    UosRestaurant findUosRestaurant = uosRestaurantRepository.findByCrawlingDateAndRestaurantNameAndMealType(input.getDate(),
                    input.getRestaurantName(), input.getMealType())
            .orElseThrow(() -> new UosRestaurantMenuException(UosRestaurantMenuException.NOT_FOUND_MENU));

    // 조회수 증가
    findUosRestaurant.increaseView();

    // 캐시에 저장
    cacheUosRestaurantRepository.save(CacheUosRestaurant.of(findUosRestaurant));

    return UosRestaurantMenuResponse.of(findUosRestaurant);
}

 

  1. 캐시에 대한 로직이 추가될 때 위와 같이 하나의 서비스 레이어 메소드에 작성하는 것이 좋은걸까요?

  2. 캐시에 대한 테스트 코드를 작성할 때 캐시를 사용할 때와 캐시를 사용하지 않을 때를 상황을 구분하여 작성하는 것이 맞을까요..?(그것이 좋겠죠....? -> 기존 비지니스 로직 테스트 코드를 수정하는것이 최선일까? 에대한 의문이 들어서 질문 드렸습니다.)

  3. 스프링에서 CacheManager를 이용하여 @Cacheable 을 활용하는 방법이 있는데, CrudRepository를 사용하는 방법과 CacheManager를 사용하는 방법 중 어느 것이 더 좋은(?) 방법인지 말씀주시면 감사하겠습니다.
    4. 마지막으로 캐시를 사용할 때 깔끔하게 비지니스 로직을 작성할 수 있는 노하우 말씀주시면 감사하겠습니다.!!!



  4. 다소 질문이 난해한데 너그럽게 이해해주시면 감사하겠습니다.ㅠㅠ


    좋은 강의 잘 듣고 있습니다. 다음 강의도 기대할께요^^!!
    감사합니다.

답변 2

·

답변을 작성해보세요.

0

안녕하세요, 개발하는쿼카님! :)

먼저 구현하신 방식에 대한 질문이 있는데요, CacheUosRestaurant에 대한 캐시를 원본 UosRestaurant와 동일한 데이터베이스로 사용하고 계신걸까요? (Repository를 추상화하고 구현체를 달리 하신거라면 아닐 수도 있지만, 단순히 보았을 때는 그래보여서요. 🙂)

만약 위 가정이 맞다는 가정 하에 말씀드리자면, 캐시는 보통 주로 사용하는 데이터베이스에 대한 질의를 줄이기 위해 활용하는데요. (빠른 응답성)
자주 바뀌지 않거나, 변경 이벤트의 빈도에 비해 조회량(쿼리량)이 월등히 높은 도메인에서 매번 데이터베이스에 접근하는 것은 리소스의 낭비가 크기 때문입니다.

보통 캐시를 구현하는 방식은 서버 1대가 사용하는 로컬 캐시(ex. caffeine cache)나, 여러 대가 공통으로 사용하는 글로벌 캐시(ex. Redis를 활용한 캐시)가 있는데요.
위와 같이 캐시 데이터가 담긴 테이블만 다르고, 원본 데이터와 동일한 데이터베이스를 조회하도록 하는 것은 성능에 대한 이점이 없을 것 같아서, 한번 재고해보셨으면 좋겠습니다. :)

그럼 질문 주신 부분에 답변 드려볼게요.

 

1. 캐시에 대한 로직이 추가될 때 위와 같이 하나의 서비스 레이어 메소드에 작성하는 것이 좋은걸까요?

상황에 따른 나은 선택이 있는 것이지 정답이 있는건 아니어서 좋다, 나쁘다 이야기할 수는 없을 것 같은데, 가장 단순한 형태로 적용하면 말씀하신 방법대로 구현할 수 있겠네요. :)

 

2. 캐시에 대한 테스트 코드를 작성할 때 캐시를 사용할 때와 캐시를 사용하지 않을 때를 상황을 구분하여 작성하는 것이 맞을까요..?(그것이 좋겠죠....? -> 기존 비지니스 로직 테스트 코드를 수정하는것이 최선일까? 에대한 의문이 들어서 질문 드렸습니다.)

캐싱 로직과 비즈니스 로직이 섞여 있어서 그런 고민이 드는 것 같은데요, 테스트할 범위에 대해 정하면 될 것 같아요.
시간적 리소스가 충분하면 둘 다 테스트하면 당연히 좋겠죠? ㅎㅎ 만약 시간이 정말 부족하고 비즈니스 로직에 대한 테스팅의 우선순위가 훨씬 높다면 선택과 집중을 할 수도 있겠네요. (테스트 케이스 자체는 분리하는 것이 맞아 보여요.)

 

3. 스프링에서 CacheManager를 이용하여 @Cacheable 을 활용하는 방법이 있는데, CrudRepository를 사용하는 방법과 CacheManager를 사용하는 방법 중 어느 것이 더 좋은(?) 방법인지 말씀주시면 감사하겠습니다.

위에서 어느정도 답변 드린 것 같네요. ㅎㅎ

 

4. 마지막으로 캐시를 사용할 때 깔끔하게 비지니스 로직을 작성할 수 있는 노하우 말씀주시면 감사하겠습니다.!!!

캐시 로직과 비즈니스 로직을 분리하고 싶다면, 데이터를 가져오는 레이어(ex. repository)에 AOP 형태로 캐시로직을 적용해볼 수 있을 것 같아요.
그렇게 되면 비즈니스 로직을 처리하는 서비스 레이어에서는 캐시에 대한 내용을 제거할 수 있겠네요.

다만 이 방법은 AOP에 대한 이해와 캐싱을 구현하기 위한 구현체, 예외 상황에 대한 대처에 따라 많이 달라질 수 있을 것 같으니 상황에 맞게 고민해서 사용하시면 좋을 것 같습니다.

 

캐싱이 워낙 범위가 넓은 주제라 상세하게 답변 드리기가 어렵긴 하네요. 관련해서 많은 학습과 여러가지 시도 해보시면 좋을 듯 합니다. ㅎㅎ
궁금한 부분이 해소되셨기를 바라요. 감사합니다. :)

우와 정말로 상세한 답변 감사합니다!!!! 🙇🏻
행복한 명절 보내세요~!!

0

인프런 AI 인턴님의 프로필

인프런 AI 인턴

2023.09.26

안녕하세요, 인프런 AI 인턴이에요.

캐시를 포함한 비지니스 로직을 작성하는 방법에 대해 여쭤보셨군요. 기존의 코드에서는 캐시를 사용하지 않고 DB를 통해 학식을 조회한 후 조회수를 증가시키는 방식으로 동작하고 있었습니다.

TO BE 코드에서는 캐시에서 학식을 조회한 후, 캐시에 학식이 존재하는 경우에는 조회수를 증가시키고 캐시를 업데이트하는 방식으로 변경되었습니다. 캐시를 사용함으로써 DB에 접근하지 않고도 조회수를 증가시키는 횟수를 줄일 수 있습니다.

코드에는 이미 트랜잭션 어노테이션이 추가되어 있어서, 메소드 실행 도중 예외가 발생하면 이전 상태로 롤백되도록 설정되어 있습니다.

테스트를 작성할 때에는 다음과 같은 시나리오를 가정해볼 수 있습니다.
1. 캐시에 학식이 존재하는 경우와 존재하지 않는 경우를 모두 테스트해봅니다.
2. 조회수가 정상적으로 증가하는지 확인합니다.
3. 캐시에 학식이 존재하는 경우에는 캐시의 상태가 예상대로 변경되었는지 확인합니다.
4. 예외 상황을 테스트해봅니다. (학식이 존재하지 않는 경우 등)

더 자세한 내용이 필요하시면 추가로 질문해주세요. 감사합니다!