-
Notifications
You must be signed in to change notification settings - Fork 0
[YS-299] feat: 지역별 공고 개수 조회 UseCase Output 캐싱 적용 및 Redis 테스트 컨테이너 추가 #92
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
Changes from 5 commits
81a06f6
3e64ca7
af4a8b8
284dd5b
5a64aef
03691f8
3e07be7
f536112
b43e40f
9d8405b
e1cdb77
7885f28
d4c6b13
3a722d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -10,7 +10,10 @@ import com.dobby.backend.application.usecase.experiment.GetMyExperimentPostsUseC | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.dobby.backend.domain.exception.ExperimentAreaInCorrectException | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.dobby.backend.domain.exception.ExperimentAreaOverflowException | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.dobby.backend.domain.exception.InvalidRequestValueException | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.dobby.backend.domain.gateway.CacheGateway | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Area | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.dobby.backend.infrastructure.database.entity.enums.experiment.RecruitStatus | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.fasterxml.jackson.databind.ObjectMapper | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import jakarta.transaction.Transactional | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.stereotype.Service | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -30,9 +33,13 @@ class ExperimentPostService( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private val getExperimentPostTotalCountByCustomFilterUseCase: GetExperimentPostTotalCountByCustomFilterUseCase, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private val getMyExperimentPostsUseCase: GetMyExperimentPostsUseCase, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private val getMyExperimentPostTotalCountUseCase: GetMyExperimentPostTotalCountUseCase, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private val cacheGateway: CacheGateway, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private val objectMapper: ObjectMapper, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Transactional | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fun createNewExperimentPost(input: CreateExperimentPostUseCase.Input): CreateExperimentPostUseCase.Output { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cacheGateway.evict("experimentPostCounts:ALL") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cacheGateway.evict("experimentPostCounts:OPEN") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return createExperimentPostUseCase.execute(input) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -69,14 +76,36 @@ class ExperimentPostService( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fun getExperimentPostCounts(input: Any): Any { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return when (input) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (input is GetExperimentPostCountsByRegionUseCase.Input && input.region == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getCachedExperimentPostCounts(input.recruitStatus)?.let { return it } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val output = when (input) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
77
to
+82
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재는 전체 지역의 공고 개수 조회 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| is GetExperimentPostCountsByRegionUseCase.Input -> getExperimentPostCountsByRegionUseCase.execute(input) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| is GetExperimentPostCountsByAreaUseCase.Input -> getExperimentPostCountsByAreaUseCase.execute(input) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else -> throw IllegalArgumentException("Invalid input type: ${input::class.java.simpleName}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (input is GetExperimentPostCountsByRegionUseCase.Input && input.region == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cacheExperimentPostCounts(input.recruitStatus, output) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return output | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
77
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ObjectMapper 예외 처리가 필요합니다.
다음과 같이 예외 처리를 추가해주세요: if (input is GetExperimentPostCountsByRegionUseCase.Input && input.region == null) {
- cacheExperimentPostCounts(input.recruitStatus, output)
+ try {
+ cacheExperimentPostCounts(input.recruitStatus, output)
+ } catch (e: Exception) {
+ log.error("Failed to cache experiment post counts: ${e.message}")
+ }
}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private fun getCachedExperimentPostCounts(recruitStatus: RecruitStatus): GetExperimentPostCountsByRegionUseCase.Output? { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val cacheKey = "experimentPostCounts:$recruitStatus" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return cacheGateway.get(cacheKey)?.let { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| objectMapper.readValue(it, GetExperimentPostCountsByRegionUseCase.Output::class.java) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private fun cacheExperimentPostCounts(recruitStatus: RecruitStatus, output: Any) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val cacheKey = "experimentPostCounts:$recruitStatus" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cacheGateway.set(cacheKey, objectMapper.writeValueAsString(output)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+95
to
105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 직렬화/역직렬화 예외 처리가 필요합니다. ObjectMapper 작업 시 발생할 수 있는 예외에 대한 처리가 필요합니다. private fun getCachedExperimentPostCounts(recruitStatus: RecruitStatus): GetExperimentPostCountsByRegionUseCase.Output? {
val cacheKey = "experimentPostCounts:$recruitStatus"
return cacheGateway.get(cacheKey)?.let {
+ try {
objectMapper.readValue(it, GetExperimentPostCountsByRegionUseCase.Output::class.java)
+ } catch (e: Exception) {
+ log.error("Failed to deserialize cached value: ${e.message}")
+ null
+ }
}
}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fun validateFilter(input: GetExperimentPostsUseCase.Input) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private fun validateFilter(input: GetExperimentPostsUseCase.Input) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 제가 이 부분 놓친 것 같은데 바꾸어주셔서 감사합니다! 🙇♀️ |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val locationInfo = input.customFilter.locationTarget ?: return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| locationInfo.areas?.let { validateLocationAreaCount(it) } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.dobby.backend.domain.gateway | ||
|
|
||
| interface CacheGateway { | ||
| fun get(key: String): String? | ||
| fun set(key: String, value: String) | ||
| fun evict(key: String) | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,33 @@ | ||||||||||||||||||||||||
| package com.dobby.backend.infrastructure.gateway.cache | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import com.dobby.backend.domain.gateway.CacheGateway | ||||||||||||||||||||||||
| import org.springframework.core.env.Environment | ||||||||||||||||||||||||
| import org.springframework.data.redis.core.RedisTemplate | ||||||||||||||||||||||||
| import org.springframework.stereotype.Component | ||||||||||||||||||||||||
| import java.util.concurrent.TimeUnit | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Component | ||||||||||||||||||||||||
| class RedisCacheGatewayImpl( | ||||||||||||||||||||||||
| private val redisTemplate: RedisTemplate<String, String>, | ||||||||||||||||||||||||
| private val environment: Environment | ||||||||||||||||||||||||
| ) : CacheGateway { | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| private val cacheTimeout = 300L | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| override fun get(key: String): String? { | ||||||||||||||||||||||||
| return redisTemplate.opsForValue().get(getCacheKey(key)) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Comment on lines
+17
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Redis 작업 실패 시 예외 처리 추가 필요 Redis 작업 실패 시 적절한 예외 처리를 추가하면 장애 상황에서도 안정적으로 동작할 수 있습니다. override fun get(key: String): String? {
- return redisTemplate.opsForValue().get(getCacheKey(key))
+ return try {
+ redisTemplate.opsForValue().get(getCacheKey(key))
+ } catch (e: Exception) {
+ logger.error("캐시 조회 실패. key={}", key, e)
+ null
+ }
}📝 Committable suggestion
Suggested change
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 라비따이야, 레디스에 캐시가 없을 수도 있지. 안그러니? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Ji-soo708 네, 말씀하신대로입니다. 레디스에 캐시가 없는 것은 정상적인 상황이므로 현재 구현처럼 단순히 null을 반환하는 것이 더 적절합니다. 제가 제안했던 에러 로깅은 불필요했네요. 이전 제안을 철회하겠습니다. ✏️ Learnings added
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| override fun set(key: String, value: String) { | ||||||||||||||||||||||||
| redisTemplate.opsForValue().set(getCacheKey(key), value, cacheTimeout, TimeUnit.MINUTES) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| override fun evict(key: String) { | ||||||||||||||||||||||||
| redisTemplate.delete(getCacheKey(key)) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| private fun getCacheKey(key: String): String { | ||||||||||||||||||||||||
| val activeProfile = environment.activeProfiles.firstOrNull() ?: "local" | ||||||||||||||||||||||||
| return "$activeProfile:$key" | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| package com.dobby.backend.application.service | ||
|
|
||
| import com.dobby.backend.application.usecase.experiment.CreateExperimentPostUseCase | ||
| import com.dobby.backend.application.usecase.experiment.GetExperimentPostCountsByRegionUseCase | ||
| import com.dobby.backend.domain.gateway.CacheGateway | ||
| import com.dobby.backend.infrastructure.database.entity.enums.MatchType | ||
| import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Area | ||
| import com.dobby.backend.infrastructure.database.entity.enums.areaInfo.Region | ||
| import com.dobby.backend.infrastructure.database.entity.enums.experiment.RecruitStatus | ||
| import com.dobby.backend.infrastructure.database.entity.enums.experiment.TimeSlot | ||
| import com.dobby.backend.infrastructure.database.entity.enums.member.GenderType | ||
| import com.dobby.backend.infrastructure.database.entity.enums.member.MemberStatus | ||
| import com.dobby.backend.infrastructure.database.entity.enums.member.ProviderType | ||
| import com.dobby.backend.infrastructure.database.entity.enums.member.RoleType | ||
| import com.dobby.backend.infrastructure.database.entity.member.MemberEntity | ||
| import com.dobby.backend.infrastructure.database.repository.MemberRepository | ||
| import com.fasterxml.jackson.databind.ObjectMapper | ||
| import io.kotest.core.spec.style.BehaviorSpec | ||
| import io.kotest.matchers.shouldBe | ||
| import io.kotest.matchers.nulls.shouldBeNull | ||
| import io.mockk.* | ||
| import org.springframework.beans.factory.annotation.Autowired | ||
| import org.springframework.boot.test.context.SpringBootTest | ||
| import org.springframework.test.context.ActiveProfiles | ||
| import java.time.LocalDate | ||
| import java.time.LocalDateTime | ||
|
|
||
| @SpringBootTest | ||
| @ActiveProfiles("test") | ||
| class ExperimentPostServiceTest @Autowired constructor( | ||
| private val experimentPostService: ExperimentPostService, | ||
| private val memberRepository: MemberRepository, | ||
| private val cacheGateway: CacheGateway, | ||
| private val objectMapper: ObjectMapper | ||
| ) : BehaviorSpec({ | ||
|
|
||
| val getExperimentPostCountsByRegionUseCase = mockk<GetExperimentPostCountsByRegionUseCase>() | ||
| val createExperimentPostUseCase = mockk<CreateExperimentPostUseCase>() | ||
|
|
||
| lateinit var memberId: String | ||
|
|
||
| beforeSpec { | ||
| clearAllMocks() | ||
|
|
||
| val savedMember = memberRepository.save( | ||
| MemberEntity( | ||
| id = "1", | ||
| oauthEmail = "test@gmail.com", | ||
| contactEmail = "test@gmail.com", | ||
| provider = ProviderType.GOOGLE, | ||
| status = MemberStatus.ACTIVE, | ||
| role = RoleType.RESEARCHER, | ||
| name = "테스트", | ||
| createdAt = LocalDateTime.now(), | ||
| updatedAt = LocalDateTime.now(), | ||
| deletedAt = null | ||
| ) | ||
| ) | ||
| memberId = savedMember.id | ||
| } | ||
|
|
||
| given("Redis 캐시에 게시글 개수를 저장할 때") { | ||
| val input = GetExperimentPostCountsByRegionUseCase.Input(region = null, recruitStatus = RecruitStatus.ALL) | ||
| val output = GetExperimentPostCountsByRegionUseCase.Output(total = 100, area = emptyList()) | ||
|
|
||
| val cacheKey = "experimentPostCounts:ALL" | ||
|
|
||
| `when`("캐시에 데이터를 저장하고 조회하면") { | ||
| cacheGateway.set(cacheKey, objectMapper.writeValueAsString(output)) | ||
| val cachedData = cacheGateway.get(cacheKey) | ||
|
|
||
| then("저장된 데이터가 올바르게 반환되어야 한다") { | ||
| cachedData shouldBe objectMapper.writeValueAsString(output) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| given("새로운 게시글을 생성할 때") { | ||
| val cacheKey = "experimentPostCounts:ALL" | ||
| cacheGateway.set(cacheKey, objectMapper.writeValueAsString(GetExperimentPostCountsByRegionUseCase.Output(100, emptyList()))) | ||
|
|
||
| val mockInput = mockk<CreateExperimentPostUseCase.Input>(relaxed = true) | ||
|
|
||
| every { mockInput.memberId } returns memberId | ||
| every { mockInput.leadResearcher } returns "야뿌 연구 리더" | ||
| every { mockInput.univName } returns "야뿌대학교" | ||
| every { mockInput.region } returns Region.SEOUL | ||
| every { mockInput.area } returns Area.GWANAKGU | ||
| every { mockInput.detailedAddress } returns "야뿌 연구소" | ||
| every { mockInput.reward } returns "Gift Card" | ||
| every { mockInput.title } returns "새로운 실험" | ||
| every { mockInput.content } returns "참여해 참여해" | ||
| every { mockInput.alarmAgree } returns true | ||
| every { mockInput.startDate } returns LocalDate.of(2024, 6, 1) | ||
| every { mockInput.endDate } returns LocalDate.of(2024, 6, 30) | ||
| every { mockInput.matchType } returns MatchType.OFFLINE | ||
| every { mockInput.count } returns 3 | ||
| every { mockInput.timeRequired } returns TimeSlot.ABOUT_1H | ||
|
|
||
| val mockTargetGroupInfo = mockk<CreateExperimentPostUseCase.TargetGroupInfo>(relaxed = true) | ||
| every { mockTargetGroupInfo.startAge } returns 20 | ||
| every { mockTargetGroupInfo.endAge } returns 30 | ||
| every { mockTargetGroupInfo.genderType } returns GenderType.MALE | ||
| every { mockTargetGroupInfo.otherCondition } returns "Healthy" | ||
|
|
||
| every { mockInput.targetGroupInfo } returns mockTargetGroupInfo | ||
|
|
||
| val mockApplyMethodInfo = mockk<CreateExperimentPostUseCase.ApplyMethodInfo>(relaxed = true) | ||
| every { mockApplyMethodInfo.content } returns "이메일로 연락주세요" | ||
| every { mockApplyMethodInfo.formUrl } returns "https://test.com/apply" | ||
| every { mockApplyMethodInfo.phoneNum } returns "010-1234-5678" | ||
|
|
||
| every { mockInput.applyMethodInfo } returns mockApplyMethodInfo | ||
|
|
||
| val mockImageListInfo = mockk<CreateExperimentPostUseCase.ImageListInfo>(relaxed = true) | ||
| every { mockImageListInfo.images } returns listOf("https://test.com/image1.jpg", "https://test.com/image2.jpg") | ||
|
|
||
| every { mockInput.imageListInfo } returns mockImageListInfo | ||
|
|
||
| every { createExperimentPostUseCase.execute(any()) } returns mockk() | ||
|
|
||
| `when`("게시글을 생성하면") { | ||
| experimentPostService.createNewExperimentPost(mockInput) | ||
|
|
||
| then("캐시가 삭제되어야 한다") { | ||
| val cachedData = cacheGateway.get(cacheKey) | ||
| cachedData.shouldBeNull() | ||
| } | ||
| } | ||
| } | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package com.dobby.backend.config | ||
|
|
||
| import org.junit.jupiter.api.extension.BeforeAllCallback | ||
| import org.junit.jupiter.api.extension.ExtensionContext | ||
| import org.testcontainers.containers.GenericContainer | ||
| import org.testcontainers.utility.DockerImageName | ||
|
|
||
| object RedisTestContainerConfig : BeforeAllCallback { | ||
|
|
||
| private const val REDIS_IMAGE = "redis:7.0.8-alpine" | ||
| private const val REDIS_PORT = 6379 | ||
|
|
||
| private val redisContainer: GenericContainer<out GenericContainer<*>> = GenericContainer(DockerImageName.parse(REDIS_IMAGE)) | ||
| .apply { | ||
| withExposedPorts(REDIS_PORT) | ||
| withReuse(true) // 컨테이너 재사용 설정 | ||
| if (!isRunning) start() | ||
| } | ||
|
|
||
| override fun beforeAll(context: ExtensionContext?) { | ||
| System.setProperty("spring.data.redis.host", redisContainer.host) | ||
| System.setProperty("spring.data.redis.port", redisContainer.getMappedPort(REDIS_PORT).toString()) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TTL은 4시간으로 설정되어 있지만, 실시간 데이터를 반영하기 위해 실험 공고 게시글 등록이나 삭제 시 해당 캐시를 삭제합니다.