Conversation
Walkthrough이번 변경에서는 .env 파일에 새로운 API 키 환경 변수가 추가되었고, build.gradle에 WebFlux, validation, Mockito, Reactor 테스트 관련 의존성이 추가되었습니다. 또한 PostgreSQL 데이터 디렉터리 내 버전 정보를 나타내는 PG_VERSION 파일 두 개가 삭제되었습니다. Changes
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (3)
💤 Files with no reviewable changes (2)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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)
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 17
🔭 Outside diff range comments (1)
postgres-data/postmaster.pid (1)
1-9: 런타임 아티팩트를 버전 관리에서 제외하세요.
postgres-data/postmaster.pid파일은 PostgreSQL 데이터베이스의 런타임 아티팩트입니다. 이런 파일들은 데이터베이스가 시작될 때마다 변경되며, 버전 관리 시스템에 포함되어서는 안 됩니다.해결 방안:
postgres-data/전체 디렉토리를.gitignore에 추가- 기존 커밋에서 이 파일 제거
- 데이터베이스 관련 런타임 파일들이 실수로 커밋되지 않도록 방지
🧹 Nitpick comments (4)
src/main/java/hello/cluebackend/domain/user/service/RegisterUserService.java (1)
17-23: 가독성과 인자 순서 오류 방지를 위해 빌더 패턴 사용 권장
생성자에 다섯 개 이상의 파라미터를 순서대로 넘기면 실수 가능성이 높습니다.UserEntity.builder()혹은 정적 팩토리 메서드를 도입하면 필드명을 명시적으로 적을 수 있어 유지보수에 용이합니다.
예)UserEntity userEntity = UserEntity.builder() .studentId(userDTO.getStudentId()) .username(userDTO.getUsername()) .password(encodedPassword) .email(userDTO.getEmail()) .role(userDTO.getRole()) .build();src/main/java/hello/cluebackend/domain/timetable/presentation/dto/request/TimetableRequestDto.java (1)
7-10: 사용하지 않는 import 제거 필요사용하지 않는 import들이 포함되어 있습니다.
-import lombok.Data; -import lombok.Setter; -import org.hibernate.annotations.processing.Pattern;src/main/java/hello/cluebackend/domain/timetable/service/TimetableService.java (1)
100-116: 주석으로 처리된 올바른 코드 제거 필요주석으로 처리된 코드는 코드베이스를 어지럽힙니다. 위의 수정사항을 적용한 후 이 주석 블록을 제거해야 합니다.
-// .uri(uriBuilder -> uriBuilder -// .scheme("https") -// .host(neisApiHost) -// .path(TIMETABLE_PATH) -// .queryParam("KEY", neisApiKey) -// .queryParam("Type", "json") -// .queryParam("pIndex", "1") -// .queryParam("pSize", "100") -// .queryParam("ATPT_OFCDC_SC_CODE", atptCode) -// .queryParam("SD_SCHUL_CODE", schoolCode) -// .queryParam("AY", year) -// .queryParam("SEM", semester) -// .queryParam("GRADE", request.getGrade()) -// .queryParam("CLASS_NM", request.getClassNumber()) -// .queryParam("TI_FROM_YMD", from) -// .queryParam("TI_TO_YMD", to) -// .build())src/main/java/hello/cluebackend/domain/timetable/presentation/TimetableController.java (1)
13-16: API 문서화 어노테이션 추가 권장컨트롤러에 Swagger/OpenAPI 문서화 어노테이션을 추가하면 API 사용성이 향상됩니다.
+@Tag(name = "Timetable", description = "시간표 조회 API") @RestController @RequestMapping("/api/timetable") @RequiredArgsConstructor public class TimetableController {각 엔드포인트에도
@Operation어노테이션을 추가하세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
.env(1 hunks)build.gradle(2 hunks)postgres-data/postmaster.pid(1 hunks)src/main/java/hello/cluebackend/domain/timetable/presentation/TimetableController.java(1 hunks)src/main/java/hello/cluebackend/domain/timetable/presentation/dto/request/TimetableRequestDto.java(1 hunks)src/main/java/hello/cluebackend/domain/timetable/presentation/dto/response/TimetableResponseDto.java(1 hunks)src/main/java/hello/cluebackend/domain/timetable/service/TimetableService.java(1 hunks)src/main/java/hello/cluebackend/domain/user/domain/repository/UserRepository.java(0 hunks)src/main/java/hello/cluebackend/domain/user/presentation/RegisterController.java(0 hunks)src/main/java/hello/cluebackend/domain/user/service/RegisterUserService.java(1 hunks)src/main/java/hello/cluebackend/global/config/SecurityConfig.java(2 hunks)src/main/java/hello/cluebackend/global/config/WebClientConfig.java(1 hunks)src/main/resources/application.yaml(2 hunks)src/test/java/hello/cluebackend/ClueBackendApplicationTests.java(0 hunks)
💤 Files with no reviewable changes (3)
- src/main/java/hello/cluebackend/domain/user/presentation/RegisterController.java
- src/main/java/hello/cluebackend/domain/user/domain/repository/UserRepository.java
- src/test/java/hello/cluebackend/ClueBackendApplicationTests.java
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/hello/cluebackend/domain/timetable/presentation/dto/response/TimetableResponseDto.java (1)
src/main/java/hello/cluebackend/domain/timetable/presentation/dto/request/TimetableRequestDto.java (1)
Getter(12-28)
🪛 Gitleaks (8.26.0)
src/main/resources/application.yaml
41-41: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🔇 Additional comments (5)
src/main/resources/application.yaml (1)
5-5: 데이터베이스 호스트명 변경이 적절합니다.PostgreSQL 호스트명을
my_postgres에서postgresql로 변경한 것은 Docker 서비스 이름 표준화에 맞는 좋은 변경입니다.build.gradle (2)
33-35: 리액티브 프로그래밍 의존성 추가가 적절합니다.WebFlux와 validation 스타터, 그리고 reactor-test 의존성을 추가한 것은 새로운 시간표 API의 리액티브 기능을 지원하기 위한 올바른 선택입니다.
60-61: 테스트 의존성 추가가 좋습니다.Mockito 의존성을 추가하여 서비스 계층 테스트를 위한 모킹 기능을 지원하는 것은 좋은 테스트 관행입니다.
src/main/java/hello/cluebackend/global/config/SecurityConfig.java (2)
77-77: CORS 설정 개선이 적절합니다.
setAllowedOrigins에서setAllowedOriginPatterns로 변경한 것은 패턴 기반 원본 매칭을 위한 올바른 업데이트입니다.
117-117: 시간표 API 엔드포인트 허용 설정이 적절합니다.시간표 API 엔드포인트
/api/timetable/**를 인증 없이 접근 가능하도록 설정한 것은 공개 API의 특성상 적절한 보안 정책입니다.
| UserEntity userEntity = new UserEntity( | ||
| userDTO.getStudentId(), | ||
| userDTO.getUsername(), | ||
| userDTO.getStudentId()+userDTO.getUsername(), | ||
| userDTO.getEmail(), | ||
| userDTO.getRole() | ||
| ); |
There was a problem hiding this comment.
평문(plain-text) 패스워드 저장은 심각한 보안 취약점입니다
현재 password 파라미터에 studentId + username을 그대로 넣고 있으며, 암호화(BCrypt 등)가 전혀 적용되지 않았습니다.
사용자 정보 DB 유출 시 즉시 계정 탈취가 가능하므로, 반드시 패스워드 필드를 DTO로 받아서 해시 후 저장하도록 수정해 주세요.
- UserEntity userEntity = new UserEntity(
- userDTO.getStudentId(),
- userDTO.getUsername(),
- userDTO.getStudentId()+userDTO.getUsername(),
- userDTO.getEmail(),
- userDTO.getRole()
- );
+ // 예시: Spring Security PasswordEncoder 주입 후 사용
+ String encodedPassword = passwordEncoder.encode(userDTO.getPassword());
+ UserEntity userEntity = new UserEntity(
+ userDTO.getStudentId(),
+ userDTO.getUsername(),
+ encodedPassword,
+ userDTO.getEmail(),
+ userDTO.getRole()
+ );🤖 Prompt for AI Agents
In src/main/java/hello/cluebackend/domain/user/service/RegisterUserService.java
around lines 17 to 23, the password is currently set as a plain-text
concatenation of studentId and username without any encryption. To fix this,
modify the code to accept the password from the DTO, then hash it securely using
a strong algorithm like BCrypt before storing it in the UserEntity. Replace the
plain-text password assignment with the hashed password to ensure secure
password storage.
There was a problem hiding this comment.
oauth2를 이용해서 비밀번호 따위는 존재하지 않음
There was a problem hiding this comment.
@fixgramwork OAuth2를 사용한다면 패스워드 저장이 필요 없다는 점을 이해했습니다.
하지만 현재 코드에서는 여전히 studentId + username을 패스워드 필드에 저장하고 있습니다. OAuth2 전용 인증을 사용한다면 다음과 같은 개선을 제안합니다:
public void registerUser(DefaultRegisterUserDTO userDTO) {
UserEntity userEntity = new UserEntity(
userDTO.getStudentId(),
userDTO.getUsername(),
null, // OAuth2 사용으로 패스워드 불필요
userDTO.getEmail(),
userDTO.getRole()
);
userRepository.save(userEntity);
}또는 UserEntity 구조를 수정하여 패스워드 필드를 아예 제거하는 것이 더 명확할 것 같습니다.
✏️ Learnings added
Learnt from: fixgramwork
PR: EscapeFrame/clueBackend#23
File: src/main/java/hello/cluebackend/domain/user/service/RegisterUserService.java:17-23
Timestamp: 2025-07-08T23:38:29.625Z
Learning: This project uses OAuth2 authentication and does not require traditional password storage or validation. The RegisterUserService in src/main/java/hello/cluebackend/domain/user/service/RegisterUserService.java should not store passwords since OAuth2 handles authentication externally.
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.
| @@ -0,0 +1 @@ | |||
| NEIS_API_KEY=8ba18e6d2b1f42f9be762009bc3dfa90 No newline at end of file | |||
There was a problem hiding this comment.
보안 경고: API 키가 버전 관리 시스템에 노출되었습니다.
NEIS API 키가 .env 파일에 하드코딩되어 버전 관리 시스템에 커밋되었습니다. 이는 심각한 보안 위험을 초래할 수 있습니다.
해결 방안:
- 이 파일을
.gitignore에 추가하여 향후 커밋을 방지 - 기존 커밋 히스토리에서 API 키 제거
- 노출된 API 키 교체 검토
- 프로덕션 환경에서는 환경 변수나 시크릿 관리 시스템 사용
🤖 Prompt for AI Agents
In the .env file at line 1, the NEIS_API_KEY is hardcoded and committed to
version control, posing a security risk. To fix this, add the .env file to
.gitignore to prevent future commits, remove the API key from the git history
using tools like git filter-branch or BFG Repo-Cleaner, replace the exposed API
key with a new one, and ensure that in production the API key is managed
securely via environment variables or a secrets management system.
There was a problem hiding this comment.
@pdh0128 네, 알겠습니다! 🐰
API 키를 잘 활용하시길 바랍니다. 향후 프로덕션 환경에서는 보안을 위해 환경 변수나 시크릿 관리 시스템 사용을 고려해보시면 좋을 것 같습니다.
(´∀`)♡
|
|
||
| http | ||
| .securityMatcher("/first-register", "/register") | ||
| .securityMatcher("/first-register", "/register","api/timetable/**") |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
보안 매처 경로 패턴 불일치를 확인하세요.
securityMatcher에서 "api/timetable/**"로 설정했지만, 실제 요청 경로는 /api/timetable/**입니다. 앞의 슬래시(/) 누락으로 인해 매처가 올바르게 작동하지 않을 수 있습니다.
다음 스크립트를 실행하여 애플리케이션의 다른 부분에서 일관된 경로 패턴을 사용하는지 확인하세요:
🏁 Script executed:
#!/bin/bash
# 설명: 시간표 API 경로 패턴의 일관성을 확인합니다.
# 테스트: 프로젝트 내에서 시간표 API 경로 사용을 검색합니다.
echo "=== 시간표 API 경로 패턴 검색 ==="
rg -A 2 -B 2 "timetable" --type java
echo "=== 보안 매처 패턴 검색 ==="
rg -A 2 -B 2 "securityMatcher" --type javaLength of output: 7496
보안 매처 경로 패턴 수정 필요
securityMatcher에 등록된 "api/timetable/**" 패턴은 앞에 슬래시가 빠져 있어 실제 컨트롤러 매핑(/api/timetable/**)과 불일치합니다. 다음 위치를 변경해 주세요:
- 파일:
src/main/java/hello/cluebackend/global/config/SecurityConfig.java - 라인: 약 122
변경 전:
.securityMatcher("/first-register", "/register","api/timetable/**")변경 후:
-.securityMatcher("/first-register", "/register","api/timetable/**")
+.securityMatcher("/first-register", "/register", "/api/timetable/**")🤖 Prompt for AI Agents
In src/main/java/hello/cluebackend/global/config/SecurityConfig.java at line
122, the securityMatcher method includes the pattern "api/timetable/**" missing
a leading slash, causing a mismatch with the actual controller mapping
"/api/timetable/**". Fix this by adding the leading slash to the pattern so it
reads "/api/timetable/**" to correctly match the controller paths.
| @NotBlank(message = "학년은 필수입니다.") | ||
| @Min(value = 1) | ||
| @Max(value = 3) | ||
| private String grade; | ||
|
|
||
| @NotBlank(message = "반은 필수입니다.") | ||
| @Min(value = 1) | ||
| @Max(value = 4) | ||
| private String classNumber; |
There was a problem hiding this comment.
String 필드에 대한 잘못된 validation 수정 필요
@Min과 @Max 어노테이션은 String 타입에 적용되지 않습니다. String 값의 숫자 범위를 검증하려면 @Pattern 어노테이션을 사용해야 합니다.
- @NotBlank(message = "학년은 필수입니다.")
- @Min(value = 1)
- @Max(value = 3)
- private String grade;
-
- @NotBlank(message = "반은 필수입니다.")
- @Min(value = 1)
- @Max(value = 4)
- private String classNumber;
+ @NotBlank(message = "학년은 필수입니다.")
+ @Pattern(regexp = "^[1-3]$", message = "학년은 1~3 사이여야 합니다.")
+ private String grade;
+
+ @NotBlank(message = "반은 필수입니다.")
+ @Pattern(regexp = "^[1-4]$", message = "반은 1~4 사이여야 합니다.")
+ private String classNumber;📝 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.
| @NotBlank(message = "학년은 필수입니다.") | |
| @Min(value = 1) | |
| @Max(value = 3) | |
| private String grade; | |
| @NotBlank(message = "반은 필수입니다.") | |
| @Min(value = 1) | |
| @Max(value = 4) | |
| private String classNumber; | |
| @NotBlank(message = "학년은 필수입니다.") | |
| @Pattern(regexp = "^[1-3]$", message = "학년은 1~3 사이여야 합니다.") | |
| private String grade; | |
| @NotBlank(message = "반은 필수입니다.") | |
| @Pattern(regexp = "^[1-4]$", message = "반은 1~4 사이여야 합니다.") | |
| private String classNumber; |
🤖 Prompt for AI Agents
In
src/main/java/hello/cluebackend/domain/timetable/presentation/dto/request/TimetableRequestDto.java
around lines 14 to 22, the fields grade and classNumber are String types but
incorrectly use @Min and @Max annotations which only apply to numeric types.
Replace these with @Pattern annotations that enforce the numeric range as a
regex pattern to validate the string values correctly.
| return webClient.get() | ||
| .uri("https://" + neisApiHost + TIMETABLE_PATH + | ||
| "?KEY=" + neisApiKey + | ||
| "&Type=json" + | ||
| "&pIndex=1" + | ||
| "&pSize=100" + | ||
| "&ATPT_OFCDC_SC_CODE=" + atptCode + | ||
| "&SD_SCHUL_CODE=" + schoolCode + | ||
| "&AY=" + year + | ||
| "&SEM=" + semester + | ||
| "&GRADE=" + request.getGrade() + | ||
| "&CLASS_NM=" + request.getClassNumber() + | ||
| "&TI_FROM_YMD=" + from + | ||
| "&TI_TO_YMD=" + to) |
There was a problem hiding this comment.
수동 URI 생성 대신 URI Builder 사용 필요
문자열 연결을 통한 URI 생성은 오류가 발생하기 쉽고 보안에 취약합니다. 주석으로 처리된 올바른 URI 빌더 방식을 사용해야 합니다.
- .uri("https://" + neisApiHost + TIMETABLE_PATH +
- "?KEY=" + neisApiKey +
- "&Type=json" +
- "&pIndex=1" +
- "&pSize=100" +
- "&ATPT_OFCDC_SC_CODE=" + atptCode +
- "&SD_SCHUL_CODE=" + schoolCode +
- "&AY=" + year +
- "&SEM=" + semester +
- "&GRADE=" + request.getGrade() +
- "&CLASS_NM=" + request.getClassNumber() +
- "&TI_FROM_YMD=" + from +
- "&TI_TO_YMD=" + to)
+ .uri(uriBuilder -> uriBuilder
+ .scheme("https")
+ .host(neisApiHost)
+ .path(TIMETABLE_PATH)
+ .queryParam("KEY", neisApiKey)
+ .queryParam("Type", "json")
+ .queryParam("pIndex", "1")
+ .queryParam("pSize", "100")
+ .queryParam("ATPT_OFCDC_SC_CODE", atptCode)
+ .queryParam("SD_SCHUL_CODE", schoolCode)
+ .queryParam("AY", year)
+ .queryParam("SEM", semester)
+ .queryParam("GRADE", request.getGrade())
+ .queryParam("CLASS_NM", request.getClassNumber())
+ .queryParam("TI_FROM_YMD", from)
+ .queryParam("TI_TO_YMD", to)
+ .build())📝 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.
| return webClient.get() | |
| .uri("https://" + neisApiHost + TIMETABLE_PATH + | |
| "?KEY=" + neisApiKey + | |
| "&Type=json" + | |
| "&pIndex=1" + | |
| "&pSize=100" + | |
| "&ATPT_OFCDC_SC_CODE=" + atptCode + | |
| "&SD_SCHUL_CODE=" + schoolCode + | |
| "&AY=" + year + | |
| "&SEM=" + semester + | |
| "&GRADE=" + request.getGrade() + | |
| "&CLASS_NM=" + request.getClassNumber() + | |
| "&TI_FROM_YMD=" + from + | |
| "&TI_TO_YMD=" + to) | |
| return webClient.get() | |
| .uri(uriBuilder -> uriBuilder | |
| .scheme("https") | |
| .host(neisApiHost) | |
| .path(TIMETABLE_PATH) | |
| .queryParam("KEY", neisApiKey) | |
| .queryParam("Type", "json") | |
| .queryParam("pIndex", "1") | |
| .queryParam("pSize", "100") | |
| .queryParam("ATPT_OFCDC_SC_CODE", atptCode) | |
| .queryParam("SD_SCHUL_CODE", schoolCode) | |
| .queryParam("AY", year) | |
| .queryParam("SEM", semester) | |
| .queryParam("GRADE", request.getGrade()) | |
| .queryParam("CLASS_NM", request.getClassNumber()) | |
| .queryParam("TI_FROM_YMD", from) | |
| .queryParam("TI_TO_YMD", to) | |
| .build()) |
🤖 Prompt for AI Agents
In
src/main/java/hello/cluebackend/domain/timetable/service/TimetableService.java
lines 86 to 99, the URI for the webClient.get() call is constructed using string
concatenation, which is error-prone and insecure. Replace this manual string
building with the URI builder approach provided by WebClient, using
uriBuilder.queryParam() methods to safely add each query parameter. This ensures
proper encoding and improves security and maintainability.
| .retryWhen(Retry.backoff(RETRY_ATTEMPTS, RETRY_DELAY) | ||
| .filter(throwable -> !(throwable instanceof WebClientResponseException.BadRequest)) | ||
| .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> { | ||
| log.error("NEIS API 재시도 횟수 초과"); | ||
| return new RuntimeException("NEIS API 호출 실패 - 호출 시간 초과"); | ||
| })) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
재시도 로직 개선 필요
현재 재시도 로직이 BadRequest만 제외하고 있지만, 다른 4xx 에러들도 재시도하면 안 됩니다.
- .filter(throwable -> !(throwable instanceof WebClientResponseException.BadRequest))
+ .filter(throwable -> {
+ if (throwable instanceof WebClientResponseException) {
+ WebClientResponseException ex = (WebClientResponseException) throwable;
+ return !ex.getStatusCode().is4xxClientError();
+ }
+ return true;
+ })📝 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.
| .retryWhen(Retry.backoff(RETRY_ATTEMPTS, RETRY_DELAY) | |
| .filter(throwable -> !(throwable instanceof WebClientResponseException.BadRequest)) | |
| .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> { | |
| log.error("NEIS API 재시도 횟수 초과"); | |
| return new RuntimeException("NEIS API 호출 실패 - 호출 시간 초과"); | |
| })) | |
| .retryWhen(Retry.backoff(RETRY_ATTEMPTS, RETRY_DELAY) | |
| .filter(throwable -> { | |
| if (throwable instanceof WebClientResponseException) { | |
| WebClientResponseException ex = (WebClientResponseException) throwable; | |
| return !ex.getStatusCode().is4xxClientError(); | |
| } | |
| return true; | |
| }) | |
| .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> { | |
| log.error("NEIS API 재시도 횟수 초과"); | |
| return new RuntimeException("NEIS API 호출 실패 - 호출 시간 초과"); | |
| })) |
🤖 Prompt for AI Agents
In
src/main/java/hello/cluebackend/domain/timetable/service/TimetableService.java
around lines 132 to 137, the retry logic currently excludes only BadRequest
(400) errors from retrying, but other 4xx client errors should also not trigger
retries. Update the filter in the retryWhen clause to exclude all 4xx HTTP
errors by checking if the throwable is an instance of WebClientResponseException
and its status code is in the 4xx range, and prevent retries for those errors.
| public Mono<ResponseEntity<List<TimetableResponseDto>>> getTodayTimetable( | ||
| @RequestParam(required = true) String grade, | ||
| @RequestParam(required = true) String classNumber | ||
| ) { |
There was a problem hiding this comment.
🛠️ Refactor suggestion
컨트롤러 레벨 validation 추가 필요
요청 파라미터에 대한 검증이 없습니다. @Valid 어노테이션과 함께 적절한 검증을 추가해야 합니다.
@GetMapping("/today")
public Mono<ResponseEntity<List<TimetableResponseDto>>> getTodayTimetable(
- @RequestParam(required = true) String grade,
- @RequestParam(required = true) String classNumber
+ @RequestParam @Pattern(regexp = "^[1-3]$", message = "학년은 1~3 사이여야 합니다.") String grade,
+ @RequestParam @Pattern(regexp = "^[1-4]$", message = "반은 1~4 사이여야 합니다.") String classNumber
) {동일한 패턴을 /weekly 엔드포인트에도 적용하세요.
Also applies to: 37-40
🤖 Prompt for AI Agents
In
src/main/java/hello/cluebackend/domain/timetable/presentation/TimetableController.java
around lines 25-28 and 37-40, the request parameters lack validation. Add
validation annotations such as @Valid and appropriate constraints (e.g.,
@NotBlank, @NotNull) to the method parameters or wrap them in a validated DTO
class. Apply the same validation pattern to both the getTodayTimetable and the
/weekly endpoint methods to ensure request parameters are properly validated at
the controller level.
| return timetableService.getTodayTimetable(request) | ||
| .map(ResponseEntity::ok) | ||
| .defaultIfEmpty(ResponseEntity.noContent().build()) | ||
| .onErrorResume(e -> Mono.just(ResponseEntity.internalServerError().build())); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
에러 처리 개선 필요
현재 모든 에러를 500 Internal Server Error로 처리하고 있습니다. 에러 타입에 따라 적절한 HTTP 상태 코드를 반환해야 합니다.
- .onErrorResume(e -> Mono.just(ResponseEntity.internalServerError().build()));
+ .onErrorResume(e -> {
+ if (e instanceof IllegalArgumentException) {
+ return Mono.just(ResponseEntity.badRequest().build());
+ }
+ return Mono.just(ResponseEntity.internalServerError().build());
+ });Also applies to: 45-45
🤖 Prompt for AI Agents
In
src/main/java/hello/cluebackend/domain/timetable/presentation/TimetableController.java
at lines 33 and 45, the error handling currently returns a generic 500 Internal
Server Error for all exceptions. Modify the onErrorResume handlers to inspect
the exception type and return appropriate HTTP status codes based on the error,
such as 404 for not found or 400 for bad requests, instead of always returning
500.
| @GetMapping("/test") | ||
| public String test(){ | ||
| return "Test"; | ||
| } |
There was a problem hiding this comment.
테스트 엔드포인트 제거 필요
프로덕션 코드에 테스트 목적의 엔드포인트가 포함되어 있습니다. 보안상 위험하므로 제거해야 합니다.
- @GetMapping("/test")
- public String test(){
- return "Test";
- }📝 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.
| @GetMapping("/test") | |
| public String test(){ | |
| return "Test"; | |
| } |
🤖 Prompt for AI Agents
In
src/main/java/hello/cluebackend/domain/timetable/presentation/TimetableController.java
around lines 19 to 22, there is a test endpoint mapped to "/test" returning a
simple string. This test endpoint should be removed entirely from the production
code to eliminate potential security risks. Delete the entire method annotated
with @GetMapping("/test") including its signature and body.
+시간표 API Webclient로 변경
Summary by CodeRabbit
NEIS_API_KEY가 추가되었습니다.