-
Notifications
You must be signed in to change notification settings - Fork 0
[YS-570] feature: AI 공고 기능 RateLimiter 카운팅 API 작업 #170
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
Conversation
개요일일 AI 키워드 추출 사용량 제한을 조회하고 관리하는 새로운 기능이 추가되었습니다. GetDailyLimitForExtractUseCase, UsageLimitGateway 메서드, Redis 구현체, REST API 엔드포인트, 데이터 전송 객체가 계층 전체에 추가되었습니다. 변경사항
순서도sequenceDiagram
participant Client
participant Controller as ExperimentPostController
participant Service as ExperimentPostService
participant UseCase as GetDailyLimitForExtractUseCase
participant Gateway as UsageLimitGateway (Redis)
participant Response
Client->>Controller: GET /v1/experiment-posts/usage-limit
activate Controller
Controller->>Service: getMyDailyUsageLimit(input)
activate Service
Service->>UseCase: execute(memberId)
activate UseCase
UseCase->>Gateway: getCurrentUsage(memberId, dailyLimit=2)
activate Gateway
Gateway->>Gateway: 캐시에서 count 조회
Gateway->>Gateway: 남은 count 계산
Gateway->>Gateway: TTL 또는 다음 자정(KST) 계산
Gateway-->>UseCase: UsageSnapshot
deactivate Gateway
UseCase-->>Service: Output
deactivate UseCase
Service-->>Controller: Output
deactivate Service
Controller->>Controller: DailyUsageSnapshotResponse로 변환
Controller-->>Client: 200 OK + JSON 응답
deactivate Controller
코드 리뷰 예상 난이도🎯 3 (중간) | ⏱️ ~20분 검토 시 주의사항:
시
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
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.
Actionable comments posted: 1
🧹 Nitpick comments (5)
infrastructure/src/main/kotlin/com/dobby/external/gateway/cache/RedisUsageLimitGatewayImpl.kt (1)
23-23: 타임존 설정을 설정 파일로 외부화하는 것을 고려하세요.
Asia/Seoul타임존이 하드코딩되어 있습니다. 향후 다른 지역 서비스를 고려하거나 테스트 용이성을 위해application.yml에서 설정하는 것이 좋습니다.예시:
@Value("\${app.timezone:Asia/Seoul}") private val timezoneId: String = "Asia/Seoul" private val zone by lazy { ZoneId.of(timezoneId) }application/src/main/kotlin/com/dobby/service/ExperimentPostService.kt (1)
159-162: 읽기 전용 Redis 작업에 @transactional이 불필요할 수 있습니다.
getMyDailyUsageLimit메서드는 Redis에서 데이터를 읽기만 하는 작업이므로@Transactional어노테이션이 필요하지 않습니다. JPA 트랜잭션은 데이터베이스 작업에만 관련되며, Redis 작업에는 적용되지 않습니다.불필요한 트랜잭션 오버헤드를 제거하려면
@Transactional을 제거하세요:- @Transactional fun getMyDailyUsageLimit(input: GetDailyLimitForExtractUseCase.Input): GetDailyLimitForExtractUseCase.Output { return getDailyLimitForExtractUseCase.execute(input) }application/src/main/kotlin/com/dobby/usecase/experiment/GetDailyLimitForExtractUseCase.kt (3)
11-13: 일일 사용량 제한값을 설정으로 외부화하는 것을 고려하세요.
DAILY_USAGE_LIMIT이 2로 하드코딩되어 있습니다. 비즈니스 요구사항이 변경되거나 환경별로 다른 제한값이 필요한 경우 코드 수정 및 재배포가 필요합니다. 애플리케이션 설정(application.yml 등)으로 외부화하면 유연성이 향상됩니다.예를 들어:
class GetDailyLimitForExtractUseCase( private val usageLimitGateway: UsageLimitGateway, private val dailyUsageLimit: Int // 설정에서 주입 ) : UseCase<GetDailyLimitForExtractUseCase.Input, GetDailyLimitForExtractUseCase.Output> { override fun execute(input: Input): Output { val snapshot = usageLimitGateway.getCurrentUsage( memberId = input.memberId, dailyLimit = dailyUsageLimit ) // ... } }
26-38: Redis 연산 실패에 대한 방어 코드 추가 권장
RedisUsageLimitGatewayImpl.getCurrentUsage()의 Redis 연산(redisTemplate.opsForValue().get(),redisTemplate.getExpire())이 예외를 발생시킬 때 처리 전략이 없습니다. 같은 이유로 설계된RedisCacheGatewayImpl은 try-catch로 방어하고 있으므로(예:getAutoComplete,setAutoComplete), 일관성을 위해 다음과 같이 개선하세요:
getCurrentUsage()에 try-catch 블록 추가하여 Redis 연산 실패 시 적절히 로깅- 또는 Redis 실패를 명시적인 도메인 예외로 변환하여 호출자가 의도적으로 처리하도록 함
RedisCacheGatewayImpl과 동일한 에러 처리 패턴 적용
15-17: memberId는 Spring Security 인증을 통해 검증됩니다.검증 결과,
memberId는getCurrentMemberId()를 통해 Spring Security의 인증 컨텍스트에서 가져오므로, 이미 인증된 사용자만 접근 가능합니다. 따라서 빈 값이나 null 값의 위험은 없습니다.다만 방어적 프로그래밍 관점에서, 사용 사례(use case) 경계에 명시적 검증을 추가하는 것은 고려할 수 있습니다:
data class Input( val memberId: String ) { init { require(memberId.isNotBlank()) { "memberId cannot be blank" } } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
application/src/main/kotlin/com/dobby/service/ExperimentPostService.kt(3 hunks)application/src/main/kotlin/com/dobby/usecase/experiment/ExtractExperimentPostKeywordsUseCase.kt(1 hunks)application/src/main/kotlin/com/dobby/usecase/experiment/GetDailyLimitForExtractUseCase.kt(1 hunks)domain/src/main/kotlin/com/dobby/gateway/UsageLimitGateway.kt(1 hunks)domain/src/main/kotlin/com/dobby/model/UsageSnapshot.kt(1 hunks)infrastructure/src/main/kotlin/com/dobby/external/gateway/cache/RedisUsageLimitGatewayImpl.kt(3 hunks)presentation/src/main/kotlin/com/dobby/api/controller/ExperimentPostController.kt(2 hunks)presentation/src/main/kotlin/com/dobby/api/dto/response/experiment/DailyUsageSnapshotResponse.kt(1 hunks)presentation/src/main/kotlin/com/dobby/api/mapper/ExperimentPostMapper.kt(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
presentation/src/main/kotlin/com/dobby/api/mapper/ExperimentPostMapper.kt (1)
presentation/src/main/kotlin/com/dobby/util/AuthenticationUtils.kt (1)
getCurrentMemberId(5-5)
⏰ 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 (6)
domain/src/main/kotlin/com/dobby/gateway/UsageLimitGateway.kt (1)
3-7: LGTM! 인터페이스 확장이 적절합니다.새로운
getCurrentUsage메서드가 기존incrementAndCheckLimit과 일관된 시그니처를 가지고 있으며, 도메인 모델UsageSnapshot을 반환하여 사용량 조회 기능을 명확하게 제공합니다.application/src/main/kotlin/com/dobby/usecase/experiment/ExtractExperimentPostKeywordsUseCase.kt (1)
36-36: 일일 사용량 제한 검증이 적절하게 배치되었습니다.OpenAI API 호출 전에 사용량 제한을 검증하여 불필요한 외부 API 호출을 방지하고 있습니다. Fail-fast 패턴을 잘 적용했습니다.
domain/src/main/kotlin/com/dobby/model/UsageSnapshot.kt (1)
5-10: LGTM! 도메인 모델이 명확하게 정의되었습니다.불변 데이터 클래스로 사용량 스냅샷을 표현하고 있으며, 프로퍼티 이름이 직관적이고 타입이 적절합니다.
presentation/src/main/kotlin/com/dobby/api/controller/ExperimentPostController.kt (1)
238-248: LGTM! API 엔드포인트가 일관성 있게 구현되었습니다.RESEARCHER 역할 기반 보안이 적용되었고, Swagger 문서화가 포함되어 있으며, 기존 컨트롤러 패턴과 일관된 구조로 작성되었습니다.
presentation/src/main/kotlin/com/dobby/api/mapper/ExperimentPostMapper.kt (1)
546-559: LGTM! 매퍼 함수가 명확하게 구현되었습니다.인증된 사용자 ID를 가져와 use case input을 생성하고, output을 response DTO로 직접 매핑하는 단순하고 명확한 구조입니다.
infrastructure/src/main/kotlin/com/dobby/external/gateway/cache/RedisUsageLimitGatewayImpl.kt (1)
39-58: <!-- [scratchpad_update]
[task verify_timezone_consistency]
[findings]
- Best practice confirmed: when timezone matters, use ZonedDateTime or explicit zone-aware API
- LocalDateTime.now() uses system default timezone - problematic for cross-timezone systems
- Mixing LocalDateTime.now() and ZonedDateTime.now(zone) is anti-pattern
[pending]- Need to examine actual RedisUsageLimitGatewayImpl.kt file to see the complete context
- Need to check if
LocalDate.now()in line 30 also needs fixing
[actions]- Fetch the full file content
[scratchpad_end] -->Now let me get the full file to see complete context:Based on my analysis of the review comment and the verification results, I can now formulate my response. The review comment is correct in identifying a timezone inconsistency issue. Let me generate the rewritten review comment.Line 30의 타임존 불일치를 수정하여 일관성 확보
incrementAndCheckLimit메서드(Line 30)에서LocalDateTime.now()를 사용하는 것과getCurrentUsage메서드(Lines 45-49)에서ZonedDateTime.now(zone)을 사용하는 것 간의 타임존 처리 불일치가 확인되었습니다. LocalDateTime은 타임존 정보를 가지지 않으므로 시스템 기본 타임존을 사용하게 되는데, 이는zone변수로 명시된 KST와 다를 수 있어 일일 한도 재설정 시간 계산에 불일치를 초래합니다.Line 30을 다음과 같이 수정하세요:
-val expireSeconds = Duration.between(LocalDateTime.now(), LocalDate.now().plusDays(1).atStartOfDay()).seconds +val expireSeconds = Duration.between(ZonedDateTime.now(zone).toLocalDateTime(), LocalDate.now().plusDays(1).atStartOfDay()).secondsLikely an incorrect or invalid review comment.
...entation/src/main/kotlin/com/dobby/api/dto/response/experiment/DailyUsageSnapshotResponse.kt
Show resolved
Hide resolved
* feature: add domain layer to view limit logics for daily snapshot * feature: add application layer usecase to execute logic * feature: add infra layer to implement gateway logic * feature: add presentation layer logic to controller
* feature: add domain layer to view limit logics for daily snapshot * feature: add application layer usecase to execute logic * feature: add infra layer to implement gateway logic * feature: add presentation layer logic to controller
💡 작업 내용
✅ 셀프 체크리스트
🙋🏻 확인해주세요
🔗 Jira 티켓
https://yappsocks.atlassian.net/browse/YS-570
Summary by CodeRabbit
새로운 기능