Skip to content
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
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ dependencies {
implementation 'org.springframework.retry:spring-retry'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'com.github.ben-manes.caffeine:caffeine'

/* Swagger */
implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.4.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableCaching
@EnableScheduling
@SpringBootApplication
public class PlanetrushApplication {
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/planetrush/planetrush/core/config/AppConfig.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package com.planetrush.planetrush.core.config;

import java.util.concurrent.TimeUnit;

import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.client.RestTemplate;

import com.github.benmanes.caffeine.cache.Caffeine;

@EnableAsync
@EnableRetry
@Configuration
Expand All @@ -16,4 +22,12 @@ public RestTemplate restTemplate() {
return new RestTemplate();
}

@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager("challengeProgressAvg");
cacheManager.setCaffeine(
Caffeine.newBuilder().expireAfterWrite(24, TimeUnit.HOURS).recordStats()
);
return cacheManager;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -53,7 +54,7 @@ public List<PlanetCollectionDto> getPlanetCollections(CollectionSearchCond searc
* <p>이 메서드는 반환값을 캐싱하여 관리합니다.</p>
* <p>캐시 미스가 발생하는 경우에만 플라스크 서버로 API 요청을 전송하여 새로운 데이터로 캐시에 저장합니다.</p>
*/
// TODO: 카페인 캐시를 이용하도록 변경
@Cacheable(cacheNames = "challengeProgressAvg", key = "#memberId")
@Override
public GetMyProgressAvgDto getMyProgressAvgPer(Long memberId) {
Member member = memberRepository.findById(memberId)
Expand Down
8 changes: 8 additions & 0 deletions src/test/java/com/planetrush/planetrush/ServiceUnitTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.planetrush.planetrush;

import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public abstract class ServiceUnitTest {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.planetrush.planetrush.member;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

import java.util.Optional;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;

import com.planetrush.planetrush.IntegrationTest;
import com.planetrush.planetrush.fixture.MemberFixture;
import com.planetrush.planetrush.infra.flask.util.FlaskApiClient;
import com.planetrush.planetrush.member.domain.Member;
import com.planetrush.planetrush.member.repository.MemberRepository;
import com.planetrush.planetrush.member.service.MemberService;
import com.planetrush.planetrush.member.service.dto.GetMyProgressAvgDto;

public class MemberIntegrationTest extends IntegrationTest {

@Autowired
private MemberService memberService;

@Autowired
private CacheManager cacheManager;

@MockBean
private MemberRepository memberRepository;

@MockBean
private FlaskApiClient flaskApiClient;

@DisplayName("통계 데이터를 조회할 때 캐싱된 값을 반환해야 한다.")
@Test
void should_use_cache_when_call_get_my_progress_avg() {
// GIVEN
Member member = Mockito.spy(MemberFixture.activeMember());
GetMyProgressAvgDto dto = GetMyProgressAvgDto.builder()
.challengeCnt(10)
.completionCnt(10)

.beautyAvg(1.0)
.lifeAvg(1.0)
.exerciseAvg(1.0)
.studyAvg(1.0)
.etcAvg(1.0)
.totalAvg(1.0)

.myBeautyAvg(1.0)
.myLifeAvg(1.0)
.myExerciseAvg(1.0)
.myStudyAvg(1.0)
.myEtcAvg(1.0)
.myTotalAvg(1.0)

.myBeautyPer(1.0)
.myLifePer(1.0)
.myExercisePer(1.0)
.myStudyPer(1.0)
.myEtcPer(1.0)
.myTotalPer(1.0)

.build();

// WHEN
when(member.getId()).thenReturn(1L);
when(memberRepository.findById(anyLong())).thenReturn(Optional.of(member));
when(flaskApiClient.getMyProgressAvg(anyLong())).thenReturn(dto);

for (int i = 0; i < 10; i++) {
memberService.getMyProgressAvgPer(member.getId());
}

// THEN
Cache cache = cacheManager.getCache("challengeProgressAvg");
assertThat(cache).isNotNull()
.extracting(it -> it.get(member.getId())).isNotNull()
.extracting(it -> it.get()).isNotNull()
.isInstanceOf(GetMyProgressAvgDto.class);
verify(flaskApiClient, times(1)).getMyProgressAvg(member.getId());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.planetrush.planetrush.member;

import org.mockito.InjectMocks;
import org.mockito.Mock;

import com.planetrush.planetrush.ServiceUnitTest;
import com.planetrush.planetrush.infra.flask.util.FlaskApiClient;
import com.planetrush.planetrush.member.repository.MemberRepository;
import com.planetrush.planetrush.member.repository.custom.ChallengeHistoryRepositoryCustom;
import com.planetrush.planetrush.member.service.MemberServiceImpl;

public abstract class MemberServiceUnitTest extends ServiceUnitTest {

@InjectMocks
protected MemberServiceImpl memberService;

@Mock
protected FlaskApiClient flaskApiClient;

@Mock
protected MemberRepository memberRepository;

@Mock
protected ChallengeHistoryRepositoryCustom challengeHistoryRepositoryCustom;
}
Loading