Skip to content

[✨Feat] 자기소개서 기본 정보 저장 API 구현#33

Merged
yong203 merged 1 commit into
devfrom
feat/#32
Jun 18, 2026
Merged

[✨Feat] 자기소개서 기본 정보 저장 API 구현#33
yong203 merged 1 commit into
devfrom
feat/#32

Conversation

@yong203

@yong203 yong203 commented Jun 18, 2026

Copy link
Copy Markdown
Member

작업 내용

  • API-009 PUT /cover-letters/{coverLetterId}/basic-info 자기소개서 등록 step1 기본 정보 저장 API를 구현했습니다.
  • 현재 사용자 소유이면서 삭제되지 않은 DRAFT 자기소개서만 수정할 수 있도록 검증을 추가했습니다.
  • title, companyName, positionTitle, jobPostingUrl을 trim 후 저장하고, blank jobPostingUrlnull로 저장합니다.
  • trim 후 validation 실패 시 VALIDATION_ERROR와 field별 details를 반환하도록 공통 비즈니스 예외 details 처리를 확장했습니다.
  • DRAFT가 아닌 자기소개서는 COVER_LETTER_NOT_DRAFT로 응답하도록 에러 코드를 추가했습니다.
  • controller/service/global exception 테스트를 추가했습니다.

스크린샷 (@PutMapping("/cover-letters/{coverLetterId}/basic-info") test)

1. 테스트 이전 초안만 작성된 cover Letter 가 db 에 저장된 모습

 111

2. @PutMapping("/cover-letters/{coverLetterId}/basic-info" 테스트 실행

222

3. 테스트 이후 basic info 가 채워진 cover Letter 모습 확인

333

4. 최대 허용 길이 이상의 값을 넣고 api 요청 시 에러 응답 확인 @PutMapping("/cover-letters/{coverLetterId}/basic-info"

스크린샷 2026-06-18 오후 3 16 47

관련 이슈

문서 반영

  • 반영한 문서:
    • docs/api/README.md
    • docs/api/cover-letters.md
    • docs/status.md

확인 결과

  • ./gradlew test
  • ./gradlew check
  • 기타: git diff --check
  • 실행하지 못함. 이유:

Summary by CodeRabbit

  • 새 기능

    • 자기소개서 기본 정보(제목, 회사명, 직무명, 채용공고 URL) 저장 API 엔드포인트 추가
    • 초안 상태의 자기소개서에만 기본 정보 수정 가능
  • 개선 사항

    • 유효성 검증 오류 발생 시 필드별 상세 오류 정보 함께 제공
  • 테스트

    • API 엔드포인트, 서비스 로직, 예외 처리에 대한 포괄적 테스트 추가

@yong203 yong203 linked an issue Jun 18, 2026 that may be closed by this pull request
14 tasks
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

PUT /cover-letters/{coverLetterId}/basic-info 엔드포인트(API-009)를 신규 구현한다. BusinessExceptiondetails 페이로드를 추가하여 필드별 검증 오류 응답을 지원하고, COVER_LETTER_NOT_DRAFT 에러 코드를 신규 등록한다. 서비스, 컨트롤러, DTO, 테스트, API 문서가 함께 추가된다.

Changes

자기소개서 기본 정보 저장 API (API-009)

Layer / File(s) Summary
BusinessException details 페이로드 및 에러 코드 확장
src/main/java/com/daon/rewrite/global/exception/BusinessException.java, src/main/java/com/daon/rewrite/global/exception/ErrorCode.java, src/main/java/com/daon/rewrite/global/exception/GlobalExceptionHandler.java
BusinessExceptionList<ErrorResponse.ErrorDetail> details 필드와 오버로드 생성자를 추가하고, COVER_LETTER_NOT_DRAFT(409 Conflict) 에러 코드를 신규 등록하며, GlobalExceptionHandler에서 e.getDetails()를 에러 응답에 포함하도록 변경한다.
saveBasicInfo 서비스 로직 및 검증
src/main/java/com/daon/rewrite/coverletter/service/CoverLetterService.java
saveBasicInfo 트랜잭션 메서드를 구현한다. 소유자/삭제 여부 조회 후 NOT_FOUND, DRAFT 상태 미충족 시 COVER_LETTER_NOT_DRAFT를 던지며, validateAndNormalizeBasicInfo에서 필수 3개 필드와 선택 jobPostingUrl을 strip·코드포인트 길이·절대 URL 형식으로 검증·정규화하고, 오류 누적 시 VALIDATION_ERRORErrorDetail 목록을 던진다.
요청/응답 DTO 및 컨트롤러 엔드포인트
src/main/java/com/daon/rewrite/coverletter/dto/SaveBasicInfoRequest.java, src/main/java/com/daon/rewrite/coverletter/dto/SaveBasicInfoResponse.java, src/main/java/com/daon/rewrite/coverletter/controller/CoverLetterController.java
SaveBasicInfoRequest·SaveBasicInfoResponse record DTO를 신규 추가한다. SaveBasicInfoResponse.from()updatedAt을 Asia/Seoul 기준 LocalDateTime으로 변환한다. CoverLetterControllerPUT /cover-letters/{coverLetterId}/basic-info 엔드포인트를 추가하여 서비스 호출 결과를 반환한다.
서비스/컨트롤러/핸들러 테스트 추가
src/test/java/com/daon/rewrite/coverletter/service/CoverLetterServiceTest.java, src/test/java/com/daon/rewrite/coverletter/controller/CoverLetterControllerTest.java, src/test/java/com/daon/rewrite/global/exception/GlobalExceptionHandlerTest.java
CoverLetterServiceTest에 saveBasicInfo 관련 테스트 6개(정상 저장·trim, blank URL→null, NOT_FOUND, COVER_LETTER_NOT_DRAFT, VALIDATION_ERROR details)를 추가한다. CoverLetterControllerTest에 PUT 시나리오 4개, GlobalExceptionHandlerTest에 details 포함 응답 및 충돌 응답 테스트 2개를 추가한다.
API 문서 및 상태 갱신
docs/api/README.md, docs/api/cover-letters.md, docs/status.md
API-009 상태를 PlannedImplemented로 갱신하고, COVER_LETTER_NOT_DRAFT 에러 예시에 details: []를 추가한다. docs/status.md에서 REQ-004를 In Progress로 변경하고 이슈/PR 링크와 검증 커맨드를 채우며, Next Issue Slice Candidates에 자기소개서 상세 조회(REQ-003, API-012) 후보를 추가한다.

Possibly related PRs

  • Rewrite-Team/Rewrite-BE#19: 커버레터 초안 생성(POST /cover-letters) 흐름과 초기 CoverLetterService/CoverLetter 모델을 도입하며, 본 PR은 동일한 CoverLetterServicesaveBasicInfo(...) 메서드를 추가 구현한다.
  • Rewrite-Team/Rewrite-BE#27: DB/JPA 전환으로 CoverLetterupdatedAt(Instant 기반) 및 CoverLetterService 계층 구조를 변경하였으며, 본 PR의 saveBasicInfo 구현이 해당 persistence 변경 위에서 동작한다.

Suggested labels

✨ Feature

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
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 (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 구현된 주요 변경사항을 명확하게 설명하고 있습니다: 자기소개서 기본 정보 저장 API 구현(API-009).
Linked Issues check ✅ Passed 모든 완료 기준이 충족되었습니다: API 구현, 유효성 검사, 에러 처리, 테스트 추가, 문서 갱신이 모두 이루어졌습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 이슈 #32의 범위 내에 있습니다: API-009 구현, 관련 DTO/서비스/테스트/문서 업데이트만 포함됩니다.
Description check ✅ Passed Pull request description is comprehensive and well-structured, following the template with all required sections properly filled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@yong203 yong203 marked this pull request as ready for review June 18, 2026 06:07
@yong203 yong203 self-assigned this Jun 18, 2026
@yong203 yong203 added the ✨ Feature 새로운 기능 구현 label Jun 18, 2026
@codecov

codecov Bot commented Jun 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.05797% with 11 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...ewrite/coverletter/service/CoverLetterService.java 76.08% 5 Missing and 6 partials ⚠️

📢 Thoughts on this report? Let us know!

@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.

🧹 Nitpick comments (3)
docs/status.md (1)

24-24: ⚡ Quick win

현재 PR을 Related Issue/PR에 추가하세요.

REQ-004의 Related Issue/PR 필드에 현재 PR 번호가 누락되어 있습니다. 이슈 #32만 있고 해당 구현 PR 링크가 없습니다.

문서 업데이트 규칙("issue 또는 PR이 생성되면 Related Issue/PR에 번호나 링크를 기록한다")에 따라 현재 PR도 추가해야 합니다.

📝 수정 제안
-| REQ-004 | 자기소개서 등록 step 저장 | In Progress | High | API-009, API-010, API-011 | [`#32`](https://github.com/Rewrite-Team/Rewrite-BE/issues/32) | `CoverLetterServiceTest`, `CoverLetterControllerTest`, `GlobalExceptionHandlerTest`, `./gradlew test`, `./gradlew check` | API-009 등록 step1 기본 정보 저장 계약 구현됨. API-010/API-011은 후속 slice 후보 기준으로 분리 진행 |
+| REQ-004 | 자기소개서 등록 step 저장 | In Progress | High | API-009, API-010, API-011 | [`#32`](https://github.com/Rewrite-Team/Rewrite-BE/issues/32), [PR #<현재PR번호>](https://github.com/Rewrite-Team/Rewrite-BE/pull/<현재PR번호>) | `CoverLetterServiceTest`, `CoverLetterControllerTest`, `GlobalExceptionHandlerTest`, `./gradlew test`, `./gradlew check` | API-009 등록 step1 기본 정보 저장 계약 구현됨. API-010/API-011은 후속 slice 후보 기준으로 분리 진행 |
🤖 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 `@docs/status.md` at line 24, In the status table row for REQ-004 (자기소개서 등록
step 저장), add the current PR number to the Related Issue/PR column following the
existing markdown link format. The field currently only contains the issue `#32`
link, but according to the documentation update rules, both the related issue
and the implementation PR number should be included. Add the PR link in the same
format as the existing issue link to complete the Related Issue/PR documentation
for REQ-004.
src/main/java/com/daon/rewrite/coverletter/dto/SaveBasicInfoResponse.java (1)

18-18: 공통 상수로 API_ZONE을 추출하세요.

API_ZONE = ZoneId.of("Asia/Seoul") 상수가 4개 DTO에서 중복되고 있습니다:

  • CreateCoverLetterResponse.java:14
  • SaveBasicInfoResponse.java:18
  • DeleteCoverLetterResponse.java:12
  • CoverLetterListItemResponse.java:18

유지보수성을 위해 coverletter 패키지 내 공통 상수 클래스 또는 유틸리티에서 한 번만 정의하고 참조하세요.

🤖 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/daon/rewrite/coverletter/dto/SaveBasicInfoResponse.java` at
line 18, The API_ZONE constant is duplicated across four DTO classes
(SaveBasicInfoResponse, CreateCoverLetterResponse, DeleteCoverLetterResponse,
and CoverLetterListItemResponse) in the coverletter package. Create a new
constants class in the coverletter package (such as CoverLetterConstants) and
define the API_ZONE constant there once with the value ZoneId.of("Asia/Seoul").
Then remove the private static final API_ZONE field from all four DTO classes
and replace any references to it with the qualified reference to the constants
class.
src/main/java/com/daon/rewrite/coverletter/service/CoverLetterService.java (1)

226-233: 중복된 URI 스킴 검증 로직 제거.

isAbsoluteUrl 메서드의 getScheme() != null && !uri.getScheme().isBlank() 검증은 불필요합니다. Java URI 파서는 빈 스킴(://, ://)을 허용하지 않고 URISyntaxException을 발생시키며, URI.isAbsolute() 호출 시점에서 이미 스킴이 존재함을 보장합니다. 따라서 다음과 같이 단순화할 수 있습니다:

private boolean isAbsoluteUrl(String value) {
    try {
        return new URI(value).isAbsolute();
    } catch (URISyntaxException e) {
        return false;
    }
}
🤖 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/daon/rewrite/coverletter/service/CoverLetterService.java`
around lines 226 - 233, The isAbsoluteUrl method contains redundant URI scheme
validation checks that are unnecessary. Since the Java URI parser already throws
URISyntaxException for invalid URIs with empty or missing schemes, and
URI.isAbsolute() inherently guarantees the existence of a valid scheme, the
additional checks for getScheme() != null and !uri.getScheme().isBlank() are
duplicate validations. Simplify the isAbsoluteUrl method to directly return the
result of new URI(value).isAbsolute() within the try block, removing the
redundant scheme null and blank checks while keeping the URISyntaxException
catch block to return false for invalid URIs.
🤖 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.

Nitpick comments:
In `@docs/status.md`:
- Line 24: In the status table row for REQ-004 (자기소개서 등록 step 저장), add the
current PR number to the Related Issue/PR column following the existing markdown
link format. The field currently only contains the issue `#32` link, but according
to the documentation update rules, both the related issue and the implementation
PR number should be included. Add the PR link in the same format as the existing
issue link to complete the Related Issue/PR documentation for REQ-004.

In `@src/main/java/com/daon/rewrite/coverletter/dto/SaveBasicInfoResponse.java`:
- Line 18: The API_ZONE constant is duplicated across four DTO classes
(SaveBasicInfoResponse, CreateCoverLetterResponse, DeleteCoverLetterResponse,
and CoverLetterListItemResponse) in the coverletter package. Create a new
constants class in the coverletter package (such as CoverLetterConstants) and
define the API_ZONE constant there once with the value ZoneId.of("Asia/Seoul").
Then remove the private static final API_ZONE field from all four DTO classes
and replace any references to it with the qualified reference to the constants
class.

In `@src/main/java/com/daon/rewrite/coverletter/service/CoverLetterService.java`:
- Around line 226-233: The isAbsoluteUrl method contains redundant URI scheme
validation checks that are unnecessary. Since the Java URI parser already throws
URISyntaxException for invalid URIs with empty or missing schemes, and
URI.isAbsolute() inherently guarantees the existence of a valid scheme, the
additional checks for getScheme() != null and !uri.getScheme().isBlank() are
duplicate validations. Simplify the isAbsoluteUrl method to directly return the
result of new URI(value).isAbsolute() within the try block, removing the
redundant scheme null and blank checks while keeping the URISyntaxException
catch block to return false for invalid URIs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f5d89613-753b-40a2-abe6-5e4dc25ce965

📥 Commits

Reviewing files that changed from the base of the PR and between 3b3a2e3 and 41698e8.

📒 Files selected for processing (13)
  • docs/api/README.md
  • docs/api/cover-letters.md
  • docs/status.md
  • src/main/java/com/daon/rewrite/coverletter/controller/CoverLetterController.java
  • src/main/java/com/daon/rewrite/coverletter/dto/SaveBasicInfoRequest.java
  • src/main/java/com/daon/rewrite/coverletter/dto/SaveBasicInfoResponse.java
  • src/main/java/com/daon/rewrite/coverletter/service/CoverLetterService.java
  • src/main/java/com/daon/rewrite/global/exception/BusinessException.java
  • src/main/java/com/daon/rewrite/global/exception/ErrorCode.java
  • src/main/java/com/daon/rewrite/global/exception/GlobalExceptionHandler.java
  • src/test/java/com/daon/rewrite/coverletter/controller/CoverLetterControllerTest.java
  • src/test/java/com/daon/rewrite/coverletter/service/CoverLetterServiceTest.java
  • src/test/java/com/daon/rewrite/global/exception/GlobalExceptionHandlerTest.java

@yong203 yong203 merged commit e35da87 into dev Jun 18, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 새로운 기능 구현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[✨Feat] 자기소개서 기본 정보 저장 API 구현

1 participant