Conversation
- 이미지 업로드 Rate Limiting 구현을 위한 에러 코드 추가 - HTTP 429 상태 코드와 함께 '요청 횟수가 너무 많습니다' 메시지 정의
- @ratelimit 어노테이션 생성 (maxRequests, timeWindowSeconds, keyExpression 속성) - RateLimitExceededException 팩토리 클래스 생성 - CustomException.of()를 활용한 예외 생성 패턴 적용
- UploadController.uploadImage()에 @ratelimit 어노테이션 추가 - 사용자별 10분당 50회 업로드 제한 설정 - #userId SpEL 표현식으로 사용자별 카운터 분리
- 제한 내 요청 시 정상 통과 테스트 - SET NX 패턴을 활용한 TTL 설정 검증 - Rate Limit 초과 시 예외 발생 테스트 - SpEL keyExpression 평가 테스트
- StringUtils.hasText() 적용하여 null-safe 체크 개선 - generateKey() 메서드에서 methodKey 중복 생성 제거 - 미사용 RateLimitExceededException.create() 메서드 제거 - paramNames null 체크 불필요성으로 인한 제거
- SETNX + INCR 두 개 명령에서 Lua 스크립트(INCR + EXPIRE)로 변경 - TTL 손실 레이스 컨디션 방지 (윈도우 롤오버 시 영구 락 문제 해결) - 관련 테스트를 Lua 스크립트 실행 검증 방식으로 업데이트
- 기존: 10분에 50회 - 변경: 1분에 20회 - 정상 사용에 여유를 두면서 비정상적 대량 업로드 방지
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 15 minutes and 17 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthrough업로드 엔드포인트에 사용자별 분산 속도 제한을 도입합니다. 새로운 Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller as UploadController
participant Aspect as RateLimitAspect
participant Redis
participant Service as UploadService
Client->>Controller: POST /upload (userId)
Controller->>Aspect: 메서드 호출 인터셉트 (`@RateLimit`)
Aspect->>Aspect: SpEL로 키 생성 (예: ratelimit:...:userId)
Aspect->>Redis: Lua 스크립트 실행 (INCR + EXPIRE)
Redis-->>Aspect: 현재 카운트 반환
alt 카운트 ≤ maxRequests
Aspect->>Service: proceed() 호출 (원본 로직 실행)
Service-->>Controller: 처리 완료
Controller-->>Client: 200 OK
else 카운트 > maxRequests
Aspect->>Redis: TTL 조회
Redis-->>Aspect: 남은 시간(초) 반환
Aspect-->>Client: 429 TOO_MANY_REQUESTS (남은 시간 안내)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
🧪 JaCoCo Coverage Report (Changed Files)Summary
Coverage by File
|
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/java/gg/agit/konect/domain/upload/controller/UploadController.java`:
- Line 20: The hardcoded RateLimit values in UploadController
(`@RateLimit`(maxRequests = 20, timeWindowSeconds = 60, keyExpression =
"#userId")) must be externalized: add a `@ConfigurationProperties` class (e.g.,
RateLimitProperties with maxRequests and timeWindowSeconds fields), load them
from application.yml/properties, and modify the rate-limiting implementation
(the RateLimit annotation handling class — e.g., RateLimitAspect or
RateLimitInterceptor) to read defaults from RateLimitProperties at runtime; then
update UploadController to remove numeric attributes and use
`@RateLimit`(keyExpression = "#userId") (or a named policy) so the operational
policy can be tuned without redeploying.
In `@src/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java`:
- Around line 86-91: The current catch in RateLimitAspect swallows SpEL failures
from parser.parseExpression(keyExpression) and silently falls back to the global
key, invalidating per-user limits; update the catch to log the failed expression
and exception (include keyExpression and methodKey) using the class logger, and
change behavior so that if the rate-limit metadata indicates a sensitive rule
(e.g., a boolean like isSensitive on the annotation/metadata you have in
RateLimitAspect) you fail explicitly by rethrowing or returning an error rather
than returning the global key; for non-sensitive cases keep the existing
fallback but ensure the error is logged with the exception to aid debugging.
- Line 41: The current RateLimitAspect uses `@Around`("@annotation(rateLimit)")
which only intercepts method-level annotations and therefore ignores class-level
RateLimit; update the aspect's pointcut expression in RateLimitAspect (the
`@Around` annotation) to include class-level matches by using "@within(rateLimit)
|| `@annotation`(rateLimit)" so both class- and method-level RateLimit annotations
are honored (alternatively, you may remove ElementType.TYPE from the RateLimit
annotation if you intentionally want only method-level application).
- Around line 49-53: The redis calls in RateLimitAspect
(redisTemplate.execute(...) and getExpire(...)) must be wrapped in explicit
exception handling to avoid propagating Redis connection/timeout errors; inside
the method in RateLimitAspect that computes currentCount and expiry for the
key/timeWindowSeconds, catch Redis connection and runtime exceptions and
implement a clear policy (choose either fail-open: log.warn with context (key,
timeWindowSeconds, exception), emit a metric/tag via your metrics system, and
skip rate-limit checks as if no prior count; or fail-closed: log.error with
context, emit a metric, and convert to a 503 ServiceUnavailable by throwing a
custom exception). Ensure both redisTemplate.execute and getExpire calls are
guarded, use the same policy for both, and include descriptive log messages and
a metric increment so operators can detect Redis failures.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4b73b009-5577-4129-b2c5-cd11b7bfeba4
📒 Files selected for processing (6)
src/main/java/gg/agit/konect/domain/upload/controller/UploadController.javasrc/main/java/gg/agit/konect/global/code/ApiResponseCode.javasrc/main/java/gg/agit/konect/global/ratelimit/annotation/RateLimit.javasrc/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.javasrc/main/java/gg/agit/konect/global/ratelimit/exception/RateLimitExceededException.javasrc/test/java/gg/agit/konect/unit/global/ratelimit/aspect/RateLimitAspectTest.java
📜 Review details
⏰ 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). (2)
- GitHub Check: coverage
- GitHub Check: Analyze (java-kotlin)
🧰 Additional context used
📓 Path-based instructions (2)
src/main/java/**/*.java
⚙️ CodeRabbit configuration file
src/main/java/**/*.java: 아래 원칙으로 리뷰 코멘트를 작성한다.
- 코멘트는 반드시 한국어로 작성한다.
- 반드시 수정이 필요한 항목만 코멘트로 남기고, 단순 취향 차이는 지적하지 않는다.
- 각 코멘트 첫 줄에 심각도를
[LEVEL: high|medium|low]형식으로 반드시 표기한다.- 심각도 기준: high=운영 장애 가능, medium=품질 저하, low=개선 권고.
- 각 코멘트는 "문제 -> 영향 -> 제안" 순서로 3문장 이내로 간결하게 작성한다.
- 가능하면 재현 조건 및 실패 시나리오도 포함한다.
- 제안은 현재 코드베이스(Spring Boot + JPA + Flyway) 패턴과 일치해야 한다.
- 보안, 트랜잭션 경계, 예외 처리, N+1, 성능 회귀 가능성을 우선 점검한다.
- 가독성: 변수/메서드 이름이 의도를 바로 드러내는지, 중첩과 메서드 길이가 과도하지 않은지 점검한다.
- 단순화: 불필요한 추상화, 중복 로직, 과한 방어 코드가 있으면 더 단순한 대안을 제시한다.
- 확장성: 새 요구사항 추가 시 변경 범위가 최소화되는 구조인지(하드코딩 분기/값 여부 포함) 점검한다.
Files:
src/main/java/gg/agit/konect/global/code/ApiResponseCode.javasrc/main/java/gg/agit/konect/global/ratelimit/exception/RateLimitExceededException.javasrc/main/java/gg/agit/konect/domain/upload/controller/UploadController.javasrc/main/java/gg/agit/konect/global/ratelimit/annotation/RateLimit.javasrc/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java
**/*
⚙️ CodeRabbit configuration file
**/*: 공통 리뷰 톤 가이드:
- 모든 코멘트는 첫 줄에
[LEVEL: ...]태그를 포함한다.- 과장된 표현 없이 사실 기반으로 작성한다.
- 한 코멘트에는 하나의 이슈만 다룬다.
- 코드 예시가 필요하면 최소 수정 예시를 제시한다.
- 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.
Files:
src/main/java/gg/agit/konect/global/code/ApiResponseCode.javasrc/main/java/gg/agit/konect/global/ratelimit/exception/RateLimitExceededException.javasrc/main/java/gg/agit/konect/domain/upload/controller/UploadController.javasrc/main/java/gg/agit/konect/global/ratelimit/annotation/RateLimit.javasrc/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.javasrc/test/java/gg/agit/konect/unit/global/ratelimit/aspect/RateLimitAspectTest.java
src/main/java/gg/agit/konect/domain/upload/controller/UploadController.java
Show resolved
Hide resolved
src/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java
Outdated
Show resolved
Hide resolved
src/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java
Outdated
Show resolved
Hide resolved
src/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java
Show resolved
Hide resolved
- 클래스 레벨 @ratelimit 지원 (@Within 추가) - Redis 장애 시 fail-open 정책 적용 (예외 발생 시 rate limit 체크 스킵) - SpEL 평가 실패 시 에러 로깅 추가 Closes coderabbitai review comments: - 클래스 레벨 @ratelimit 선언과 실제 AOP 적용 범위 불일치 문제 - Redis 장애 시 레이트리밋 체크 예외 전파 문제 - SpEL 표현식 평가 실패 시 로깅 없이 전역 제한으로 폴백 문제
dh2906
left a comment
There was a problem hiding this comment.
코드리뷰 반영 완료:
[2] 클래스 레벨 @ratelimit 지원 - @Within(rateLimit) || @annotation(rateLimit)로 수정
[3] Redis 장애 처리 - try-catch로 예외 처리, fail-open 정책 적용
[4] SpEL 평가 실패 로깅 - log.error()로 keyExpression과 methodKey 기록
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java`:
- Around line 86-92: In RateLimitAspect (around the StandardEvaluationContext
creation and use of signature.getParameterNames()), add a defensive null-check
for the result of signature.getParameterNames(): if it returns null, fall back
to generating stable placeholder names (e.g., "arg0".."argN") or use an empty
array and map variables by index using joinPoint.getArgs(); then proceed to set
variables into the context using those names so the for-loop cannot NPE when
paramNames is null. This change ensures the loop that sets
context.setVariable(...) in RateLimitAspect will be safe in builds compiled
without -parameters.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: a8cb239c-b461-409c-8014-a5c26d4b7cbb
📒 Files selected for processing (4)
build.gradlesrc/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.javasrc/test/java/gg/agit/konect/support/EmbeddedRedisConfig.javasrc/test/java/gg/agit/konect/support/IntegrationTestSupport.java
📜 Review details
⏰ 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). (2)
- GitHub Check: coverage
- GitHub Check: Analyze (java-kotlin)
🧰 Additional context used
📓 Path-based instructions (2)
**/*
⚙️ CodeRabbit configuration file
**/*: 공통 리뷰 톤 가이드:
- 모든 코멘트는 첫 줄에
[LEVEL: ...]태그를 포함한다.- 과장된 표현 없이 사실 기반으로 작성한다.
- 한 코멘트에는 하나의 이슈만 다룬다.
- 코드 예시가 필요하면 최소 수정 예시를 제시한다.
- 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.
Files:
build.gradlesrc/test/java/gg/agit/konect/support/EmbeddedRedisConfig.javasrc/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.javasrc/test/java/gg/agit/konect/support/IntegrationTestSupport.java
src/main/java/**/*.java
⚙️ CodeRabbit configuration file
src/main/java/**/*.java: 아래 원칙으로 리뷰 코멘트를 작성한다.
- 코멘트는 반드시 한국어로 작성한다.
- 반드시 수정이 필요한 항목만 코멘트로 남기고, 단순 취향 차이는 지적하지 않는다.
- 각 코멘트 첫 줄에 심각도를
[LEVEL: high|medium|low]형식으로 반드시 표기한다.- 심각도 기준: high=운영 장애 가능, medium=품질 저하, low=개선 권고.
- 각 코멘트는 "문제 -> 영향 -> 제안" 순서로 3문장 이내로 간결하게 작성한다.
- 가능하면 재현 조건 및 실패 시나리오도 포함한다.
- 제안은 현재 코드베이스(Spring Boot + JPA + Flyway) 패턴과 일치해야 한다.
- 보안, 트랜잭션 경계, 예외 처리, N+1, 성능 회귀 가능성을 우선 점검한다.
- 가독성: 변수/메서드 이름이 의도를 바로 드러내는지, 중첩과 메서드 길이가 과도하지 않은지 점검한다.
- 단순화: 불필요한 추상화, 중복 로직, 과한 방어 코드가 있으면 더 단순한 대안을 제시한다.
- 확장성: 새 요구사항 추가 시 변경 범위가 최소화되는 구조인지(하드코딩 분기/값 여부 포함) 점검한다.
Files:
src/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java
🪛 ast-grep (0.42.1)
src/test/java/gg/agit/konect/support/EmbeddedRedisConfig.java
[info] 45-45: "Detected use of a Java socket that is not encrypted. As a result, the
traffic could be read by an attacker intercepting the network traffic. Use
an SSLSocket created by 'SSLSocketFactory' or 'SSLServerSocketFactory'
instead."
Context: new ServerSocket(0)
Note: [CWE-319] Cleartext Transmission of Sensitive Information [REFERENCES]
- https://owasp.org/Top10/A02_2021-Cryptographic_Failures
(unencrypted-socket-java)
🔇 Additional comments (5)
src/test/java/gg/agit/konect/support/EmbeddedRedisConfig.java (2)
45-48: [LEVEL: low] Static analysis 경고는 false positive입니다.
ServerSocket(0)에 대한 "unencrypted socket" 경고는 테스트용 embedded Redis 포트 탐색 목적이므로 무시해도 됩니다. 민감한 데이터 전송이 아닌 단순 포트 할당용입니다.
24-28: [LEVEL: resolved] Spring이 LettuceConnectionFactory 초기화를 자동으로 처리합니다이 코드는 올바릅니다.
LettuceConnectionFactory는InitializingBean을 구현하므로,@Bean메서드에서 반환될 때 Spring이 자동으로afterPropertiesSet()을 호출합니다. 명시적 호출은 불필요하며,@PostConstruct startRedis()가 실행된 후 빈이 요청될 때 초기화가 완료되므로 순서도 올바릅니다.src/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java (1)
41-75: [LEVEL: low] 이전 리뷰 피드백이 반영되었습니다.
- 클래스 레벨
@RateLimit지원을 위한@within(rateLimit)포인트컷 추가 완료- Redis 장애 시 fail-open 정책(lines 63-67)으로 API 가용성 보장
- SpEL 평가 실패 시
log.error()로깅 추가(lines 99-100)구조적으로 잘 개선되었습니다.
build.gradle (1)
89-89: [LEVEL: low] embedded-redis 테스트 의존성 추가 확인
com.github.codemonstur:embedded-redis는kstyrc/embedded-redis의 유지보수 포크로 적절한 선택입니다. Rate limiting 테스트를 위한 Redis 인프라 구성에 필요합니다.src/test/java/gg/agit/konect/support/IntegrationTestSupport.java (1)
46-46: [LEVEL: low] 통합 테스트에 EmbeddedRedisConfig 추가 확인Rate limiting 기능을 위한 Redis 인프라를 통합 테스트 컨텍스트에 올바르게 추가했습니다.
src/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java
Show resolved
Hide resolved
- Lua 스크립트 객체를 매 요청마다 생성에서 생성자에서 한 번만 생성하도록 변경 - Redis getExpire 호출을 제한 초과 시에만 실행하도록 지연 로딩 적용 - @requiredargsconstructor 제거 및 커스텀 생성자 정리
- signature.getParameterNames()가 null을 반환하는 경우 방어 - -parameters 컴파일 플래그 없이 빌드된 경우 arg0, arg1... 형태의 플레이스홀더 이름 생성하여 SpEL 평가 안전하게 수행
There was a problem hiding this comment.
♻️ Duplicate comments (2)
src/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java (2)
69-75:⚠️ Potential issue | 🔴 Critical[LEVEL: high] 제한 초과 경로의 Redis 조회가 fail-open 정책에서 빠져 있습니다.
문제: Line 71의
redisTemplate.getExpire(key)는 예외 처리가 없어, 제한을 넘긴 요청에서 Redis 타임아웃/연결 오류가 나면 500이 그대로 전파됩니다. 영향: 평소에는 정상 처리되다가 임계 사용자 요청만 장애 시점에 업로드 실패로 바뀌어 운영 장애를 더 복잡하게 만듭니다. 제안: TTL 조회도 같은try/catch로 감싸고, 실패 시timeWindowSeconds로 대체하거나 앞선execute()와 동일한 fail-open 정책으로 통일하세요.최소 수정 예시
if (currentCount > maxRequests) { - Long remainingSeconds = redisTemplate.getExpire(key); + Long remainingSeconds; + try { + remainingSeconds = redisTemplate.getExpire(key); + } catch (Exception e) { + log.warn("Rate limit TTL lookup failed for key={}. Using default window.", key, e); + remainingSeconds = Long.valueOf(timeWindowSeconds); + } long remaining = remainingSeconds != null && remainingSeconds > 0 ? remainingSeconds : timeWindowSeconds; throw RateLimitExceededException.withRemainingTime(remaining); }As per coding guidelines, '보안, 트랜잭션 경계, 예외 처리, N+1, 성능 회귀 가능성을 우선 점검한다.'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java` around lines 69 - 75, The TTL lookup on limit-exceeded path uses redisTemplate.getExpire(key) without error handling, so Redis failures can surface as 500s for throttled requests; wrap the TTL fetch in the same try/catch/fail-open pattern used by the earlier execute() call in RateLimitAspect: catch any Redis exceptions around redisTemplate.getExpire(key) and on error (or null/invalid TTL) fall back to timeWindowSeconds, then call RateLimitExceededException.withRemainingTime(remaining) as before; reference the redisTemplate.getExpire(key) call, the surrounding RateLimitAspect class, and the RateLimitExceededException.withRemainingTime method when making the change.
98-105:⚠️ Potential issue | 🟠 Major[LEVEL: medium] SpEL 실패 시 전역 키로 폴백되어 사용자별 제한이 무너집니다.
문제: Line 102-105는
keyExpression이 설정된 경우에도 평가 실패 시ratelimit:<method>로 폴백해 모든 사용자가 같은 카운터를 공유하게 만듭니다. 영향:#userId오타나 파라미터명 변경만으로 한 사용자의 업로드가 전체 사용자 429로 번질 수 있습니다. 제안: 사용자별 제한이 필수인 규칙에서는 이 경로를 설정 오류로 처리해 즉시 실패시키고, 전역 폴백은 명시적으로 허용한 경우에만 사용하세요.As per coding guidelines, '확장성: 새 요구사항 추가 시 변경 범위가 최소화되는 구조인지(하드코딩 분기/값 여부 포함) 점검한다.'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java` around lines 98 - 105, The current catch in RateLimitAspect swallows SpEL failures and returns a global key (RATE_LIMIT_KEY_PREFIX + methodKey), breaking per-user limits; change the behavior so that if a keyExpression was provided and parsing/evaluation via parser.parseExpression(keyExpression).getValue(context) throws, you treat it as a configuration/error condition rather than silently falling back: log the full exception, and rethrow a specific runtime exception (or return an error response) so the request fails fast; if a global fallback is explicitly allowed, gate that behavior behind a configurable flag (e.g., allowGlobalFallback) checked when methodKey/keyExpression are present so the current global-key fallback only occurs when the flag is true.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java`:
- Around line 69-75: The TTL lookup on limit-exceeded path uses
redisTemplate.getExpire(key) without error handling, so Redis failures can
surface as 500s for throttled requests; wrap the TTL fetch in the same
try/catch/fail-open pattern used by the earlier execute() call in
RateLimitAspect: catch any Redis exceptions around redisTemplate.getExpire(key)
and on error (or null/invalid TTL) fall back to timeWindowSeconds, then call
RateLimitExceededException.withRemainingTime(remaining) as before; reference the
redisTemplate.getExpire(key) call, the surrounding RateLimitAspect class, and
the RateLimitExceededException.withRemainingTime method when making the change.
- Around line 98-105: The current catch in RateLimitAspect swallows SpEL
failures and returns a global key (RATE_LIMIT_KEY_PREFIX + methodKey), breaking
per-user limits; change the behavior so that if a keyExpression was provided and
parsing/evaluation via parser.parseExpression(keyExpression).getValue(context)
throws, you treat it as a configuration/error condition rather than silently
falling back: log the full exception, and rethrow a specific runtime exception
(or return an error response) so the request fails fast; if a global fallback is
explicitly allowed, gate that behavior behind a configurable flag (e.g.,
allowGlobalFallback) checked when methodKey/keyExpression are present so the
current global-key fallback only occurs when the flag is true.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 76db3bff-2a44-42e6-9ddd-0b1d13b99a49
📒 Files selected for processing (1)
src/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java
📜 Review details
⏰ 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). (2)
- GitHub Check: coverage
- GitHub Check: Analyze (java-kotlin)
🧰 Additional context used
📓 Path-based instructions (2)
src/main/java/**/*.java
⚙️ CodeRabbit configuration file
src/main/java/**/*.java: 아래 원칙으로 리뷰 코멘트를 작성한다.
- 코멘트는 반드시 한국어로 작성한다.
- 반드시 수정이 필요한 항목만 코멘트로 남기고, 단순 취향 차이는 지적하지 않는다.
- 각 코멘트 첫 줄에 심각도를
[LEVEL: high|medium|low]형식으로 반드시 표기한다.- 심각도 기준: high=운영 장애 가능, medium=품질 저하, low=개선 권고.
- 각 코멘트는 "문제 -> 영향 -> 제안" 순서로 3문장 이내로 간결하게 작성한다.
- 가능하면 재현 조건 및 실패 시나리오도 포함한다.
- 제안은 현재 코드베이스(Spring Boot + JPA + Flyway) 패턴과 일치해야 한다.
- 보안, 트랜잭션 경계, 예외 처리, N+1, 성능 회귀 가능성을 우선 점검한다.
- 가독성: 변수/메서드 이름이 의도를 바로 드러내는지, 중첩과 메서드 길이가 과도하지 않은지 점검한다.
- 단순화: 불필요한 추상화, 중복 로직, 과한 방어 코드가 있으면 더 단순한 대안을 제시한다.
- 확장성: 새 요구사항 추가 시 변경 범위가 최소화되는 구조인지(하드코딩 분기/값 여부 포함) 점검한다.
Files:
src/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java
**/*
⚙️ CodeRabbit configuration file
**/*: 공통 리뷰 톤 가이드:
- 모든 코멘트는 첫 줄에
[LEVEL: ...]태그를 포함한다.- 과장된 표현 없이 사실 기반으로 작성한다.
- 한 코멘트에는 하나의 이슈만 다룬다.
- 코드 예시가 필요하면 최소 수정 예시를 제시한다.
- 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.
Files:
src/main/java/gg/agit/konect/global/ratelimit/aspect/RateLimitAspect.java
🔍 개요
🚀 주요 변경 내용
Rate Limiting 기반 구조 추가
@RateLimit어노테이션 추가: 메서드 레벨에서 Rate Limit 적용 가능RateLimitAspect구현: AOP를 통한 선언적 Rate Limit 처리RateLimitExceededException팩토리: 429 응답 생성TOO_MANY_REQUESTS(429) 응답 코드 추가이미지 업로드 API 적용
UploadController.uploadImage()에 Rate Limit 적용#userId로 사용자 식별)테스트
RateLimitAspectTest단위 테스트 추가💬 참고 사항
✅ Checklist (완료 조건)