2025/08/28 1차배포#14
Conversation
Walkthrough새로운 잡 관리 기능이 추가됨: JobController와 JobSearchController REST 엔드포인트, JobService 인터페이스, JobServiceImpl 구현. CRUD와 전공/상태 기반 조회 제공. JobSearchController에 동일 경로(GET /jobs/search/) 중복 매핑이 존재. ServiceImpl의 전공/상태 조회는 자기 재귀 호출로 구현됨. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Client
participant C as JobController (/jobs)
participant S as JobService
participant R as JobRepository
Client->>C: GET /jobs/{id}
C->>S: findJobById(id)
S->>R: findById(id)
R-->>S: Optional<Job>
S-->>C: Optional<Job>
C-->>Client: 200 Optional<Job>
Client->>C: POST /jobs
C->>S: postJob(job)
S->>R: save(job)
R-->>S: void
S-->>C: void
C-->>Client: 201
Client->>C: PATCH /jobs/{id}
C->>S: editJob(id, requestJob)
S->>R: findById(id)
alt 존재함
S->>R: save(updatedJob)
R-->>S: void
S-->>C: void
C-->>Client: 200
else 없음
S-->>C: throws IllegalArgumentException("Job not found")
C-->>Client: 404/400 (예외 매핑에 따름)
end
Client->>C: DELETE /jobs/{id}
C->>S: deleteJob(id)
S->>R: deleteById(id)
R-->>S: void
S-->>C: void
C-->>Client: 204
sequenceDiagram
autonumber
actor Client
participant SC as JobSearchController (/jobs/search)
participant S as JobService
participant R as JobRepository
Note over SC: 두 메서드가 모두 GET "/"로 매핑됨 (경로 충돌 가능)
Client->>SC: GET /jobs/search?major=...
SC->>S: findJobsByMajor(major)
Note over S: 현재 구현은 자기 재귀 호출
S-->>S: findJobsByMajor(major)
Note over S: 실제 의도는 R.queryByMajor(major)
Client->>SC: GET /jobs/search?status=...
SC->>S: findJobsByStatus(status)
Note over S: 현재 구현은 자기 재귀 호출
S-->>S: findJobsByStatus(status)
Note over S: 실제 의도는 R.queryByStatus(status)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (4)
src/main/java/com/adhd/jova_v2/global/jobs/service/JobService.java (1)
15-17: 변경 메서드의 반환값 정의
postJob,editJob이void이면 클라이언트가 생성/수정 결과(식별자, 최신 상태)를 알 수 없습니다.Job또는 식별자 반환을 권장합니다.- void editJob(long id, Job requestJob); - void postJob(Job job); + Job editJob(long id, Job requestJob); + Job postJob(Job job);src/main/java/com/adhd/jova_v2/global/jobs/controller/JobSearchController.java (1)
27-27: 메서드명 오타/일관성
getJobsfilteredByStatus→getJobsFilteredByStatus로 카멜케이스 일관성 유지.- public List<Job> getJobsfilteredByStatus (@RequestParam("status") JobStatus status) { + public List<Job> getJobsFilteredByStatus (@RequestParam("status") JobStatus status) {src/main/java/com/adhd/jova_v2/global/jobs/controller/JobController.java (1)
18-21: Optional을 그대로 응답으로 반환 지양존재하지 않는 경우 404를 내려주는 것이 REST 관례에 맞습니다.
ResponseEntity로 감싸는 리팩터를 권장합니다.- public Optional<Job> findSingleJobById (@PathVariable long id) { - return jobService.findJobById(id); + public org.springframework.http.ResponseEntity<Job> findSingleJobById (@PathVariable long id) { + return jobService.findJobById(id) + .map(org.springframework.http.ResponseEntity::ok) + .orElseGet(() -> org.springframework.http.ResponseEntity.notFound().build()); }필요시 import:
import org.springframework.http.ResponseEntity;src/main/java/com/adhd/jova_v2/global/jobs/service/impl/JobServiceImpl.java (1)
15-17: 트랜잭션 기본값 설정 제안읽기 위주 서비스라면 클래스에
@Transactional(Transactional.TxType.SUPPORTS)또는 Spring@Transactional(readOnly = true)를 두고, 쓰기 메서드만 오버라이드하는 방식이 안전합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
src/main/java/com/adhd/jova_v2/global/jobs/controller/JobController.java(1 hunks)src/main/java/com/adhd/jova_v2/global/jobs/controller/JobSearchController.java(1 hunks)src/main/java/com/adhd/jova_v2/global/jobs/service/JobService.java(1 hunks)src/main/java/com/adhd/jova_v2/global/jobs/service/impl/JobServiceImpl.java(1 hunks)
| @PostMapping("/") | ||
| public void postSingleJob (Job job) { | ||
| jobService.postJob(job); | ||
| } |
There was a problem hiding this comment.
POST 본문 역직렬화 누락
@RequestBody가 없어 JSON 본문 바인딩이 되지 않습니다.
- public void postSingleJob (Job job) {
+ public void postSingleJob (@RequestBody Job job) {
jobService.postJob(job);
}🤖 Prompt for AI Agents
In src/main/java/com/adhd/jova_v2/global/jobs/controller/JobController.java
around lines 28 to 31, the POST handler parameter is missing @RequestBody so the
JSON body won't be deserialized; add the @RequestBody annotation to the Job
parameter (e.g., public void postSingleJob(@RequestBody Job job)) and ensure the
RequestBody import (org.springframework.web.bind.annotation.RequestBody) is
present and that the endpoint accepts application/json.
| @PatchMapping("/{id}") | ||
| public void editSingleJob (@PathVariable long id, Job requestJob) { | ||
| jobService.editJob(id, requestJob); | ||
| } |
There was a problem hiding this comment.
PATCH 본문 역직렬화 누락
requestJob에 @RequestBody 추가 필요.
- public void editSingleJob (@PathVariable long id, Job requestJob) {
+ public void editSingleJob (@PathVariable long id, @RequestBody Job requestJob) {
jobService.editJob(id, requestJob);
}📝 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.
| @PatchMapping("/{id}") | |
| public void editSingleJob (@PathVariable long id, Job requestJob) { | |
| jobService.editJob(id, requestJob); | |
| } | |
| @PatchMapping("/{id}") | |
| public void editSingleJob(@PathVariable long id, @RequestBody Job requestJob) { | |
| jobService.editJob(id, requestJob); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/com/adhd/jova_v2/global/jobs/controller/JobController.java
around lines 33 to 36, the PATCH handler is missing request body deserialization
for the Job parameter; add the @RequestBody annotation to the requestJob
parameter so Spring will deserialize the HTTP PATCH body into the Job object
(e.g., change signature to include @RequestBody Job requestJob) and ensure
imports are present.
| @GetMapping("/") | ||
| public List<Job> getJobsFilteredByMajor (@RequestParam("major") Major major) { | ||
| return jobService.findJobsByMajor(major); | ||
| } | ||
|
|
||
| @GetMapping("/") | ||
| public List<Job> getJobsfilteredByStatus (@RequestParam("status") JobStatus status) { | ||
| return jobService.findJobsByStatus(status); | ||
| } |
There was a problem hiding this comment.
중복 매핑 충돌: 두 개의 GET "/"
@GetMapping("/")가 2개라서 런타임에 Ambiguous mapping이 발생합니다. 경로를 분리하세요.
- @GetMapping("/")
- public List<Job> getJobsFilteredByMajor (@RequestParam("major") Major major) {
+ @GetMapping("/by-major")
+ public List<Job> getJobsFilteredByMajor (@RequestParam("major") Major major) {
return jobService.findJobsByMajor(major);
}
- @GetMapping("/")
- public List<Job> getJobsfilteredByStatus (@RequestParam("status") JobStatus status) {
+ @GetMapping("/by-status")
+ public List<Job> getJobsFilteredByStatus (@RequestParam("status") JobStatus status) {
return jobService.findJobsByStatus(status);
}📝 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("/") | |
| public List<Job> getJobsFilteredByMajor (@RequestParam("major") Major major) { | |
| return jobService.findJobsByMajor(major); | |
| } | |
| @GetMapping("/") | |
| public List<Job> getJobsfilteredByStatus (@RequestParam("status") JobStatus status) { | |
| return jobService.findJobsByStatus(status); | |
| } | |
| @GetMapping("/by-major") | |
| public List<Job> getJobsFilteredByMajor(@RequestParam("major") Major major) { | |
| return jobService.findJobsByMajor(major); | |
| } | |
| @GetMapping("/by-status") | |
| public List<Job> getJobsFilteredByStatus(@RequestParam("status") JobStatus status) { | |
| return jobService.findJobsByStatus(status); | |
| } |
🤖 Prompt for AI Agents
In
src/main/java/com/adhd/jova_v2/global/jobs/controller/JobSearchController.java
around lines 21 to 29, there are two methods both annotated with
@GetMapping("/") causing an ambiguous mapping at runtime; update the endpoints
to have distinct paths (for example change one to @GetMapping("/by-major") and
the other to @GetMapping("/by-status")) or consolidate into a single handler
that accepts optional request parameters and delegates to jobService
accordingly, and adjust method names if needed to reflect the new paths.
| public List<Job> getJobsFilteredByMajor (@RequestParam("major") Major major) { | ||
| return jobService.findJobsByMajor(major); | ||
| } | ||
|
|
||
| @GetMapping("/") | ||
| public List<Job> getJobsfilteredByStatus (@RequestParam("status") JobStatus status) { | ||
| return jobService.findJobsByStatus(status); |
There was a problem hiding this comment.
💡 Verification agent
❓ Verification inconclusive
@RequestParam으로 엔티티 바인딩 불가 가능성
Major는 엔티티로 보이며 기본 컨버터가 String -> Major 변환을 제공하지 않습니다. 커스텀 Converter<String, Major>가 없다면 실패합니다. 안전하게 majorId: long을 받고 Service에서 조회하도록 바꾸세요.
커스텀 컨버터 유무 확인 스크립트:
대안 diff:
- public List<Job> getJobsFilteredByMajor (@RequestParam("major") Major major) {
- return jobService.findJobsByMajor(major);
+ public List<Job> getJobsFilteredByMajor (@RequestParam("majorId") long majorId) {
+ return jobService.findJobsByMajorId(majorId);
}🏁 Script executed:
#!/bin/bash
rg -nP 'Converter\s*<\s*String\s*,\s*Major\s*>' -C2
rg -nP '@InitBinder|WebDataBinder' -C2Length of output: 94
@RequestParam 파라미터를 Major → majorId(long)로 변경
커스텀 Converter<String, Major>가 존재하지 않아 String → Major 바인딩이 실패합니다. 엔티티 대신 ID를 받도록 수정하세요.
- public List<Job> getJobsFilteredByMajor(@RequestParam("major") Major major) {
- return jobService.findJobsByMajor(major);
+ public List<Job> getJobsFilteredByMajor(@RequestParam("majorId") long majorId) {
+ return jobService.findJobsByMajorId(majorId);
}📝 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.
| public List<Job> getJobsFilteredByMajor (@RequestParam("major") Major major) { | |
| return jobService.findJobsByMajor(major); | |
| } | |
| @GetMapping("/") | |
| public List<Job> getJobsfilteredByStatus (@RequestParam("status") JobStatus status) { | |
| return jobService.findJobsByStatus(status); | |
| public List<Job> getJobsFilteredByMajor(@RequestParam("majorId") long majorId) { | |
| return jobService.findJobsByMajorId(majorId); | |
| } |
🤖 Prompt for AI Agents
In
src/main/java/com/adhd/jova_v2/global/jobs/controller/JobSearchController.java
around lines 22–28, change the getJobsFilteredByMajor method to accept a
primitive ID instead of a Major entity: replace the @RequestParam("major") Major
major parameter with @RequestParam("majorId") long majorId (or long id) and then
use that ID to query jobs (e.g., call a service method like
jobService.findJobsByMajorId(majorId) or load the Major inside the
controller/service by ID before querying). Ensure the request parameter name
matches the new name and update the service call accordingly so String→Major
binding is no longer required.
| @Transactional | ||
| public void editJob(long id, Job requestJob){ | ||
| Optional<Job> existingJob = jobRepository.findById(id); | ||
| if (existingJob.isPresent()){ | ||
| Job updatedJob = existingJob.get(); | ||
| updatedJob.builder() | ||
| .id(id) | ||
| .title(requestJob.getTitle()) | ||
| .description(requestJob.getDescription()) | ||
| .status(requestJob.getStatus()) | ||
| .build(); | ||
| jobRepository.save(updatedJob); | ||
| } | ||
| else { | ||
| throw new IllegalArgumentException("Job not found"); | ||
| } | ||
| } |
There was a problem hiding this comment.
수정 로직 불변 객체 빌더 오용 및 비효율적 Optional 처리
updatedJob.builder()는 새 객체 빌더를 생성할 뿐, 기존 엔티티를 변경하지 않습니다. 또한 Optional의 분기 대신 orElseThrow가 간결합니다.
@Override
@Transactional
public void editJob(long id, Job requestJob){
- Optional<Job> existingJob = jobRepository.findById(id);
- if (existingJob.isPresent()){
- Job updatedJob = existingJob.get();
- updatedJob.builder()
- .id(id)
- .title(requestJob.getTitle())
- .description(requestJob.getDescription())
- .status(requestJob.getStatus())
- .build();
- jobRepository.save(updatedJob);
- }
- else {
- throw new IllegalArgumentException("Job not found");
- }
+ Job updatedJob = jobRepository.findById(id)
+ .orElseThrow(() -> new IllegalArgumentException("Job not found"));
+ // JPA 엔티티 필드 직접 변경 → 더티체킹으로 플러시
+ updatedJob.setTitle(requestJob.getTitle());
+ updatedJob.setDescription(requestJob.getDescription());
+ updatedJob.setStatus(requestJob.getStatus());
+ // 명시적 저장이 필요 없다면 다음 줄은 생략 가능
+ jobRepository.save(updatedJob);
}📝 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.
| @Transactional | |
| public void editJob(long id, Job requestJob){ | |
| Optional<Job> existingJob = jobRepository.findById(id); | |
| if (existingJob.isPresent()){ | |
| Job updatedJob = existingJob.get(); | |
| updatedJob.builder() | |
| .id(id) | |
| .title(requestJob.getTitle()) | |
| .description(requestJob.getDescription()) | |
| .status(requestJob.getStatus()) | |
| .build(); | |
| jobRepository.save(updatedJob); | |
| } | |
| else { | |
| throw new IllegalArgumentException("Job not found"); | |
| } | |
| } | |
| @Override | |
| @Transactional | |
| public void editJob(long id, Job requestJob) { | |
| Job updatedJob = jobRepository.findById(id) | |
| .orElseThrow(() -> new IllegalArgumentException("Job not found")); | |
| // JPA 엔티티 필드 직접 변경 → 더티체킹으로 플러시 | |
| updatedJob.setTitle(requestJob.getTitle()); | |
| updatedJob.setDescription(requestJob.getDescription()); | |
| updatedJob.setStatus(requestJob.getStatus()); | |
| // 명시적 저장이 필요 없다면 다음 줄은 생략 가능 | |
| jobRepository.save(updatedJob); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/com/adhd/jova_v2/global/jobs/service/impl/JobServiceImpl.java
around lines 32 to 48, the code calls updatedJob.builder() which only creates a
new builder and does not mutate the existing entity, and the Optional handling
is verbose; replace the Optional branching with
jobRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("Job
not found")); then either (preferred for JPA) update the retrieved entity by
setting its mutable fields from requestJob (setTitle, setDescription, setStatus)
and save, or if Job is immutable, construct a new Job via the builder copying
request fields and the original id then save that new instance; ensure
transactional behavior remains and remove the incorrect builder call on the
existing entity.
| public List<Job> findJobsByMajor (Major major){ | ||
| return findJobsByMajor(major); | ||
| } | ||
| @Override | ||
| public List<Job> findJobsByStatus (JobStatus status){ | ||
| return findJobsByStatus(status); | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
무한 재귀로 인한 StackOverflowError
findJobsByMajor/Status가 자기 자신을 호출합니다. 레포지토리로 위임하도록 수정하세요.
@Override
public List<Job> findJobsByMajor (Major major){
- return findJobsByMajor(major);
+ return jobRepository.findByMajor(major);
}
@Override
public List<Job> findJobsByStatus (JobStatus status){
- return findJobsByStatus(status);
+ return jobRepository.findByStatus(status);
}레포지토리 메서드 존재 확인:
🏁 Script executed:
#!/bin/bash
rg -nP '(interface|class)\s+JobRepository\b' -C3
rg -nP '\bfindByMajor\s*\(' -n -C2
rg -nP '\bfindByStatus\s*\(' -n -C2Length of output: 604
무한 재귀 제거 및 리포지토리 메서드 선언 추가
- JobServiceImpl의
findJobsByMajor/Status가 자기 자신을 호출하는 무한 재귀를 제거하고
jobRepository.findByMajor(major)/jobRepository.findByStatus(status)로 위임 - JobRepository 인터페이스에
선언을 추가하여 위임 메서드가 존재하도록 보장
List<Job> findByMajor(Major major); List<Job> findByStatus(JobStatus status);
🤖 Prompt for AI Agents
In src/main/java/com/adhd/jova_v2/global/jobs/service/impl/JobServiceImpl.java
around lines 54 to 60, the methods findJobsByMajor and findJobsByStatus
currently call themselves creating infinite recursion; change each to delegate
to the repository (e.g., jobRepository.findByMajor(major) and
jobRepository.findByStatus(status)). Also add corresponding method signatures to
the JobRepository interface: List<Job> findByMajor(Major major); and List<Job>
findByStatus(JobStatus status); so the service delegation compiles and uses the
repository instead of recursing.
| List<Job> findJobsByMajor(Major major); | ||
| List<Job> findJobsByStatus(JobStatus status); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
서비스 경계에서 엔티티 타입(Major) 직접 노출 지양
컨트롤러/외부 계층에서 Major 엔티티를 직접 주입받는 시그니처는 결합도를 높입니다. majorId(long) 또는 값 객체/DTO로 변경을 권장합니다.
- List<Job> findJobsByMajor(Major major);
+ List<Job> findJobsByMajorId(long majorId);📝 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.
| List<Job> findJobsByMajor(Major major); | |
| List<Job> findJobsByStatus(JobStatus status); | |
| List<Job> findJobsByMajorId(long majorId); | |
| List<Job> findJobsByStatus(JobStatus status); |
🤖 Prompt for AI Agents
In src/main/java/com/adhd/jova_v2/global/jobs/service/JobService.java around
lines 13-14, the service API currently exposes the Major entity in the method
signature (findJobsByMajor(Major major)), which increases coupling; change the
signature to accept a primitive identifier or DTO (e.g., long majorId or
MajorDto) instead, update the service implementation to resolve/use the Major by
id (or map DTO to entity), update any repository call to query by major id
(e.g., findByMajorId) and adjust all callers/controllers to pass the id/DTO
rather than the entity; keep findJobsByStatus(JobStatus status) as-is if
JobStatus is a value enum.
🔗 관련 이슈
✨ 작업 내용
Summary by CodeRabbit