Skip to content

Conversation

@Ji-soo708
Copy link
Member

@Ji-soo708 Ji-soo708 commented Feb 10, 2025

💡 작업 내용

  • 지역별 공고 개수 조회 UseCase Output 캐싱 적용
    지역별 공고 개수 조회 시 (Region이 null인 경우에만)

    • 스크린샷 2025-02-10 오후 7 12 55
  • Redis 테스트 컨테이너 추가
    레디스가 적용된 ExperimentPostService 테스트 코드 실행하는 동안에만 레디스 컨테이너가 활성화됨

    • 스크린샷 2025-02-10 오후 10 56 51
  • 지역별 공고 개수 조회 서비스 테스트 코드 추가

✅ 셀프 체크리스트

  • PR 제목을 형식에 맞게 작성했나요?
  • 브랜치 전략에 맞는 브랜치에 PR을 올리고 있나요?
  • 테스트는 잘 통과했나요?
  • 빌드에 성공했나요?
  • 본인을 assign 해주세요.
  • 해당 PR에 맞는 label을 붙여주세요.

🙋🏻‍ 확인해주세요

  • 로컬에 도커(docker desktop)를 설치해서 실행해야 정상적으로 동작합니다❗️

🔗 Jira 티켓


https://yappsocks.atlassian.net/browse/YS-299

Summary by CodeRabbit

  • 새로운 기능
    • CI 워크플로우에 Redis 서비스가 추가되어 통합 테스트가 가능해졌습니다.
    • 실험 게시물 기능에 캐싱이 도입되어, 게시물 생성 시 관련 통계 정보가 자동으로 갱신됩니다.
    • Testcontainers 라이브러리를 통해 컨테이너화된 환경에서의 통합 테스트 지원이 추가되었습니다.
  • 버그 수정
    • 테스트 환경에서 Redis 연결을 위한 설정이 업데이트되었습니다.
  • 문서화
    • 테스트 클래스에 대한 프로파일 설정이 추가되었습니다.
  • 구성 변경
    • 테스트용 데이터베이스 설정이 인메모리 H2 데이터베이스로 변경되었습니다.

@Ji-soo708 Ji-soo708 self-assigned this Feb 10, 2025
@coderabbitai
Copy link

coderabbitai bot commented Feb 10, 2025

"""

Walkthrough

CI 파이프라인에 Redis 서비스를 추가하여 Redis와의 통합 테스트가 가능하도록 워크플로우가 수정되었으며, Gradle 빌드 스크립트에 Testcontainers 관련 종속성이 추가되었습니다. ExperimentPostService에는 캐시 게이트웨이와 ObjectMapper가 주입되어 캐시 관련 로직이 도입되었고, 캐시를 조회 및 저장하는 전용 메서드가 추가되었습니다. 또한, CacheGateway 인터페이스와 이를 구현하는 RedisCacheGatewayImpl 클래스가 새로 추가되었으며, 테스트 환경에서는 Redis 테스트 컨테이너와 in-memory H2 데이터베이스를 사용하도록 설정이 변경되었습니다.

Changes

파일 경로 변경 요약
.github/workflows/ci-dev.yml CI 워크플로우에 Redis 서비스 추가, 테스트 단계 이름 변경("Build with Gradle" → "Run tests with Gradle") 및 환경 변수(SPRING_REDIS_HOST, SPRING_REDIS_PORT) 설정
build.gradle.kts Testcontainers 종속성(org.testcontainers:testcontainers, ...:junit-jupiter) 추가
src/main/kotlin/.../ExperimentPostService.kt 캐시 관련 로직 추가 (캐시 조회, 저장, 삭제 메서드 도입 및 접근제어 변경)
src/main/kotlin/.../CacheGateway.kt &
src/main/kotlin/.../RedisCacheGatewayImpl.kt
캐시 연산을 위한 CacheGateway 인터페이스 및 Redis 기반 구현체 추가, 캐시 타임아웃 로직 포함
src/test/kotlin/.../ExperimentPostServiceTest.kt
src/test/kotlin/.../RedisTestContainer.kt
src/test/kotlin/.../S3PreSignedUrlProviderTest.kt
Redis 통합 테스트를 위한 테스트 클래스와 Redis 테스트 컨테이너 추가, 테스트 프로파일 적용
src/test/resources/application-test.yml 데이터소스 URL을 in-memory H2로 전환, driver-class-name 및 Hibernate dialect 수정, Redis 호스트 변경

Sequence Diagram(s)

sequenceDiagram
    participant Client as 클라이언트
    participant Service as ExperimentPostService
    participant Cache as CacheGateway
    participant UseCase as 사용 사례 처리
    
    Client->>Service: getExperimentPostCounts(input)
    Service->>Cache: get(key)
    alt 캐시 데이터가 존재할 경우
        Cache-->>Service: 캐시 데이터 반환
        Service-->>Client: 캐시 데이터 반환
    else 캐시 데이터가 없음
        Service->>UseCase: 실제 계산 요청
        UseCase-->>Service: 계산 결과 반환
        Service->>Cache: set(key, 결과)
        Service-->>Client: 계산 결과 반환
    end
Loading

Poem

귀여운 토끼가 기쁜 소식을 전해요,
코드 속 변화가 새봄처럼 피어납니다.
Redis와 캐시가 함께 노래하며,
테스트 컨테이너가 활짝 웃네요.
작은 코드 한 줄마다 꿈이 자라나요.
🐰✨ 즐거운 변화, 모두에게 행운이길!
"""


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot changed the title feat: 지역별 공고 개수 조회 UseCase Output 캐싱 적용 및 Redis 테스트 컨테이너 추가 [YS-299] feat: 지역별 공고 개수 조회 UseCase Output 캐싱 적용 및 Redis 테스트 컨테이너 추가 Feb 10, 2025
@Ji-soo708 Ji-soo708 added ⚙️ CHORE config, workflow.yaml ✅ TEST 테스트 코드 추가 ✨ FEATURE 기능 추가 🔒 HOLD 홀드 labels Feb 10, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (6)
src/main/kotlin/com/dobby/backend/domain/gateway/CacheGateway.kt (1)

3-7: 인터페이스에 KDoc 문서화 추가 필요

각 메서드의 동작과 매개변수에 대한 설명을 KDoc으로 추가하면 인터페이스의 사용성이 향상될 것 같습니다.

 interface CacheGateway {
+    /**
+     * 주어진 키에 해당하는 캐시된 값을 조회합니다.
+     * @param key 조회할 캐시 키
+     * @return 캐시된 값 또는 캐시 미스 시 null
+     */
     fun get(key: String): String?
+    
+    /**
+     * 주어진 키로 값을 캐시에 저장합니다.
+     * @param key 저장할 캐시 키
+     * @param value 저장할 값
+     */
     fun set(key: String, value: String)
+    
+    /**
+     * 주어진 키에 해당하는 캐시 항목을 제거합니다.
+     * @param key 제거할 캐시 키
+     */
     fun evict(key: String)
 }
src/test/kotlin/com/dobby/backend/config/RedisTestContainerConfig.kt (1)

13-18: 컨테이너 시작 실패 시 예외 처리 및 로깅 추가 필요

컨테이너 시작 실패 시 적절한 예외 처리와 로깅을 추가하면 디버깅이 용이해질 것 같습니다.

 private val redisContainer: GenericContainer<out GenericContainer<*>> = GenericContainer(DockerImageName.parse(REDIS_IMAGE))
     .apply {
         withExposedPorts(REDIS_PORT)
         withReuse(true) // 컨테이너 재사용 설정
-        if (!isRunning) start()
+        if (!isRunning) {
+            try {
+                start()
+                logger.info("Redis 테스트 컨테이너가 시작되었습니다. host={}, port={}", host, getMappedPort(REDIS_PORT))
+            } catch (e: Exception) {
+                logger.error("Redis 테스트 컨테이너 시작 실패", e)
+                throw IllegalStateException("Redis 테스트 컨테이너를 시작할 수 없습니다", e)
+            }
+        }
     }
src/main/kotlin/com/dobby/backend/infrastructure/gateway/cache/RedisCacheGatewayImpl.kt (1)

15-15: 캐시 타임아웃을 설정 파일로 이동 필요

하드코딩된 캐시 타임아웃 값을 설정 파일로 이동하면 환경에 따라 유연하게 조정할 수 있습니다.

-    private val cacheTimeout = 300L
+    @Value("\${cache.timeout-minutes:300}")
+    private lateinit var cacheTimeout: Long
src/test/kotlin/com/dobby/backend/application/service/ExperimentPostServiceTest.kt (2)

62-76: 캐시 실패 케이스에 대한 테스트 추가 필요

캐시 조회/저장 실패 시의 동작을 검증하는 테스트 케이스를 추가하면 좋을 것 같습니다.

+    given("Redis 캐시 작업이 실패할 때") {
+        val input = GetExperimentPostCountsByRegionUseCase.Input(region = null, recruitStatus = RecruitStatus.ALL)
+        val cacheKey = "experimentPostCounts:ALL"
+
+        `when`("캐시 저장이 실패하면") {
+            every { redisTemplate.opsForValue().set(any(), any(), any(), any()) } throws RuntimeException("Redis 연결 실패")
+            
+            then("예외가 발생하지 않고 정상적으로 처리되어야 한다") {
+                shouldNotThrow<Exception> {
+                    cacheGateway.set(cacheKey, "test-value")
+                }
+            }
+        }
+    }

78-130: 게시글 생성 실패 시 캐시 동작 테스트 추가 필요

게시글 생성이 실패했을 때 캐시가 어떻게 동작해야 하는지 검증하는 테스트를 추가하면 좋을 것 같습니다.

+    given("게시글 생성이 실패할 때") {
+        val cacheKey = "experimentPostCounts:ALL"
+        val initialCacheValue = objectMapper.writeValueAsString(GetExperimentPostCountsByRegionUseCase.Output(100, emptyList()))
+        cacheGateway.set(cacheKey, initialCacheValue)
+
+        every { createExperimentPostUseCase.execute(any()) } throws RuntimeException("게시글 생성 실패")
+
+        `when`("게시글을 생성하면") {
+            shouldThrow<RuntimeException> {
+                experimentPostService.createNewExperimentPost(mockInput)
+            }
+
+            then("캐시는 그대로 유지되어야 한다") {
+                val cachedData = cacheGateway.get(cacheKey)
+                cachedData shouldBe initialCacheValue
+            }
+        }
+    }
src/main/kotlin/com/dobby/backend/application/service/ExperimentPostService.kt (1)

78-94: 캐시 구현에 TTL 추가 및 예외 처리 개선이 필요합니다.

캐시 구현이 잘 되어 있지만, 다음 개선사항을 고려해주세요:

  1. 캐시 데이터의 유효 기간(TTL) 설정
  2. 캐시 작업 실패에 대한 예외 처리 추가

예시 구현:

 if (input is GetExperimentPostCountsByRegionUseCase.Input && input.region == null) {
-    getCachedExperimentPostCounts(input.recruitStatus)?.let { return it }
+    try {
+        getCachedExperimentPostCounts(input.recruitStatus)?.let { return it }
+    } catch (e: Exception) {
+        // 캐시 조회 실패 시 로깅하고 계속 진행
+        log.warn("Failed to get cached counts: ${e.message}")
+    }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a49fd2 and 5a64aef.

📒 Files selected for processing (7)
  • .github/workflows/ci-dev.yml (2 hunks)
  • build.gradle.kts (1 hunks)
  • src/main/kotlin/com/dobby/backend/application/service/ExperimentPostService.kt (3 hunks)
  • src/main/kotlin/com/dobby/backend/domain/gateway/CacheGateway.kt (1 hunks)
  • src/main/kotlin/com/dobby/backend/infrastructure/gateway/cache/RedisCacheGatewayImpl.kt (1 hunks)
  • src/test/kotlin/com/dobby/backend/application/service/ExperimentPostServiceTest.kt (1 hunks)
  • src/test/kotlin/com/dobby/backend/config/RedisTestContainerConfig.kt (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (3)
src/main/kotlin/com/dobby/backend/application/service/ExperimentPostService.kt (1)

36-37: 캐시 무효화 전략이 적절합니다.

새로운 실험 공고가 생성될 때 관련된 모든 캐시를 무효화하는 것이 올바른 접근입니다.

Also applies to: 41-42

.github/workflows/ci-dev.yml (1)

15-37: Redis 테스트 환경 구성이 적절합니다.

CI 환경에 Redis 서비스가 잘 구성되어 있으며, 환경 변수도 올바르게 설정되었습니다.

build.gradle.kts (1)

83-84: Testcontainers 의존성이 적절히 추가되었습니다.

테스트 스코프에 Testcontainers 의존성이 올바르게 추가되었습니다.

Comment on lines +17 to +19
override fun get(key: String): String? {
return redisTemplate.opsForValue().get(getCacheKey(key))
}
Copy link

@coderabbitai coderabbitai bot Feb 10, 2025

Choose a reason for hiding this comment

The 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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
override fun get(key: String): String? {
return redisTemplate.opsForValue().get(getCacheKey(key))
}
override fun get(key: String): String? {
return try {
redisTemplate.opsForValue().get(getCacheKey(key))
} catch (e: Exception) {
logger.error("캐시 조회 실패. key={}", key, e)
null
}
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

라비따이야, 레디스에 캐시가 없을 수도 있지. 안그러니?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ji-soo708 네, 말씀하신대로입니다. 레디스에 캐시가 없는 것은 정상적인 상황이므로 현재 구현처럼 단순히 null을 반환하는 것이 더 적절합니다. 제가 제안했던 에러 로깅은 불필요했네요. 이전 제안을 철회하겠습니다.


✏️ Learnings added
Learnt from: Ji-soo708
PR: YAPP-Github/Gradmeet-BE#92
File: src/main/kotlin/com/dobby/backend/infrastructure/gateway/cache/RedisCacheGatewayImpl.kt:17-19
Timestamp: 2025-02-10T11:52:40.151Z
Learning: Redis cache operations that return null (like get) should not log errors as cache misses are part of normal operation. The null return value already appropriately handles both cache misses and error cases.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +96 to 106
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))
}
Copy link

Choose a reason for hiding this comment

The 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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

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))
}
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
}
}
}
private fun cacheExperimentPostCounts(recruitStatus: RecruitStatus, output: Any) {
val cacheKey = "experimentPostCounts:$recruitStatus"
cacheGateway.set(cacheKey, objectMapper.writeValueAsString(output))
}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/test/kotlin/com/dobby/backend/config/RedisTestContainer.kt (1)

10-15: Redis 컨테이너 구성 개선이 필요합니다.

Redis 컨테이너 설정에서 다음 사항들을 고려해 주세요:

  1. Redis 버전을 상수로 분리하여 관리
  2. 포트 바인딩을 동적으로 할당하도록 수정

다음과 같이 수정을 제안합니다:

+    private const val REDIS_VERSION = "7.0.8-alpine"
+    private const val REDIS_PORT = 6379
-    private val redis: GenericContainer<*> = GenericContainer(DockerImageName.parse("redis:7.0.8-alpine"))
+    private val redis: GenericContainer<*> = GenericContainer(DockerImageName.parse("redis:$REDIS_VERSION"))
         .apply {
-            withExposedPorts(6379)
-            portBindings = listOf("6379:6379")
+            withExposedPorts(REDIS_PORT)
             withReuse(true)
         }
src/test/kotlin/com/dobby/backend/infrastructure/s3/S3PreSignedUrlProviderTest.kt (2)

17-18: 테스트 프로파일 사용에 대한 문서화가 필요합니다.

테스트 프로파일 사용 목적과 설정에 대한 설명을 주석으로 추가하면 좋을 것 같습니다.

다음과 같이 수정을 제안합니다:

+/**
+ * 테스트 프로파일을 사용하여 Redis 테스트 컨테이너와 함께 실행됩니다.
+ * application-test.yml의 설정이 적용됩니다.
+ */
 @SpringBootTest
 @ActiveProfiles("test")
 class S3PreSignedUrlProviderTest : BehaviorSpec({

47-55: 추가적인 에러 케이스 테스트가 필요합니다.

현재는 확장자가 없는 경우만 테스트하고 있습니다. 다음과 같은 추가 케이스들도 테스트하면 좋을 것 같습니다:

  • 지원하지 않는 파일 확장자
  • 파일 이름이 너무 긴 경우
  • 특수 문자가 포함된 경우

테스트 케이스 추가를 제안드립니다. 필요하시다면 테스트 코드 작성을 도와드릴 수 있습니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a64aef and 3e07be7.

📒 Files selected for processing (4)
  • src/test/kotlin/com/dobby/backend/application/service/ExperimentPostServiceTest.kt (1 hunks)
  • src/test/kotlin/com/dobby/backend/config/RedisTestContainer.kt (1 hunks)
  • src/test/kotlin/com/dobby/backend/infrastructure/s3/S3PreSignedUrlProviderTest.kt (1 hunks)
  • src/test/resources/application-test.yml (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/kotlin/com/dobby/backend/application/service/ExperimentPostServiceTest.kt
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (3)
src/test/resources/application-test.yml (3)

3-4: 데이터베이스 설정 업데이트 확인
Spring datasource의 URL이 파일 기반 H2에서 인메모리 H2로 변경되었으며, driver-class-name이 추가되었습니다. 이 설정은 테스트 시 데이터베이스 초기화와 격리된 환경을 보장하는 데 적합합니다.


16-16: Hibernate Dialect 변경 검토
Hibernate Dialect가 MySQL8에서 H2용으로 변경되었습니다. 인메모리 H2 데이터베이스와의 호환성을 고려한 올바른 설정입니다.


37-37: Redis 호스트 주소 명시성 개선
Redis의 host 값이 localhost에서 127.0.0.1로 변경되어, 네트워크 환경에 따라 발생할 수 있는 모호성을 제거하고 명확한 IP 주소로 지정한 점이 좋습니다.

Comment on lines 20 to 24
val redisHost = "127.0.0.1"
val redisPort = "6379"

System.setProperty("spring.data.redis.host", redisHost)
System.setProperty("spring.data.redis.port", redisPort)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

시스템 프로퍼티 관리 개선이 필요합니다.

테스트 실행 후 시스템 프로퍼티를 정리하지 않으면 다른 테스트에 영향을 줄 수 있습니다.

다음과 같이 수정을 제안합니다:

     override suspend fun beforeSpec(spec: Spec) {
         redis.start()
 
-        val redisHost = "127.0.0.1"
-        val redisPort = "6379"
+        val redisHost = redis.host
+        val redisPort = redis.firstMappedPort.toString()
 
         System.setProperty("spring.data.redis.host", redisHost)
         System.setProperty("spring.data.redis.port", redisPort)
     }
 
     override suspend fun afterSpec(spec: Spec) {
+        System.clearProperty("spring.data.redis.host")
+        System.clearProperty("spring.data.redis.port")
         redis.stop()
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val redisHost = "127.0.0.1"
val redisPort = "6379"
System.setProperty("spring.data.redis.host", redisHost)
System.setProperty("spring.data.redis.port", redisPort)
override suspend fun beforeSpec(spec: Spec) {
redis.start()
- val redisHost = "127.0.0.1"
- val redisPort = "6379"
+ val redisHost = redis.host
+ val redisPort = redis.firstMappedPort.toString()
System.setProperty("spring.data.redis.host", redisHost)
System.setProperty("spring.data.redis.port", redisPort)
}
override suspend fun afterSpec(spec: Spec) {
+ System.clearProperty("spring.data.redis.host")
+ System.clearProperty("spring.data.redis.port")
redis.stop()
}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/test/kotlin/com/dobby/backend/config/RedisTestContainer.kt (1)

19-31: 시스템 프로퍼티 관리 개선이 필요합니다.

테스트 실행 후 시스템 프로퍼티를 정리하지 않으면 다른 테스트에 영향을 줄 수 있습니다. 또한, Redis 호스트를 하드코딩하는 대신 컨테이너의 호스트 정보를 사용하는 것이 더 안전합니다.

     override suspend fun beforeSpec(spec: Spec) {
         redis.start()
 
-        val redisHost = "127.0.0.1"
-        val redisPort = redis.getMappedPort(6379).toString()
+        val redisHost = redis.host
+        val redisPort = redis.firstMappedPort.toString()
 
         System.setProperty("spring.data.redis.host", redisHost)
         System.setProperty("spring.data.redis.port", redisPort)
     }
 
     override suspend fun afterSpec(spec: Spec) {
+        System.clearProperty("spring.data.redis.host")
+        System.clearProperty("spring.data.redis.port")
         redis.stop()
     }
🧹 Nitpick comments (1)
src/test/kotlin/com/dobby/backend/config/RedisTestContainer.kt (1)

11-17: 컨테이너 구성 개선이 필요합니다.

다음과 같은 개선사항을 제안드립니다:

  1. Redis 버전을 상수로 분리하여 관리하면 업데이트가 용이해집니다.
  2. 로컬 환경에서 포트 충돌을 방지하기 위해 동적 포트 바인딩을 고려해보세요.
 object RedisTestContainer : TestListener {
+    private const val REDIS_VERSION = "7.0.8-alpine"
     private val isCI = System.getenv("GITHUB_ACTIONS") == "true"
 
-    private val redis: GenericContainer<*> = GenericContainer(DockerImageName.parse("redis:7.0.8-alpine")).apply {
+    private val redis: GenericContainer<*> = GenericContainer(DockerImageName.parse("redis:$REDIS_VERSION")).apply {
         withExposedPorts(6379)
-
-        if (!isCI) {
-            setPortBindings(listOf("6379:6379"))
-        }
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f536112 and 9d8405b.

📒 Files selected for processing (1)
  • src/test/kotlin/com/dobby/backend/config/RedisTestContainer.kt (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build

Comment on lines 78 to +83
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) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재는 전체 지역의 공고 개수 조회 (region == null)가 가장 빈번하게 호출될 것으로 예상되어 이 경우에만 캐싱을 적용했습니다. 만약 특정 지역에 대한 조회 요청도 증가하는 것이 확인된다면, 해당 부분에 대한 캐싱 정책도 추가로 검토할 예정입니다~

Comment on lines 39 to 43
@Transactional
fun createNewExperimentPost(input: CreateExperimentPostUseCase.Input): CreateExperimentPostUseCase.Output {
cacheGateway.evict("experimentPostCounts:ALL")
cacheGateway.evict("experimentPostCounts:OPEN")
return createExperimentPostUseCase.execute(input)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TTL은 4시간으로 설정되어 있지만, 실시간 데이터를 반영하기 위해 실험 공고 게시글 등록이나 삭제 시 해당 캐시를 삭제합니다.

Comment on lines 8 to 17
object RedisTestContainer : TestListener {
private val isCI = System.getenv("GITHUB_ACTIONS") == "true"

private val redis: GenericContainer<*> = GenericContainer(DockerImageName.parse("redis:7.0.8-alpine")).apply {
withExposedPorts(6379)

if (!isCI) {
setPortBindings(listOf("6379:6379"))
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RedisTestContainer는 테스트 실행 시 실제 Redis 인스턴스를 컨테이너로 띄워주는 역할을 합니다.
이를 통해 테스트 환경에서 일관된 Redis 환경을 제공하며, 독립적인 검증이 가능하게 합니다. 테스트 종료 후 자동으로 컨테이너를 정리하여 개발 환경에 영향을 주지 않습니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/main/kotlin/com/dobby/backend/application/service/ExperimentPostService.kt (1)

96-106: 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("캐시된 값 역직렬화 실패: ${e.message}")
+                null
+            }
         }
     }

     private fun cacheExperimentPostCounts(recruitStatus: RecruitStatus, output: Any) {
         val cacheKey = "experimentPostCounts:$recruitStatus"
+        try {
             cacheGateway.set(cacheKey, objectMapper.writeValueAsString(output))
+        } catch (e: Exception) {
+            log.error("캐시 값 직렬화 실패: ${e.message}")
+        }
     }
🧹 Nitpick comments (2)
src/main/kotlin/com/dobby/backend/application/service/ExperimentPostService.kt (2)

41-42: 캐시 키 제거 로직 중복을 제거하는 것이 좋겠습니다.

createNewExperimentPost와 deleteExperimentPost에서 동일한 캐시 키를 제거하는 로직이 중복되어 있습니다.

다음과 같이 private 메서드로 추출하는 것을 제안합니다:

+    private fun evictExperimentPostCountsCaches() {
+        cacheGateway.evict("experimentPostCounts:ALL")
+        cacheGateway.evict("experimentPostCounts:OPEN")
+    }

     @Transactional
     fun createNewExperimentPost(input: CreateExperimentPostUseCase.Input): CreateExperimentPostUseCase.Output {
-        cacheGateway.evict("experimentPostCounts:ALL")
-        cacheGateway.evict("experimentPostCounts:OPEN")
+        evictExperimentPostCountsCaches()
         return createExperimentPostUseCase.execute(input)
     }

     @Transactional
     fun deleteExperimentPost(input: DeleteExperimentPostUseCase.Input) {
-        cacheGateway.evict("experimentPostCounts:ALL")
-        cacheGateway.evict("experimentPostCounts:OPEN")
+        evictExperimentPostCountsCaches()
         deleteExperimentPostUseCase.execute(input)
     }

Also applies to: 154-155


78-94: 캐시 키를 상수로 분리하면 좋겠습니다.

캐시 키가 여러 곳에서 문자열 리터럴로 사용되고 있습니다. 이는 오타나 불일치의 위험이 있습니다.

다음과 같이 companion object에 상수로 분리하는 것을 제안합니다:

class ExperimentPostService(
    // ... 생성자 매개변수들
) {
+    companion object {
+        private const val CACHE_KEY_ALL = "experimentPostCounts:ALL"
+        private const val CACHE_KEY_OPEN = "experimentPostCounts:OPEN"
+        private fun getCacheKey(recruitStatus: RecruitStatus) = "experimentPostCounts:$recruitStatus"
+    }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9d8405b and e1cdb77.

📒 Files selected for processing (4)
  • src/main/kotlin/com/dobby/backend/application/service/ExperimentPostService.kt (4 hunks)
  • src/main/kotlin/com/dobby/backend/infrastructure/gateway/cache/RedisCacheGatewayImpl.kt (1 hunks)
  • src/test/kotlin/com/dobby/backend/application/service/ExperimentPostServiceTest.kt (1 hunks)
  • src/test/kotlin/com/dobby/backend/config/RedisTestContainer.kt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/test/kotlin/com/dobby/backend/config/RedisTestContainer.kt
  • src/test/kotlin/com/dobby/backend/application/service/ExperimentPostServiceTest.kt
  • src/main/kotlin/com/dobby/backend/infrastructure/gateway/cache/RedisCacheGatewayImpl.kt
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (2)
src/main/kotlin/com/dobby/backend/application/service/ExperimentPostService.kt (2)

36-37: 의존성 주입이 올바르게 구현되었습니다!

캐시 관련 의존성이 적절하게 추가되었습니다.


108-108: validateFilter 메서드의 가시성 변경이 적절합니다!

내부에서만 사용되는 메서드를 private으로 변경한 것은 캡슐화 측면에서 좋은 변경입니다.

@Ji-soo708 Ji-soo708 requested a review from chock-cho February 10, 2025 15:24
Copy link
Member

@chock-cho chock-cho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가장 빈번하게 호출될 전체 공고 개수(region == null) 조회에만 캐싱을 적용한 점이 적절한 설계로 보여요 💪
또한, 레디스 테스트 컨테이너를 도입해 테스트 환경에서도 일관된 검증이 가능하도록 한 점도 지수님 코드랑 자료 서치하면서 공부를 좀 해야겠다는 생각을 하게 됐습니다. 👍

다만, 코드를 보면서 몇 가지 의문이 든 점을 쭉 써보았는데, 확인해주시고 코멘트 남겨주시면 감사하겠습니다 🙇‍♀️

Comment on lines 151 to 155
@Transactional
fun deleteExperimentPost(input: DeleteExperimentPostUseCase.Input) {
evictExperimentPostCountsCaches()
deleteExperimentPostUseCase.execute(input)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 캐시 삭제를 먼저 하고 👉 DB에서 삭제하는 로직으로 받아들였습니다.
그런데 deleteExperimentPostUseCase.execute 시에 예외가 발생한다면 캐시는 삭제됐는데 DB에서는 삭제되지 않는 상황이 발생할 수도 있지 않나...! 싶습니다.
DB에서 삭제를 하고 👉 캐시 삭제 순으로 해야 DB 삭제 시에 예외가 발생하더라도, 캐시가 남아있기 때문에 어느정도 일관성이 유지될 수 있지 않나 생각이 듭니다🤔
지수님 생각은 어떠실지 궁금합니다.

Copy link
Member Author

@Ji-soo708 Ji-soo708 Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정님 말이 맞습니다. 먼저 DB에서 데이터를 삭제하고 캐시를 무효화시키는 흐름이 맞는데 이 부분을 놓쳤네요. 꼼꼼한 리뷰 감사합니다! 👍

Comment on lines 157 to 160
private fun evictExperimentPostCountsCaches() {
cacheGateway.evict("experimentPostCounts:ALL")
cacheGateway.evict("experimentPostCounts:OPEN")
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

캐시 키를 List으로 관리하는 게 어떨까 제안드려요!

private fun evictExperimentPostCountsCaches() {
    listOf("ALL", "OPEN").forEach { cacheGateway.evict("experimentPostCounts:$it") }
}

이렇게 되면 추후에 다른 지역을 기준으로 공고를 조회하는 키가 추가되더라도, 유지보수성이 좋아질 수 있을 거라 기대되는데 지수님 의견은 어떠신지요? 😊🤔

}

fun validateFilter(input: GetExperimentPostsUseCase.Input) {
private fun validateFilter(input: GetExperimentPostsUseCase.Input) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 제가 이 부분 놓친 것 같은데 바꾸어주셔서 감사합니다! 🙇‍♀️

Comment on lines 8 to 32
object RedisTestContainer : TestListener {
private val isCI = System.getenv("GITHUB_ACTIONS") == "true"

private val redis: GenericContainer<*> = GenericContainer(DockerImageName.parse("redis:7.0.8-alpine")).apply {
withExposedPorts(6379)

if (!isCI) {
portBindings = listOf("6379:6379")
}
}

override suspend fun beforeSpec(spec: Spec) {
redis.start()

val redisHost = "127.0.0.1"
val redisPort = redis.getMappedPort(6379).toString()

System.setProperty("spring.data.redis.host", redisHost)
System.setProperty("spring.data.redis.port", redisPort)
}

override suspend fun afterSpec(spec: Spec) {
redis.stop()
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 테스트 컨테이너가 테스트를 실행할 때마다 매번 레디스 컨테이너를 새로 띄우는 코드로 보여집니다.
앞선 커밋에서는 isRunning 체크로 개선하신 것 같은데, 이를 철회하고 해당 코드를 확정지으신 이유가 궁금합니다 🙋‍♀️

제 생각에는
✅ 컨테이너 재사용을 활성화
✅ beforeSpec에서 기존에 isRunning 체크방식으로 컨테이너 중복실행 방지
✅ afterSpec에서 CI 환경에서는 컨테이너를 자동으로 정리해주는 기능이 있으니, if(!isCI) 로 로컬 환경에서만 컨테이너 정리

식으로 개선할 수 있을 것 같은데, 지수님 의견이 듣고 싶어요 🤔🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

옹 좋은 의견입니다. 👍

일단 Redis 컨테이너를 매번 새로 띄우는 방식으로 변경한 이유는, 현재 단계에서는 Redis를 사용하는 서비스 테스트 코드가 하나라 한 번만 컨테이너가 실행되기 때문에 성능이나 리소스 측면에서 큰 부담이 없기 때문입니다. 또한, 트러블슈팅을 진행하면서 Redis 컨테이너를 매번 새로 띄우면 항상 깨끗한 상태에서 테스트가 실행되므로, 현재 코드가 더 안정적이라고 판단했습니다.

현재는 Redis를 사용하는 테스트가 하나뿐이고, 앞으로도 크게 늘어날 것 같지는 않아서 수정님 코드로 진행해도 문제가 없을 것 같습니다.
따라서, 우선 개선된 코드로 적용하고, 이후 문제가 발생하면 롤백하는 방식이 더 좋을 것 같아요~

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7885f28 and 3a722d3.

📒 Files selected for processing (2)
  • src/main/kotlin/com/dobby/backend/application/service/ExperimentPostService.kt (4 hunks)
  • src/test/kotlin/com/dobby/backend/config/RedisTestContainer.kt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/kotlin/com/dobby/backend/config/RedisTestContainer.kt
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (3)
src/main/kotlin/com/dobby/backend/application/service/ExperimentPostService.kt (3)

36-37: 의존성 주입이 적절히 구현되었습니다!

캐시 구현에 필요한 CacheGatewayObjectMapper가 private 필드로 잘 추가되었습니다.


95-100: 직렬화/역직렬화 예외 처리가 필요합니다.

ObjectMapper 작업 시 발생할 수 있는 예외에 대한 처리가 필요합니다.


152-155: ⚠️ Potential issue

캐시 무효화 순서를 변경해야 합니다.

DB 작업 실패 시 데이터 일관성을 유지하기 위해, 캐시 무효화는 DB 작업 성공 후에 수행되어야 합니다.

다음과 같이 순서를 변경해주세요:

 @Transactional
 fun deleteExperimentPost(input: DeleteExperimentPostUseCase.Input) {
-    deleteExperimentPostUseCase.execute(input)
-    evictExperimentPostCountsCaches()
+    try {
+        deleteExperimentPostUseCase.execute(input)
+        evictExperimentPostCountsCaches()
+    } catch (e: Exception) {
+        throw e
+    }
 }

Likely an incorrect or invalid review comment.

Comment on lines 77 to +93
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) {
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
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

ObjectMapper 예외 처리가 필요합니다.

getExperimentPostCounts 메서드에서 캐시 저장 시 ObjectMapper.writeValueAsString() 호출에 대한 예외 처리가 누락되었습니다.

다음과 같이 예외 처리를 추가해주세요:

 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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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) {
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
}
fun getExperimentPostCounts(input: Any): Any {
if (input is GetExperimentPostCountsByRegionUseCase.Input && input.region == null) {
getCachedExperimentPostCounts(input.recruitStatus)?.let { return it }
}
val output = when (input) {
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) {
try {
cacheExperimentPostCounts(input.recruitStatus, output)
} catch (e: Exception) {
log.error("Failed to cache experiment post counts: ${e.message}")
}
}
return output
}

@Ji-soo708 Ji-soo708 requested a review from chock-cho February 11, 2025 00:45
@Ji-soo708 Ji-soo708 removed the 🔒 HOLD 홀드 label Feb 11, 2025
Copy link
Member

@chock-cho chock-cho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 리뷰 사항 반영해주셔서 감사합니다.
수고 많으셨어요 👍👍💪

@Ji-soo708 Ji-soo708 merged commit fbc6cc5 into dev Feb 11, 2025
3 checks passed
@Ji-soo708 Ji-soo708 deleted the feat/YS-299 branch February 11, 2025 02:58
chock-cho pushed a commit that referenced this pull request Feb 16, 2025
…92)

* feat: add CacheGateway for RedisCaching

* feat: cache GetExperimentPostCountsByRegionUseCase output for faster response

* test: add Redis Testcontainer configuration

* test: add Redis caching test for ExperimentPostService

* chore: update ci yml for redis test container

* fix: fix RedisTestContainer

* fix: add test profiles to s3 test code

* fix: fix redis test container

* fix: fix redis test container

* fix: prevent Redis port conflict in CI environment

* feat: add caching to deleteExperimentPost

* refactor: extract evictExperimentPostCountsCaches logic

* refactor: apply a review comment to ExperimentPostService

* refactor: refactor RedisTestContainer
chock-cho pushed a commit that referenced this pull request Feb 16, 2025
…92)

* feat: add CacheGateway for RedisCaching

* feat: cache GetExperimentPostCountsByRegionUseCase output for faster response

* test: add Redis Testcontainer configuration

* test: add Redis caching test for ExperimentPostService

* chore: update ci yml for redis test container

* fix: fix RedisTestContainer

* fix: add test profiles to s3 test code

* fix: fix redis test container

* fix: fix redis test container

* fix: prevent Redis port conflict in CI environment

* feat: add caching to deleteExperimentPost

* refactor: extract evictExperimentPostCountsCaches logic

* refactor: apply a review comment to ExperimentPostService

* refactor: refactor RedisTestContainer
chock-cho pushed a commit that referenced this pull request Feb 16, 2025
…92)

* feat: add CacheGateway for RedisCaching

* feat: cache GetExperimentPostCountsByRegionUseCase output for faster response

* test: add Redis Testcontainer configuration

* test: add Redis caching test for ExperimentPostService

* chore: update ci yml for redis test container

* fix: fix RedisTestContainer

* fix: add test profiles to s3 test code

* fix: fix redis test container

* fix: fix redis test container

* fix: prevent Redis port conflict in CI environment

* feat: add caching to deleteExperimentPost

* refactor: extract evictExperimentPostCountsCaches logic

* refactor: apply a review comment to ExperimentPostService

* refactor: refactor RedisTestContainer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚙️ CHORE config, workflow.yaml ✨ FEATURE 기능 추가 ✅ TEST 테스트 코드 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants