Skip to content

[Feat] 공고 저장, 저장취소 API 구현#22

Merged
jihoonkim501 merged 6 commits into
developfrom
feat/#20
Jul 21, 2026
Merged

[Feat] 공고 저장, 저장취소 API 구현#22
jihoonkim501 merged 6 commits into
developfrom
feat/#20

Conversation

@jihoonkim501

@jihoonkim501 jihoonkim501 commented Jul 19, 2026

Copy link
Copy Markdown
Member

#️⃣ 관련 이슈

closed #20

PR 유형

어떤 변경 사항이 있나요?

  • 새로운 기능 추가
  • 버그 수정
  • 코드 리팩토링
  • 주석 추가 및 수정
  • 문서 수정
  • 테스트 추가, 테스트 리팩토링
  • 빌드 부분 혹은 패키지 매니저 수정
  • 파일 혹은 폴더명 수정
  • 파일 혹은 폴더 삭제

PR Checklist

PR이 다음 요구 사항을 충족하는지 확인하세요.

  • 커밋 메시지 컨벤션에 맞게 작성했습니다.
  • 변경 사항에 대한 테스트를 했습니다.(버그 수정/기능에 대한 테스트).

🧩 작업 내용

📸 스크린샷(선택)

  • 저장
image
  • 저장 (저장한 항목 재저장시)
image
  • 취소
image
  • 취소 (저장하지않은 | 이미 취소한 공고 취소시)
image

📣 To Reviewers

  • Reviewers : 팀 선택
  • Labels : 작업 유형, 자기 자신

Summary by CodeRabbit

  • 새로운 기능
    • 채용 공고를 저장하고 저장을 취소할 수 있는 기능이 추가되었습니다.
  • 개선 사항
    • 저장/취소 요청의 성공 응답과 상태 안내(중복 저장, 미저장 상태)가 확장되었습니다.
    • 채용 공고의 상세 텍스트와 태그 정보(위치, 주요 업무, 요건, 우대, 기술 태그, 추정 여부)가 반영됩니다.
    • 저장 기록의 시간 기준이 더 일관되게 관리됩니다.

@jihoonkim501 jihoonkim501 self-assigned this Jul 19, 2026
@jihoonkim501 jihoonkim501 added the ✨ Feat 새로운 기능 추가 label Jul 19, 2026
@jihoonkim501 jihoonkim501 linked an issue Jul 19, 2026 that may be closed by this pull request
2 tasks
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a1ccb9b4-f682-4c96-949d-ca19c9fc4afc

📥 Commits

Reviewing files that changed from the base of the PR and between 5f93567 and 5e995ed.

📒 Files selected for processing (3)
  • src/main/java/com/leets7th/job_is_be/domain/job/service/JobService.java
  • src/main/java/com/leets7th/job_is_be/global/status/ErrorStatus.java
  • src/main/java/com/leets7th/job_is_be/global/status/SuccessStatus.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/com/leets7th/job_is_be/domain/job/service/JobService.java
  • src/main/java/com/leets7th/job_is_be/global/status/SuccessStatus.java
  • src/main/java/com/leets7th/job_is_be/global/status/ErrorStatus.java

📝 Walkthrough

Walkthrough

Job 공고의 텍스트·태그 매핑을 확장하고, 공고 저장·저장 취소 서비스와 REST API를 추가했습니다. SavedJob의 저장 시간은 OffsetDateTime으로 변경되었으며, JPA 감사 시간 공급자와 관련 상태 코드가 추가되었습니다.

Changes

Job 공고 데이터 매핑

Layer / File(s) Summary
Job 필드 및 변환 흐름 확장
src/main/java/com/leets7th/job_is_be/domain/job/entity/Job.java, src/main/java/com/leets7th/job_is_be/domain/job/converter/JobPostingConverter.java
공고 텍스트·태그 필드를 Job에 추가하고 생성 및 동기화 변환에 매핑합니다.

공고 저장 기능

Layer / File(s) Summary
저장 상태 저장소 및 서비스
src/main/java/com/leets7th/job_is_be/domain/job/entity/SavedJob.java, src/main/java/com/leets7th/job_is_be/domain/job/repository/SavedJobRepository.java, src/main/java/com/leets7th/job_is_be/domain/job/service/JobService.java
저장 시간을 OffsetDateTime으로 변경하고 저장 여부 확인, 저장, 삭제 로직을 트랜잭션 서비스로 구현합니다.
저장·저장 취소 API와 상태 코드
src/main/java/com/leets7th/job_is_be/domain/job/controller/JobController.java, src/main/java/com/leets7th/job_is_be/global/status/ErrorStatus.java, src/main/java/com/leets7th/job_is_be/global/status/SuccessStatus.java
POSTDELETE /api/jobs/{jobId}/save 엔드포인트와 관련 성공·실패 상태를 추가합니다.

시간 감사 설정

Layer / File(s) Summary
OffsetDateTime 감사 공급자
src/main/java/com/leets7th/job_is_be/global/config/JpaConfig.java, src/main/java/com/leets7th/job_is_be/domain/personality/entity/PersonalityTestAnswer.java, src/main/java/com/leets7th/job_is_be/domain/user/entity/*.java
JPA 감사 시간을 OffsetDateTime.now()로 공급하도록 설정하고 기존 시간 필드의 타입 통일 TODO를 기록합니다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested reviewers: yukyoungs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning 저장/취소 API와 무관한 Job 필드 확장, JPA 시간 설정 변경, 다수 엔티티의 TODO 주석 추가가 포함됩니다. 저장/취소 기능에 필요한 변경만 유지하고, 시간 타입 통일과 추가 필드 매핑은 별도 이슈나 PR로 분리하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 공고 저장/저장취소 API 구현이라는 주요 변경을 정확하고 간결하게 요약합니다.
Linked Issues check ✅ Passed 직접 이슈 #20의 공고 저장 및 저장 취소 API 구현 요구사항이 컨트롤러, 서비스, 저장소 추가로 충족됩니다.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/main/java/com/leets7th/job_is_be/global/config/JpaConfig.java (1)

15-17: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

OffsetDateTime.now()가 시스템 기본 timezone을 사용합니다.

배포 환경(예: UTC 컨테이너 vs KST 서버)에 따라 저장되는 offset이 달라질 수 있습니다. 한국 서비스라면 고정 offset 사용을 권장합니다.

♻️ 제안: 고정 timezone offset 사용
     `@Bean`
     public DateTimeProvider offsetDateTimeProvider() {
-        return () -> Optional.of(OffsetDateTime.now());
+        return () -> Optional.of(OffsetDateTime.now(ZoneOffset.ofHours(9)));
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/leets7th/job_is_be/global/config/JpaConfig.java` around
lines 15 - 17, Update the offsetDateTimeProvider bean to create the current
OffsetDateTime with the service’s fixed Korean timezone offset instead of using
the system default timezone; preserve the existing Optional-wrapped
DateTimeProvider contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/leets7th/job_is_be/domain/job/controller/JobController.java`:
- Around line 17-35: JobController의 saveJob 및 unsaveJob에서 클라이언트가 전달한
`@RequestParam` userId를 신뢰하지 않도록 수정하세요. 인증 설정 전까지는 세션/토큰에서 검증된 사용자 ID를 사용하고 요청 값과
불일치하면 거부하거나, 검증 수단이 없으면 두 엔드포인트를 비활성화해 다른 사용자의 저장 상태를 변경할 수 없게 하세요.

In `@src/main/java/com/leets7th/job_is_be/domain/job/service/JobService.java`:
- Around line 26-41: Update JobService.saveJob to handle the database
unique-constraint race between existsByUserIdAndJobId and
savedJobRepository.save: catch DataIntegrityViolationException from the save
operation and translate it to GeneralException(ErrorStatus.JOB_ALREADY_SAVED),
preserving the existing pre-check and normal save behavior.

---

Nitpick comments:
In `@src/main/java/com/leets7th/job_is_be/global/config/JpaConfig.java`:
- Around line 15-17: Update the offsetDateTimeProvider bean to create the
current OffsetDateTime with the service’s fixed Korean timezone offset instead
of using the system default timezone; preserve the existing Optional-wrapped
DateTimeProvider contract.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4d2bf790-b046-4926-b404-c27c3f86bcb6

📥 Commits

Reviewing files that changed from the base of the PR and between 83c2a30 and 5f93567.

📒 Files selected for processing (14)
  • src/main/java/com/leets7th/job_is_be/domain/job/controller/JobController.java
  • src/main/java/com/leets7th/job_is_be/domain/job/converter/JobPostingConverter.java
  • src/main/java/com/leets7th/job_is_be/domain/job/entity/Job.java
  • src/main/java/com/leets7th/job_is_be/domain/job/entity/SavedJob.java
  • src/main/java/com/leets7th/job_is_be/domain/job/repository/SavedJobRepository.java
  • src/main/java/com/leets7th/job_is_be/domain/job/service/JobService.java
  • src/main/java/com/leets7th/job_is_be/domain/personality/entity/PersonalityTestAnswer.java
  • src/main/java/com/leets7th/job_is_be/domain/user/entity/Resume.java
  • src/main/java/com/leets7th/job_is_be/domain/user/entity/User.java
  • src/main/java/com/leets7th/job_is_be/domain/user/entity/UserProfile.java
  • src/main/java/com/leets7th/job_is_be/domain/user/entity/UserWithdrawal.java
  • src/main/java/com/leets7th/job_is_be/global/config/JpaConfig.java
  • src/main/java/com/leets7th/job_is_be/global/status/ErrorStatus.java
  • src/main/java/com/leets7th/job_is_be/global/status/SuccessStatus.java

Comment on lines +17 to +35
// TODO 인증 세팅 후 @AuthenticationPrincipal로 교체
@PostMapping("/{jobId}/save")
public ResponseEntity<ApiResponse<Void>> saveJob(
@PathVariable Long jobId,
@RequestParam Long userId
) {
jobService.saveJob(userId, jobId);
return ApiResponse.success(SuccessStatus.JOB_SAVE_SUCCESS);
}

// TODO 인증 세팅 후 @AuthenticationPrincipal로 교체
@DeleteMapping("/{jobId}/save")
public ResponseEntity<ApiResponse<Void>> unsaveJob(
@PathVariable Long jobId,
@RequestParam Long userId
) {
jobService.unsaveJob(userId, jobId);
return ApiResponse.success(SuccessStatus.JOB_UNSAVE_SUCCESS);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

userId@RequestParam으로 수신하면 IDOR 취약점이 발생합니다.

TODO로 인증 적용 후 @AuthenticationPrincipal로 교체 예정인 것은 확인했습니다. 하지만 현재 상태로 배포하면 임의의 userId를 쿼리 파라미터로 전달하여 다른 사용자의 공고 저장/취소를 조작할 수 있습니다. 인증 설정 전까지는 최소한 컨트롤러 단에서 세션/토큰 기반 사용자 검증을 수행하거나, 해당 엔드포인트를 비활성화하는 것을 권장합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/leets7th/job_is_be/domain/job/controller/JobController.java`
around lines 17 - 35, JobController의 saveJob 및 unsaveJob에서 클라이언트가 전달한
`@RequestParam` userId를 신뢰하지 않도록 수정하세요. 인증 설정 전까지는 세션/토큰에서 검증된 사용자 ID를 사용하고 요청 값과
불일치하면 거부하거나, 검증 수단이 없으면 두 엔드포인트를 비활성화해 다른 사용자의 저장 상태를 변경할 수 없게 하세요.

@yeonjuncho yeonjuncho left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

승인합니다

@jihoonkim501
jihoonkim501 merged commit 457548b into develop Jul 21, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feat 새로운 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 공고 저장, 저장취소 API 구현

2 participants