Skip to content

Conversation

@Ji-soo708
Copy link
Member

@Ji-soo708 Ji-soo708 commented Jul 28, 2025

💡 작업 내용

✅ 셀프 체크리스트

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

🙋🏻‍ 확인해주세요

  • 관련된 Discussion 등이 있다면 첨부해주세요

🔗 Jira 티켓


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

Summary by CodeRabbit

  • 신규 기능

    • 실험 게시물 키워드 추출 시, 하루에 회원당 2회로 사용량이 제한됩니다. 제한 초과 시 안내 메시지가 제공됩니다.
    • 키워드 추출 결과가 여러 키워드를 포함하도록 응답 구조가 개선되었습니다.
  • 버그 수정

    • 키워드 추출 응답 및 관련 매핑에서 단수형에서 복수형으로 일관성 있게 변경되었습니다.
  • 리팩터링

    • 내부 키워드 추출 방식이 OpenAI 기반으로 전환되었습니다.
    • 키워드 추출 및 로그 기록 관련 구조가 개선되었습니다.
  • 테스트

    • 일일 사용량 제한 및 정상 추출 시나리오에 대한 테스트가 추가되었습니다.
  • 데이터베이스

    • 실험 게시물 키워드 추출 로그 테이블이 추가되어 사용 이력이 저장됩니다.

@Ji-soo708 Ji-soo708 self-assigned this Jul 28, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 28, 2025

Walkthrough

이 변경사항은 실험 게시글 키워드 추출 기능을 기존 게이트웨이에서 OpenAI 기반 게이트웨이로 대체하고, 멤버별 일일 사용 제한(2회)을 도입하며, 추출 결과를 로그로 저장하는 로직을 추가합니다. 이에 따라 도메인, 인프라, 프레젠테이션 계층에 걸쳐 데이터 모델, 예외, 매퍼, 저장소, 테스트 코드가 대폭 수정 및 확장되었습니다.

Changes

Cohort / File(s) Change Summary
UseCase 및 테스트 리팩토링
application/src/main/kotlin/com/dobby/usecase/experiment/ExtractExperimentPostKeywordsUseCase.kt, application/src/test/kotlin/com/dobby/usecase/experiment/ExtractExperimentPostKeywordsUseCaseTest.kt
키워드 추출 게이트웨이를 OpenAI 기반으로 변경, 멤버 ID 및 일일 사용 제한 로직 추가, 추출 결과 로그 저장 및 예외 처리 추가, 테스트 시나리오 확장 및 Mock 객체 대체
예외 및 도메인 모델 추가/변경
domain/src/main/kotlin/com/dobby/exception/DobbyException.kt, domain/src/main/kotlin/com/dobby/model/experiment/ExperimentPostKeywordsLog.kt, domain/src/main/kotlin/com/dobby/model/experiment/keyword/ExperimentPostKeywords.kt
일일 사용 제한 초과 예외 추가, 키워드 로그 도메인 및 팩토리 메서드 신설, 키워드 단수→복수형 이름 변경
게이트웨이 및 인터페이스 변경/추가/삭제
domain/src/main/kotlin/com/dobby/gateway/OpenAiGateway.kt, domain/src/main/kotlin/com/dobby/gateway/experiment/ExperimentKeywordExtractionGateway.kt, domain/src/main/kotlin/com/dobby/gateway/experiment/ExperimentPostKeywordsLogGateway.kt
OpenAiGateway 인터페이스 신설, 기존 ExperimentKeywordExtractionGateway 삭제, 키워드 로그 게이트웨이 인터페이스 신설
인프라: 컨버터, 매퍼, 외부/저장소 구현
infrastructure/src/main/kotlin/com/dobby/converter/JsonConverter.kt, infrastructure/src/main/kotlin/com/dobby/mapper/ExperimentPostKeywordsLogMapper.kt, infrastructure/src/main/kotlin/com/dobby/external/gateway/experiment/ExperimentPostKeywordsLogGatewayImpl.kt, infrastructure/src/main/kotlin/com/dobby/external/openai/OpenAiGatewayImpl.kt, infrastructure/src/main/kotlin/com/dobby/external/prompt/ExperimentPostKeywordMapper.kt, infrastructure/src/main/kotlin/com/dobby/persistence/entity/experiment/ExperimentPostKeywordsLogEntity.kt, infrastructure/src/main/kotlin/com/dobby/persistence/repository/ExperimentPostKeywordsLogRepository.kt
키워드 로그 엔티티/저장소/매퍼/게이트웨이 구현, OpenAiGatewayImpl로 클래스명 및 인터페이스 교체, JsonConverter 신설, 키워드 매퍼 및 반환타입 복수형으로 변경
DB 마이그레이션
infrastructure/src/main/resources/db/migration/V202507271926__add_experiment_post_keywords_log_table.sql
experiment_post_keywords_log 테이블 및 인덱스 생성
프레젠테이션 계층 DTO/매퍼
presentation/src/main/kotlin/com/dobby/api/dto/response/experiment/ExtractKeywordResponse.kt, presentation/src/main/kotlin/com/dobby/api/mapper/ExperimentPostMapper.kt
DTO 필드명 및 타입 복수형으로 변경, 매퍼에서 멤버 ID 전달 및 응답 필드명 수정

Sequence Diagram(s)

sequenceDiagram
    participant API
    participant Mapper
    participant UseCase
    participant MemberGateway
    participant LogGateway
    participant OpenAiGateway

    API->>Mapper: toExtractKeywordUseCaseInput(request)
    Mapper->>UseCase: Input(memberId, text)
    UseCase->>MemberGateway: findById(memberId)
    UseCase->>LogGateway: countByMemberIdAndCreatedAtBetween(memberId, todayStart, todayEnd)
    alt 사용 가능 횟수 초과
        UseCase-->>API: ExperimentPostKeywordsDailyLimitExceededException
    else 사용 가능
        UseCase->>OpenAiGateway: extractKeywords(text)
        UseCase->>LogGateway: save(Log)
        UseCase-->>Mapper: Output(experimentPostKeywords)
        Mapper-->>API: ExtractKeywordResponse(experimentPostKeywords)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Suggested labels

✅ TEST, ✨ FEATURE

Suggested reviewers

  • chock-cho

Poem

🐇
오늘은 키워드를 뽑는 날,
토끼 귀 쫑긋, 멤버별로 딱 두 번!
OpenAI가 도와주고,
로그도 착착 남겨두고,
예외도 챙겨주는 똑똑한 우리 집.
데이터는 풍성하게,
리뷰는 살금살금!

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/YS-522

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @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.

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

🧹 Nitpick comments (2)
infrastructure/src/main/kotlin/com/dobby/external/openai/OpenAiGatewayImpl.kt (1)

70-77: JSON 응답 정리 로직을 개선할 수 있습니다.

현재 구현도 잘 작동하지만, 정규식 체인을 더 효율적으로 만들 수 있습니다.

 private fun cleanJsonResponse(content: String): String {
-    return content.trim()
-        .replace(Regex("^```json\\s*"), "")
-        .replace(Regex("```\\s*$"), "")
-        .replace(Regex("^`+"), "")
-        .replace(Regex("`+$"), "")
-        .trim()
+    return content.trim()
+        .replace(Regex("^```(?:json)?\\s*|```\\s*$|^`+|`+$"), "")
+        .trim()
 }
application/src/main/kotlin/com/dobby/usecase/experiment/ExtractExperimentPostKeywordsUseCase.kt (1)

33-46: 키워드 추출 실행 로직 개선

비즈니스 로직이 올바른 순서로 구현되었습니다:

  1. 멤버 조회
  2. 일일 사용 제한 검증 (불필요한 API 호출 방지)
  3. 키워드 추출 수행
  4. 로그 생성 및 저장

다만, 로그 저장 실패 시 예외 처리를 고려해볼 필요가 있습니다.

로그 저장 실패에 대한 예외 처리를 추가하는 것을 고려해보세요:

try {
    experimentPostKeywordsGateway.save(log)
} catch (e: Exception) {
    // 로그 저장 실패 시에도 키워드 추출 결과는 반환
    // 별도 로깅이나 모니터링 필요
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between eb9e822 and de60b0d.

📒 Files selected for processing (18)
  • application/src/main/kotlin/com/dobby/usecase/experiment/ExtractExperimentPostKeywordsUseCase.kt (1 hunks)
  • application/src/test/kotlin/com/dobby/usecase/experiment/ExtractExperimentPostKeywordsUseCaseTest.kt (1 hunks)
  • domain/src/main/kotlin/com/dobby/exception/DobbyException.kt (1 hunks)
  • domain/src/main/kotlin/com/dobby/gateway/OpenAiGateway.kt (1 hunks)
  • domain/src/main/kotlin/com/dobby/gateway/experiment/ExperimentKeywordExtractionGateway.kt (0 hunks)
  • domain/src/main/kotlin/com/dobby/gateway/experiment/ExperimentPostKeywordsLogGateway.kt (1 hunks)
  • domain/src/main/kotlin/com/dobby/model/experiment/ExperimentPostKeywordsLog.kt (1 hunks)
  • domain/src/main/kotlin/com/dobby/model/experiment/keyword/ExperimentPostKeywords.kt (1 hunks)
  • infrastructure/src/main/kotlin/com/dobby/converter/JsonConverter.kt (1 hunks)
  • infrastructure/src/main/kotlin/com/dobby/external/gateway/experiment/ExperimentPostKeywordsLogGatewayImpl.kt (1 hunks)
  • infrastructure/src/main/kotlin/com/dobby/external/openai/OpenAiGatewayImpl.kt (2 hunks)
  • infrastructure/src/main/kotlin/com/dobby/external/prompt/ExperimentPostKeywordMapper.kt (2 hunks)
  • infrastructure/src/main/kotlin/com/dobby/mapper/ExperimentPostKeywordsLogMapper.kt (1 hunks)
  • infrastructure/src/main/kotlin/com/dobby/persistence/entity/experiment/ExperimentPostKeywordsLogEntity.kt (1 hunks)
  • infrastructure/src/main/kotlin/com/dobby/persistence/repository/ExperimentPostKeywordsLogRepository.kt (1 hunks)
  • infrastructure/src/main/resources/db/migration/V202507271926__add_experiment_post_keywords_log_table.sql (1 hunks)
  • presentation/src/main/kotlin/com/dobby/api/dto/response/experiment/ExtractKeywordResponse.kt (1 hunks)
  • presentation/src/main/kotlin/com/dobby/api/mapper/ExperimentPostMapper.kt (1 hunks)
💤 Files with no reviewable changes (1)
  • domain/src/main/kotlin/com/dobby/gateway/experiment/ExperimentKeywordExtractionGateway.kt
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: Ji-soo708
PR: YAPP-Github/Gradmeet-BE#150
File: infrastructure/src/main/kotlin/com/dobby/external/prompt/ExperimentPostKeywordMapper.kt:52-58
Timestamp: 2025-07-17T08:06:16.021Z
Learning: AI가 텍스트에서 키워드를 추출하는 과정에서 enum 변환 실패(IllegalArgumentException)는 정상적인 상황에 가깝다. 따라서 ExperimentPostKeywordMapper에서 TimeSlot, MatchType 등의 enum 변환 실패 시 로깅을 추가하지 않는 것이 적절하다.
Learnt from: Ji-soo708
PR: YAPP-Github/Gradmeet-BE#140
File: domain/src/main/kotlin/com/dobby/gateway/auth/GoogleAuthGateway.kt:7-7
Timestamp: 2025-06-04T09:02:40.657Z
Learning: Ji-soo708의 팀은 로컬 ktlint 기준과 GitHub ktlint 체크 기준이 다를 때, 기존 코드베이스와의 일관성을 위해 로컬 기준을 우선시한다.
Learnt from: Ji-soo708
PR: YAPP-Github/Gradmeet-BE#154
File: infrastructure/src/main/kotlin/com/dobby/mapper/ExperimentPostKeywordsLogMapper.kt:15-22
Timestamp: 2025-07-27T12:37:15.372Z
Learning: Ji-soo708의 팀에서는 JSON 역직렬화 시 예외를 catch하여 래핑하기보다는, 예외를 자연스럽게 상위로 전파시켜 원본 컨텍스트를 보존하고 후속 조치를 용이하게 하는 것을 선호한다.
domain/src/main/kotlin/com/dobby/model/experiment/keyword/ExperimentPostKeywords.kt (1)

Learnt from: Ji-soo708
PR: #150
File: infrastructure/src/main/kotlin/com/dobby/external/prompt/ExperimentPostKeywordMapper.kt:52-58
Timestamp: 2025-07-17T08:06:16.021Z
Learning: AI가 텍스트에서 키워드를 추출하는 과정에서 enum 변환 실패(IllegalArgumentException)는 정상적인 상황에 가깝다. 따라서 ExperimentPostKeywordMapper에서 TimeSlot, MatchType 등의 enum 변환 실패 시 로깅을 추가하지 않는 것이 적절하다.

application/src/main/kotlin/com/dobby/usecase/experiment/ExtractExperimentPostKeywordsUseCase.kt (1)

Learnt from: Ji-soo708
PR: #150
File: infrastructure/src/main/kotlin/com/dobby/external/prompt/ExperimentPostKeywordMapper.kt:52-58
Timestamp: 2025-07-17T08:06:16.021Z
Learning: AI가 텍스트에서 키워드를 추출하는 과정에서 enum 변환 실패(IllegalArgumentException)는 정상적인 상황에 가깝다. 따라서 ExperimentPostKeywordMapper에서 TimeSlot, MatchType 등의 enum 변환 실패 시 로깅을 추가하지 않는 것이 적절하다.

domain/src/main/kotlin/com/dobby/exception/DobbyException.kt (1)

Learnt from: Ji-soo708
PR: #150
File: infrastructure/src/main/kotlin/com/dobby/external/prompt/ExperimentPostKeywordMapper.kt:52-58
Timestamp: 2025-07-17T08:06:16.021Z
Learning: AI가 텍스트에서 키워드를 추출하는 과정에서 enum 변환 실패(IllegalArgumentException)는 정상적인 상황에 가깝다. 따라서 ExperimentPostKeywordMapper에서 TimeSlot, MatchType 등의 enum 변환 실패 시 로깅을 추가하지 않는 것이 적절하다.

application/src/test/kotlin/com/dobby/usecase/experiment/ExtractExperimentPostKeywordsUseCaseTest.kt (1)

Learnt from: Ji-soo708
PR: #150
File: infrastructure/src/main/kotlin/com/dobby/external/prompt/ExperimentPostKeywordMapper.kt:52-58
Timestamp: 2025-07-17T08:06:16.021Z
Learning: AI가 텍스트에서 키워드를 추출하는 과정에서 enum 변환 실패(IllegalArgumentException)는 정상적인 상황에 가깝다. 따라서 ExperimentPostKeywordMapper에서 TimeSlot, MatchType 등의 enum 변환 실패 시 로깅을 추가하지 않는 것이 적절하다.

infrastructure/src/main/kotlin/com/dobby/external/openai/OpenAiGatewayImpl.kt (1)

Learnt from: Ji-soo708
PR: #150
File: infrastructure/src/main/kotlin/com/dobby/external/feign/openAi/OpenAiFeignClient.kt:10-14
Timestamp: 2025-07-17T08:06:13.596Z
Learning: Ji-soo708의 팀은 현재 OpenAI 공식 API만 사용할 예정이므로, FeignClient URL을 외부화하지 않고 하드코딩하는 것을 선호한다. 팀의 요구사항에 따라 단순함을 우선시하는 접근 방식을 취한다.

domain/src/main/kotlin/com/dobby/model/experiment/ExperimentPostKeywordsLog.kt (1)

Learnt from: Ji-soo708
PR: #150
File: infrastructure/src/main/kotlin/com/dobby/external/prompt/ExperimentPostKeywordMapper.kt:52-58
Timestamp: 2025-07-17T08:06:16.021Z
Learning: AI가 텍스트에서 키워드를 추출하는 과정에서 enum 변환 실패(IllegalArgumentException)는 정상적인 상황에 가깝다. 따라서 ExperimentPostKeywordMapper에서 TimeSlot, MatchType 등의 enum 변환 실패 시 로깅을 추가하지 않는 것이 적절하다.

infrastructure/src/main/kotlin/com/dobby/external/prompt/ExperimentPostKeywordMapper.kt (1)

Learnt from: Ji-soo708
PR: #150
File: infrastructure/src/main/kotlin/com/dobby/external/prompt/ExperimentPostKeywordMapper.kt:52-58
Timestamp: 2025-07-17T08:06:16.021Z
Learning: AI가 텍스트에서 키워드를 추출하는 과정에서 enum 변환 실패(IllegalArgumentException)는 정상적인 상황에 가깝다. 따라서 ExperimentPostKeywordMapper에서 TimeSlot, MatchType 등의 enum 변환 실패 시 로깅을 추가하지 않는 것이 적절하다.

presentation/src/main/kotlin/com/dobby/api/mapper/ExperimentPostMapper.kt (1)

Learnt from: Ji-soo708
PR: #150
File: infrastructure/src/main/kotlin/com/dobby/external/prompt/ExperimentPostKeywordMapper.kt:52-58
Timestamp: 2025-07-17T08:06:16.021Z
Learning: AI가 텍스트에서 키워드를 추출하는 과정에서 enum 변환 실패(IllegalArgumentException)는 정상적인 상황에 가깝다. 따라서 ExperimentPostKeywordMapper에서 TimeSlot, MatchType 등의 enum 변환 실패 시 로깅을 추가하지 않는 것이 적절하다.

infrastructure/src/main/kotlin/com/dobby/mapper/ExperimentPostKeywordsLogMapper.kt (1)

Learnt from: Ji-soo708
PR: #150
File: infrastructure/src/main/kotlin/com/dobby/external/prompt/ExperimentPostKeywordMapper.kt:52-58
Timestamp: 2025-07-17T08:06:16.021Z
Learning: AI가 텍스트에서 키워드를 추출하는 과정에서 enum 변환 실패(IllegalArgumentException)는 정상적인 상황에 가깝다. 따라서 ExperimentPostKeywordMapper에서 TimeSlot, MatchType 등의 enum 변환 실패 시 로깅을 추가하지 않는 것이 적절하다.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (34)
infrastructure/src/main/kotlin/com/dobby/converter/JsonConverter.kt (2)

6-13: JSON 변환 유틸리티 클래스가 잘 구현되었습니다.

Jackson ObjectMapper를 사용한 간단하고 명확한 구현입니다. Spring Component로 등록되어 의존성 주입이 가능하고, 제네릭 메서드로 타입 안전성을 제공합니다.


12-12: 예외 처리 고려사항.

Jackson의 readValue 메서드는 JsonProcessingException을 던질 수 있습니다. 현재는 예외를 그대로 전파하고 있는데, 팀의 기존 learnings에 따르면 예외를 자연스럽게 상위로 전파시키는 것을 선호하므로 현재 구현이 적절합니다.

domain/src/main/kotlin/com/dobby/model/experiment/keyword/ExperimentPostKeywords.kt (1)

6-6: 클래스명 변경이 적절합니다.

ExperimentPostKeyword에서 ExperimentPostKeywords로의 복수형 변경이 코드베이스 전반의 일관성을 위해 적절하게 수행되었습니다. 내부 구조는 그대로 유지되어 안정성을 보장합니다.

domain/src/main/kotlin/com/dobby/gateway/OpenAiGateway.kt (1)

5-7: 깔끔한 게이트웨이 인터페이스 설계입니다.

단일 책임 원칙을 따르는 명확한 인터페이스입니다. extractKeywords 메서드는 직관적인 시그니처를 가지고 있으며, 도메인 계층에서 OpenAI 구현 세부사항을 추상화하여 좋은 아키텍처를 보여줍니다.

domain/src/main/kotlin/com/dobby/exception/DobbyException.kt (1)

71-71: 일일 사용 제한 예외가 적절히 구현되었습니다.

새로운 예외 ExperimentPostKeywordsDailyLimitExceededException이 기존 코드베이스의 패턴을 따라 적절한 에러 코드(EP0014)와 명확한 메시지로 구현되었습니다. OpenAI API 비용 관리를 위한 일일 사용 제한 기능에 필요한 예외입니다.

presentation/src/main/kotlin/com/dobby/api/dto/response/experiment/ExtractKeywordResponse.kt (1)

3-3: DTO 응답 모델이 도메인 변경사항과 일관성 있게 업데이트되었습니다.

import 구문과 프로퍼티명이 ExperimentPostKeyword에서 ExperimentPostKeywords로 일관되게 변경되었습니다. API 응답 구조가 도메인 모델 변경사항을 적절히 반영하고 있습니다.

Also applies to: 8-8

infrastructure/src/main/kotlin/com/dobby/persistence/repository/ExperimentPostKeywordsLogRepository.kt (1)

7-9: LGTM! 깔끔하고 표준적인 구현입니다.

Spring Data JPA 컨벤션을 잘 따르고 있으며, 일일 사용량 제한 확인을 위한 카운트 메서드가 적절하게 정의되어 있습니다.

infrastructure/src/main/resources/db/migration/V202507271926__add_experiment_post_keywords_log_table.sql (1)

1-11: 데이터베이스 스키마 설계가 우수합니다.

테이블 구조가 로깅 요구사항에 적합하고, (member_id, created_at) 복합 인덱스가 일일 사용량 카운트 쿼리 성능을 최적화합니다. 외래키 제약조건도 적절히 설정되어 있습니다.

domain/src/main/kotlin/com/dobby/gateway/experiment/ExperimentPostKeywordsLogGateway.kt (1)

6-9: 클린 아키텍처 원칙을 잘 따른 게이트웨이 인터페이스입니다.

도메인 레벨의 추상화가 적절하고, 메서드 시그니처가 로깅 요구사항에 잘 맞습니다.

infrastructure/src/main/kotlin/com/dobby/external/openai/OpenAiGatewayImpl.kt (2)

1-2: 리팩토링이 잘 수행되었습니다.

패키지 구조 변경과 인터페이스 업데이트가 새로운 아키텍처에 잘 맞습니다. 클래스명과 반환 타입 변경도 적절합니다.

Also applies to: 10-12, 18-22, 30-30


43-60: 견고한 에러 핸들링과 응답 처리 로직입니다.

FeignException과 일반 Exception을 적절히 처리하고 있으며, JSON 응답 정리 로직이 OpenAI 응답의 다양한 포맷을 잘 처리합니다.

infrastructure/src/main/kotlin/com/dobby/persistence/entity/experiment/ExperimentPostKeywordsLogEntity.kt (1)

12-27: JPA 엔티티가 모범 사례를 잘 따르고 있습니다.

데이터베이스 스키마와 완벽히 일치하며, LAZY 페치와 불변 필드 사용이 적절합니다. 컬럼 정의도 정확합니다.

infrastructure/src/main/kotlin/com/dobby/external/prompt/ExperimentPostKeywordMapper.kt (1)

10-10: 도메인 모델 이름 변경에 따른 일관성 있는 리팩토링

ExperimentPostKeyword에서 ExperimentPostKeywords로의 도메인 모델 이름 변경이 일관성 있게 적용되었습니다. 매핑 로직은 그대로 유지되어 기능상 문제가 없습니다.

Also applies to: 17-18, 62-62

domain/src/main/kotlin/com/dobby/model/experiment/ExperimentPostKeywordsLog.kt (1)

8-26: 잘 설계된 도메인 모델

로그 엔티티가 적절히 설계되었습니다:

  • 불변 데이터 클래스로 도메인 무결성 보장
  • 팩토리 메서드 패턴으로 객체 생성 캡슐화
  • TimeProvider 사용으로 테스트 가능성 향상
presentation/src/main/kotlin/com/dobby/api/mapper/ExperimentPostMapper.kt (2)

524-528: 일일 사용 제한을 위한 멤버 ID 추가

키워드 추출 요청에 getCurrentMemberId()를 통해 현재 멤버 ID를 포함하도록 수정되어, 일일 사용 제한 기능을 지원할 수 있게 되었습니다.


531-535: 도메인 모델 변경에 따른 응답 매핑 수정

experimentPostKeyword에서 experimentPostKeywords로 속성명이 변경되어 복수형 도메인 모델과 일치하게 되었습니다.

application/src/main/kotlin/com/dobby/usecase/experiment/ExtractExperimentPostKeywordsUseCase.kt (2)

13-22: 의존성 주입과 상수 정의

새로운 의존성들이 적절히 주입되었고, 일일 사용 제한을 상수로 정의하여 유지보수성을 높였습니다.


48-62: 일일 사용 제한 검증 로직

일일 사용 제한 검증이 정확히 구현되었습니다:

  • TimeProvider 사용으로 테스트 가능성 확보
  • 하루 시작/끝 시간 계산이 정확함
  • 제한 초과 시 적절한 예외 발생
infrastructure/src/main/kotlin/com/dobby/mapper/ExperimentPostKeywordsLogMapper.kt (1)

11-32: JSON 직렬화를 포함한 매퍼 구현

로그 엔티티와 도메인 모델 간의 변환이 올바르게 구현되었습니다:

  • JsonConverter를 통한 응답 객체의 직렬화/역직렬화
  • 양방향 매핑 메서드 제공
  • JSON 역직렬화 예외를 자연스럽게 상위로 전파하여 팀의 선호 방식 준수
application/src/test/kotlin/com/dobby/usecase/experiment/ExtractExperimentPostKeywordsUseCaseTest.kt (9)

3-19: import 구문이 적절하게 구성되었습니다.

리팩터링된 UseCase의 새로운 의존성들(OpenAiGateway, ExperimentPostKeywordsLogGateway, MemberGateway 등)과 테스트에 필요한 모든 클래스들이 올바르게 import되어 있습니다.


22-40: 테스트 설정이 잘 구성되었습니다.

모든 의존성에 대한 mock 객체 생성, TimeProvider 객체 모킹을 통한 시간 제어, 그리고 적절한 cleanup 로직이 포함되어 테스트 격리성을 보장합니다.


42-72: 일일 한도 미초과 시나리오가 적절하게 테스트되었습니다.

  • 현재 사용 횟수(1회)가 한도(2회) 미만인 상황을 정확히 시뮬레이션
  • 모든 의존성에 대한 적절한 mock 설정
  • Input과 Output 구조가 리팩터링된 UseCase와 일치
  • 시간 제어를 통한 일일 범위 계산 로직 검증

74-99: 일일 한도 초과 시나리오가 올바르게 테스트되었습니다.

  • 사용 횟수가 한도(2회)에 도달한 상황을 정확히 시뮬레이션
  • 적절한 예외 타입(ExperimentPostKeywordsDailyLimitExceededException) 검증
  • 한도 초과 시 호출되지 않아야 할 서비스들에 대한 불필요한 mocking 생략

3-11: 의존성 임포트 구성이 적절합니다.

새로운 기능에 필요한 모든 의존성들이 올바르게 임포트되어 있습니다. OpenAiGateway로의 전환과 로그 기능 추가에 따른 변경사항이 정확히 반영되었습니다.


17-18: mockkObject 사용 시 정리 로직 확인

TimeProvider를 mockkObject로 모킹하고 있는데, afterSpec에서 unmockkAll()을 호출하여 정리하고 있어 좋습니다.


22-32: 모든 의존성이 올바르게 모킹되었습니다.

UseCase에 필요한 4개의 의존성(openAiGateway, experimentPostKeywordsLogGateway, memberGateway, idGenerator)이 모두 적절히 모킹되어 생성자에 전달되었습니다.


42-71: 성공 시나리오 테스트가 포괄적입니다.

일일 사용 한도를 초과하지 않은 경우의 테스트가 잘 구성되어 있습니다. 특히:

  • TimeProvider 모킹으로 일관된 시간 제어
  • 로그 카운트 조건 (1회 사용으로 한도 미초과)
  • 모든 의존성의 상호작용 검증
  • 최종 결과 검증

테스트 커버리지가 우수합니다.


74-99: 일일 한도 초과 시나리오 테스트가 정확합니다.

한도 초과 시 예외 발생을 검증하는 테스트가 적절히 구성되어 있습니다:

  • 로그 카운트를 2로 설정하여 한도 도달 상황 시뮬레이션
  • ExperimentPostKeywordsDailyLimitExceededException 예외 타입 검증
  • shouldThrow 사용으로 예외 발생 확인

비즈니스 로직의 핵심 요구사항이 정확히 테스트되고 있습니다.

infrastructure/src/main/kotlin/com/dobby/external/gateway/experiment/ExperimentPostKeywordsLogGatewayImpl.kt (6)

1-14: 클래스 구조와 의존성 설정이 적절합니다.

  • 패키지 구조가 기존 패턴을 따름
  • Spring의 @component를 통한 적절한 빈 등록
  • 생성자 주입을 통한 깔끔한 의존성 관리
  • 필요한 모든 import 구문 포함

16-20: save 메서드 구현이 올바릅니다.

도메인 객체 → 엔티티 → 저장 → 도메인 객체 변환의 표준 패턴을 따르며, mapper를 적절히 활용하여 계층 간 변환을 처리합니다.


22-28: countByMemberIdAndCreatedAtBetween 메서드 구현이 적절합니다.

  • 메서드 시그니처가 인터페이스 계약과 정확히 일치
  • 일일 사용량 제한 확인을 위한 날짜 범위 쿼리에 적합한 매개변수 구조
  • Repository로의 단순한 위임이 이 간단한 조회 작업에 적합

10-14: 게이트웨이 구현이 표준 패턴을 따릅니다.

Spring Component로 등록되고 repository와 mapper를 의존성 주입받는 표준적인 구조입니다. 클린 아키텍처의 게이트웨이 패턴이 올바르게 적용되었습니다.


16-20: 저장 메서드 구현이 적절합니다.

도메인 → 엔티티 → 저장 → 도메인 변환의 표준적인 플로우를 따르고 있습니다. 매퍼를 통한 양방향 변환이 정확히 구현되어 있습니다.


22-28: 카운트 쿼리 메서드가 효율적입니다.

일일 사용량 확인을 위한 카운트 쿼리가 직관적으로 구현되어 있습니다. LocalDateTime 범위 조건을 사용하여 정확한 일일 범위 계산이 가능합니다. Repository 레벨에서 카운트만 반환하므로 메모리 효율적입니다.

@Ji-soo708 Ji-soo708 merged commit 376ab86 into dev Jul 28, 2025
4 checks passed
@Ji-soo708 Ji-soo708 deleted the feat/YS-522 branch July 28, 2025 10:02
@Ji-soo708 Ji-soo708 added 🐛 BUG 버그 ✨ FEATURE 기능 추가 🔥 HOTFIX 핫픽스 labels Jul 28, 2025
chock-cho pushed a commit that referenced this pull request Oct 21, 2025
* chore: add `experiment_post_keywords_log` table SQL file

* feat: add ExperimentPostKeywordsLog Entity and domain model

* feat: add ExperimentPostKeywordsLog Repository interface

* feat: serialize `ExperimentPostKeyword` to JSON for database storage

* fix: add memberId field to mapper

* feat: add daily usage limit validation for experiment post keywords

* chore: add idx_experiment_keywords_log for covering index

* test: add comprehensive tests for daily usage limit validation

* style: apply ktlint formatting

* style: rename class file for consistency

* refactor: clarify responsibilities by restructuring OpenAI Gateway and renaming classes
chock-cho pushed a commit that referenced this pull request Oct 21, 2025
* chore: add `experiment_post_keywords_log` table SQL file

* feat: add ExperimentPostKeywordsLog Entity and domain model

* feat: add ExperimentPostKeywordsLog Repository interface

* feat: serialize `ExperimentPostKeyword` to JSON for database storage

* fix: add memberId field to mapper

* feat: add daily usage limit validation for experiment post keywords

* chore: add idx_experiment_keywords_log for covering index

* test: add comprehensive tests for daily usage limit validation

* style: apply ktlint formatting

* style: rename class file for consistency

* refactor: clarify responsibilities by restructuring OpenAI Gateway and renaming classes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 BUG 버그 ✨ FEATURE 기능 추가 🔥 HOTFIX 핫픽스

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants