Skip to content

πŸ› Fix: 3μ°¨ μΆ”κ°€ μš”μ²­μ‚¬ν•­μ„ μˆ˜μ •ν•œλ‹€#164

Merged
EunjinWoo merged 6 commits into
developfrom
fix/159-3rd
Dec 22, 2025
Merged

πŸ› Fix: 3μ°¨ μΆ”κ°€ μš”μ²­μ‚¬ν•­μ„ μˆ˜μ •ν•œλ‹€#164
EunjinWoo merged 6 commits into
developfrom
fix/159-3rd

Conversation

@EunjinWoo
Copy link
Copy Markdown
Member

@EunjinWoo EunjinWoo commented Dec 21, 2025

✨ Related Issue


πŸ“Œ Task Details

  • ν•΄λ‹Ή 곡고의 전체 μ§€μ›μž 쑰회 및 검색
  • 문자/메일 ν…œν”Œλ¦Ώ μ‚­μ œ api
  • api/v1/positions/recruitment/{recruitmentId} νŠΉμ • 곡고의 파트 전체 μ‘°νšŒμ—μ„œ 파트 μΆ”κ°€ν•œ 곡고 λΉˆκ°’μœΌλ‘œ μ˜€λŠ” 것 μˆ˜μ • -> ν¬μ§€μ…˜ 말고 organizationRole κ°€μ Έμ˜€λ„λ‘
  • /api/v1/admin/applications/distribute-evaluators μ§€μ›μ„œ 평가 λ‹΄λ‹Ήμž λΆ„λ°°μ—μ„œ νŒŒνŠΈκ°€ κ³΅ν†΅μΌλ•Œλ„ λΆ„λ°° κ°€λŠ₯ν•˜κ²Œ μˆ˜μ •
  • /api/v1/admin/applications/distribute-evaluators/latest/{recruitmentId} 지원평가 λ‹΄λ‹Ήμž λ°°μ • μ΅œμ‹  μš”μ²­ μ‘°νšŒμ—μ„œ μ§€μ›νŒŒνŠΈκ°€ κ³΅ν†΅μΌλ•Œλ„ 쑰회 κ°€λŠ₯ν•˜λ„λ‘ μˆ˜μ •
  • λ©΄μ ‘ 유무 κ°’ μΆ”κ°€ μš”μ²­ : /api/v1/recruitments/my-organizations λ‚΄κ°€ μ†ν•œ 쑰직의 λͺ¨λ“  λ¦¬ν¬λ£¨νŒ… λͺ©λ‘μ‘°νšŒμ—μ„œ 각 곡고별 λ©΄μ ‘ 유무λ₯Ό νŒλ‹¨ ν•  수 μžˆλŠ” κ°’ μΆ”κ°€

πŸ’¬ Review Requirements (Optional)

Summary by CodeRabbit

  • New Features

    • λͺ¨μ§‘곡고별 μ§€μ›μž 이름/이메일 검색 κΈ°λŠ₯ μΆ”κ°€
    • ν…œν”Œλ¦Ώ μ‚­μ œ κΈ°λŠ₯ μΆ”κ°€ (μ‚¬μš©μž κΆŒν•œ 기반)
  • Improvements

    • ν…œν”Œλ¦Ώ μˆ˜μ •Β·μ‚­μ œ μ‹œ μ‚¬μš©μž κΆŒν•œ 검증 κ°•ν™”
    • μ—­ν•  λ―Έμ§€μ •(곡톡) λŒ€μƒ μ§€μ›μ„œ 처리 및 ν‰κ°€μž 배치 κ°œμ„ 
    • 곡고 μš”μ•½μ— λ©΄μ ‘ ν•„μš” μ—¬λΆ€(isInterviewRequired) λ…ΈμΆœ μΆ”κ°€

✏️ Tip: You can customize this high-level summary in your review settings.

@EunjinWoo EunjinWoo requested a review from KJaeKwan December 21, 2025 05:14
@EunjinWoo EunjinWoo self-assigned this Dec 21, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 21, 2025

Walkthrough

κ΄€λ¦¬μžμš© μ§€μ›μž 검색 μ—”λ“œν¬μΈνŠΈμ™€ ApplicantForMailSms DTOκ°€ μΆ”κ°€λ˜μ—ˆκ³ , 쑰직 μ—­ν• (null) 처리 및 QueryDSL 기반 쑰회 ν™•μž₯이 μ—¬λŸ¬ μ €μž₯μ†ŒΒ·μ„œλΉ„μŠ€μ— μ μš©λ˜μ—ˆμŠ΅λ‹ˆλ‹€. ν…œν”Œλ¦Ώ μ—…λ°μ΄νŠΈ/μ‚­μ œμ— μ‚¬μš©μž κΆŒν•œ 검증이 λ„μž…λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

Changes

μ½”ν˜ΈνŠΈ / 파일(λ“€) λ³€κ²½ μš”μ•½
μ§€μ›μž 검색 API
src/main/java/KUSITMS/WITHUS/domain/application/application/controller/AdminApplicationController.java
μ‹ κ·œ GET μ—”λ“œν¬μΈνŠΈ /api/v1/admin/applications/recruitment/{recruitmentId}/search μΆ”κ°€ β€” ν‚€μ›Œλ“œλ‘œ μ§€μ›μž 검색 ν›„ ApplicantForMailSms 리슀트 λ°˜ν™˜
μ§€μ›μž DTO
src/main/java/KUSITMS/WITHUS/domain/application/application/dto/ApplicationResponseDTO.java
μ‹ κ·œ record ApplicantForMailSms μΆ”κ°€ (applicationId, name, email, profileImageUrl) 및 정적 from() λ©”μ„œλ“œ
지원 μ €μž₯μ†Œ / 리포지토리 κ΅¬ν˜„
src/main/java/.../application/repository/ApplicationJpaRepository.java, .../ApplicationRepository.java, .../ApplicationRepositoryImpl.java
findByRecruitment_IdAndOrganizationRoleIsNull(Long), findByRecruitmentIdAndNameOrEmail(Long, String) λ“± ν‚€μ›Œλ“œ/곡톡역할 쑰회 λ©”μ„œλ“œ μΆ”κ°€ 및 κ΅¬ν˜„ (QueryDSL/쑰건절 포함)
지원 μ„œλΉ„μŠ€
src/main/java/.../application/service/ApplicationService.java, .../ApplicationServiceImpl.java
searchApplicantsForMailSms(Long, String) λ©”μ„œλ“œ μΆ”κ°€; getById()μ—μ„œ 평가 κΈ°μ€€ 쑰회λ₯Ό findCommonAndByOrganizationRole()으둜 λ³€κ²½
ν‰κ°€μž λ°°μ • 둜직
src/main/java/.../service/evaluator/EvaluatorAssignmentService.java
organizationRoleIdκ°€ null인 경우 곡톡 μ—­ν• λ‘œ μ²˜λ¦¬ν•˜λ„λ‘ λΆ„κΈ° μΆ”κ°€
λ°°μ • κ΄€λ ¨ DTO/μ—”ν‹°ν‹°
src/main/java/.../applicationEvaluator/dto/ApplicationEvaluatorRequestDTO.java, src/main/java/.../distributionRequest/entity/DistributionAssignment.java, src/main/java/.../distributionRequest/dto/DistributionRequestResponseDTO.java
PartAssignment.organizationRoleIdμ—μ„œ @NotNull 제거(μ„€λͺ… λ³€κ²½), DistributionAssignment의 JoinColumn nullable 제거(선택적), 응닡 DTOμ—μ„œ null-safe 처리 μΆ”κ°€
DistributionRequest 리포지토리 κ΅¬ν˜„
src/main/java/.../distributionRequest/repository/DistributionRequestRepositoryImpl.java
JPAQueryFactory μ£Όμž… 및 QueryDSL 기반 fetchJoin 쿼리둜 λ¦¬νŒ©ν† λ§
평가 κΈ°μ€€ μ €μž₯μ†Œ
src/main/java/.../evaluation/evaluationCriteria/repository/EvaluationCriteriaRepository.java, .../EvaluationCriteriaRepositoryImpl.java
findCommonAndByOrganizationRole(Long, EvaluationType, OrganizationRole) μΆ”κ°€ 및 QueryDSL κ΅¬ν˜„
μ§μœ„ DTO 및 μ„œλΉ„μŠ€ λ¦¬νŒ©ν† λ§
src/main/java/.../recruitment/position/dto/PositionResponseDTO.java, .../recruitment/position/service/PositionServiceImpl.java
Detail.from(OrganizationRole) μΆ”κ°€, findAllByRecruitmentId()λ₯Ό QueryDSL 기반 쑰회둜 λ³€κ²½ 및 OrganizationRole λ§€ν•‘
ν…œν”Œλ¦Ώ 컨트둀러/μ„œλΉ„μŠ€/레포
src/main/java/.../template/controller/TemplateController.java, .../service/TemplateService.java, .../service/TemplateServiceImpl.java, .../repository/TemplateRepository.java, .../repository/TemplateRepositoryImpl.java
μ—…λ°μ΄νŠΈμ— @CurrentUser User 인자 μΆ”κ°€(κΆŒν•œ 검증), ν…œν”Œλ¦Ώ μ‚­μ œ μ—”λ“œν¬μΈνŠΈΒ·μ„œλΉ„μŠ€Β·λ ˆν¬ κ΅¬ν˜„ μΆ”κ°€, validateUserHasAccess() λ„μž…, μ‚­μ œ κΆŒν•œ 검사 및 μ˜ˆμ™Έ μ½”λ“œ μΆ”κ°€
μ—λŸ¬ μ½”λ“œ
src/main/java/KUSITMS/WITHUS/global/exception/ErrorCode.java
μ‹ κ·œ enum μƒμˆ˜ TEMPLATE_NO_PERMISSION("TEMPLATE403", "ν•΄λ‹Ή ν…œν”Œλ¦Ώμ— λŒ€ν•œ κΆŒν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€.", HttpStatus.FORBIDDEN) μΆ”κ°€
응닡 DTO(λͺ¨μ§‘)
src/main/java/.../recruitment/recruitment/dto/RecruitmentResponseDTO.java
Simple λ ˆμ½”λ“œμ— isInterviewRequired Boolean ν•„λ“œ μΆ”κ°€ 및 λ§€ν•‘ 반영

Sequence Diagram(s)

sequenceDiagram
    actor Admin
    participant Controller as AdminApplicationController
    participant Service as ApplicationService
    participant Repo as ApplicationRepository
    participant DB

    Admin->>Controller: GET /api/v1/admin/applications/recruitment/{id}/search?keyword=...
    Controller->>Service: searchApplicantsForMailSms(recruitmentId, keyword)
    Service->>Repo: findByRecruitmentIdAndNameOrEmail(recruitmentId, keyword)
    Repo->>DB: SELECT ... WHERE recruitment_id=? AND (name ILIKE '%keyword%' OR email ILIKE '%keyword%') / or recruitment + orgRole IS NULL
    DB-->>Repo: [Application...]
    Repo-->>Service: [Application...]
    Service->>Service: map Application -> ApplicantForMailSms
    Service-->>Controller: SuccessResponse<List<ApplicantForMailSms>>
    Controller-->>Admin: 200 OK
Loading
sequenceDiagram
    actor User
    participant Controller as TemplateController
    participant Service as TemplateService
    participant Repo as TemplateRepository
    participant DB
    participant Validator as validateUserHasAccess

    User->>Controller: DELETE /templates/{id}
    Controller->>Service: delete(templateId, user)
    Service->>Repo: getById(templateId)
    Repo->>DB: SELECT ... JOIN organization WHERE id=?
    DB-->>Repo: Template
    Repo-->>Service: Template
    Service->>Validator: validateUserHasAccess(template, user)
    alt κΆŒν•œ 있음
        Validator-->>Service: OK
        Service->>Repo: delete(templateId)
        Repo->>DB: DELETE FROM template WHERE id=?
        DB-->>Repo: success
        Repo-->>Service: success
        Service-->>Controller: SuccessResponse("μ‚­μ œ μ™„λ£Œ")
        Controller-->>User: 200 OK
    else κΆŒν•œ μ—†μŒ
        Validator-->>Service: throw CustomException(TEMPLATE_NO_PERMISSION)
        Service-->>Controller: exception
        Controller-->>User: ErrorResponse(TEMPLATE403)
    end
Loading

Estimated code review effort

🎯 3 (쀑간) | ⏱️ ~25λΆ„

  • μΆ”κ°€ κ²€ν†  ꢌμž₯ ν•­λͺ©:
    • OrganizationRole null 처리 일관성(μ—”ν‹°ν‹°, DTO, μ €μž₯μ†Œ 쿼리)
    • QueryDSL 쿼리(DistributionRequestRepositoryImpl, EvaluationCriteriaRepositoryImpl, PositionServiceImpl)μ—μ„œμ˜ fetchJoin/μ„±λŠ₯Β·N+1 μœ„ν—˜
    • Template κΆŒν•œ 검증(validateUserHasAccess) 및 μ˜ˆμ™Έ 처리 흐름
    • ApplicationServiceImpl.getById()μ—μ„œ 평가 κΈ°μ€€ λ³€κ²½ 영ν–₯ λ²”μœ„

Possibly related issues

Possibly related PRs

Suggested labels

πŸ›bug, 🌟feature

Suggested reviewers

  • SeongHo5356
  • KJaeKwan

Poem

🐰
μƒˆλ‘œ 온 검색 κΈΈ 따라 λ›°λ©°,
곡톡 역할도 μ‚΄ν”Όλ„€, μ‚΄κΈˆμ‚΄κΈˆ.
ν…œν”Œλ¦Ώ 문은 μ—΄μ‡ λ‘œ 잠그고,
κΆŒν•œ 확인 ν›„ μ•ˆμ „νžˆ λ‹«μ•„μš”.
λ‹Ήκ·Ό ν•œ μž… μΆ•ν•˜μ˜ 좀을 μΆ°μš” πŸ₯•βœ¨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.11% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive PR 제λͺ©μ΄ λ³€κ²½μ‚¬ν•­μ˜ μ£Όμš” λ‚΄μš©μ„ λͺ…ν™•ν•˜κ²Œ μš”μ•½ν•˜μ§€ λͺ»ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. '3μ°¨ μΆ”κ°€ μš”μ²­ μˆ˜μ •'은 맀우 λͺ¨ν˜Έν•˜λ©°, μ‹€μ œ 변경사항(μ§€μ›μž 검색, ν…œν”Œλ¦Ώ μ‚­μ œ, 파트 쑰회 μˆ˜μ • λ“±)을 ꡬ체적으둜 μ„€λͺ…ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 제λͺ©μ„ 더 ꡬ체적으둜 λ³€κ²½ν•˜μ—¬ μ£Όμš” 변경사항을 λͺ…ν™•νžˆ ν‘œν˜„ν•˜μ„Έμš”. 예: 'feat: μ§€μ›μž 검색 및 ν…œν”Œλ¦Ώ μ‚­μ œ API μΆ”κ°€, ν‰κ°€μž λΆ„λ°° 및 쑰회 κΈ°λŠ₯ κ°œμ„ '
βœ… Passed checks (1 passed)
Check name Status Explanation
Description check βœ… Passed PR μ„€λͺ…이 ν…œν”Œλ¦Ώμ˜ ν•„μˆ˜ μ„Ήμ…˜μ„ ν¬ν•¨ν•˜κ³  있으며, Related Issue와 Task Detailsκ°€ μΆ©λΆ„νžˆ κΈ°μˆ λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. λͺ¨λ“  6개의 μž‘μ—… ν•­λͺ©μ΄ μ²΄ν¬λ˜μ–΄ 있고 ꡬ체적인 λ‚΄μš©μ΄ μ œμ‹œλ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.
✨ Finishing touches
  • πŸ“ Generate docstrings
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/159-3rd

πŸ“œ Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between aadfdb0 and 1d38cd5.

πŸ“’ Files selected for processing (1)
  • src/main/java/KUSITMS/WITHUS/domain/recruitment/recruitment/dto/RecruitmentResponseDTO.java (1 hunks)
⏰ 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)
  • GitHub Check: build-docker-image
πŸ”‡ Additional comments (1)
src/main/java/KUSITMS/WITHUS/domain/recruitment/recruitment/dto/RecruitmentResponseDTO.java (1)

272-284: 변경사항이 μ˜¬λ°”λ₯΄κ²Œ κ΅¬ν˜„λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

Simple λ ˆμ½”λ“œμ— isInterviewRequired ν•„λ“œκ°€ 적절히 μΆ”κ°€λ˜μ—ˆκ³ , νŒ©ν† λ¦¬ λ©”μ„œλ“œλ„ μ •ν™•ν•˜κ²Œ μ—…λ°μ΄νŠΈλ˜μ—ˆμŠ΅λ‹ˆλ‹€. PR λͺ©ν‘œμΈ λ©΄μ ‘ 유무 정보 제곡 κΈ°λŠ₯κ³Ό μΌμΉ˜ν•˜λ©°, 파일 λ‚΄ λ‹€λ₯Έ DTOλ“€μ˜ νŒ¨ν„΄κ³Όλ„ 일관성을 μœ μ§€ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.


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.

❀️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/main/java/KUSITMS/WITHUS/domain/application/distributionRequest/dto/DistributionRequestResponseDTO.java (1)

42-48: Null-safety κ΅¬ν˜„μ΄ μ˜¬λ°”λ¦…λ‹ˆλ‹€.

organizationRole이 null일 λ•Œ NPEλ₯Ό λ°©μ§€ν•˜λŠ” 둜직이 μ •ν™•ν•˜κ²Œ κ΅¬ν˜„λ˜μ—ˆμŠ΅λ‹ˆλ‹€. "곡톡" 역할을 λ‚˜νƒ€λ‚΄λŠ” κΈ°λ³Έκ°’ μ²˜λ¦¬λ„ PR λͺ©μ κ³Ό 잘 λΆ€ν•©ν•©λ‹ˆλ‹€.

πŸ’‘ 선택적 κ°œμ„ μ‚¬ν•­: 맀직 μŠ€νŠΈλ§μ„ μƒμˆ˜λ‘œ μΆ”μΆœ

"곡톡" λ¬Έμžμ—΄μ΄ ν•˜λ“œμ½”λ”©λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. λ™μΌν•œ 값이 λ‹€λ₯Έ κ³³μ—μ„œλ„ μ‚¬μš©λ  κ°€λŠ₯성이 μžˆλ‹€λ©΄ μƒμˆ˜λ‘œ μΆ”μΆœν•˜λŠ” 것을 κ³ λ €ν•΄λ³΄μ„Έμš”.

μ˜ˆμ‹œ:

+    private static final String COMMON_ROLE_NAME = "곡톡";
+
     @Schema(description = "평가 λ‹΄λ‹Ήμž λΆ„λ°° μ΅œμ‹  이λ ₯ 리슀트 DTO")
     public record Assignment(
             @Schema(description = "지원 μ—­ν•  λͺ…") String organizationRoleName,
             @Schema(description = "평가 νƒ€μž…") EvaluationType evaluationType,
             @Schema(description = "μ§€μ›μ„œλ‹Ή λ°°μ •ν•œ ν‰κ°€μž 수") int count
     ) {
         public static Assignment from(DistributionAssignment a) {
             return new Assignment(
-                    a.getOrganizationRole() != null ? a.getOrganizationRole().getName() : "곡톡",
+                    a.getOrganizationRole() != null ? a.getOrganizationRole().getName() : COMMON_ROLE_NAME,
                     a.getEvaluationType(),
                     a.getCount()
             );
         }
     }
src/main/java/KUSITMS/WITHUS/domain/application/application/controller/AdminApplicationController.java (1)

159-167: λŒ€λŸ‰ 결과에 λŒ€ν•œ νŽ˜μ΄μ§€λ„€μ΄μ…˜ κ³ λ €

ν˜„μž¬ μ—”λ“œν¬μΈνŠΈλŠ” Listλ₯Ό λ°˜ν™˜ν•˜μ—¬ λͺ¨λ“  κ²°κ³Όλ₯Ό ν•œ λ²ˆμ— κ°€μ Έμ˜΅λ‹ˆλ‹€. μ§€μ›μž μˆ˜κ°€ λ§Žμ€ 곡고의 경우 μ„±λŠ₯ λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

메일/SMS λ°œμ†‘ λͺ©μ μƒ 전체 λͺ©λ‘μ΄ ν•„μš”ν•˜λ‹€λ©΄ ν˜„μž¬ κ΅¬ν˜„μ΄ μ ν•©ν•˜μ§€λ§Œ, 검색 κ²°κ³Όκ°€ λ§Žμ„ κ²ƒμœΌλ‘œ μ˜ˆμƒλœλ‹€λ©΄ νŽ˜μ΄μ§€λ„€μ΄μ…˜ λ„μž…μ„ κ³ λ €ν•΄ λ³΄μ„Έμš”.

src/main/java/KUSITMS/WITHUS/domain/recruitment/position/service/PositionServiceImpl.java (1)

17-19: 정적 μž„ν¬νŠΈ recruitmentκ°€ create() λ©”μ„œλ“œμ˜ 둜컬 λ³€μˆ˜μ™€ ν˜Όλ™λ  수 μžˆμŠ΅λ‹ˆλ‹€.

Line 41μ—μ„œ Recruitment recruitment = ...둜 μ„ μ–Έλœ 둜컬 λ³€μˆ˜μ™€ 정적 μž„ν¬νŠΈ QRecruitment.recruitment의 이름이 λ™μΌν•©λ‹ˆλ‹€. ν˜„μž¬λŠ” λ‹€λ₯Έ λ©”μ„œλ“œ μŠ€μ½”ν”„μ— μžˆμ–΄ λ¬Έμ œκ°€ μ—†μ§€λ§Œ, ν–₯ν›„ λ¦¬νŒ©ν† λ§μ΄λ‚˜ μ½”λ“œ 볡사 μ‹œ ν˜Όλž€μ„ μ•ΌκΈ°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ“œ Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 1b55b40 and aadfdb0.

πŸ“’ Files selected for processing (22)
  • src/main/java/KUSITMS/WITHUS/domain/application/application/controller/AdminApplicationController.java (1 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/application/application/dto/ApplicationResponseDTO.java (1 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/application/application/repository/ApplicationJpaRepository.java (1 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/application/application/repository/ApplicationRepository.java (2 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/application/application/repository/ApplicationRepositoryImpl.java (2 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/application/application/service/ApplicationService.java (1 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/application/application/service/ApplicationServiceImpl.java (2 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/application/application/service/evaluator/EvaluatorAssignmentService.java (2 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/application/applicationEvaluator/dto/ApplicationEvaluatorRequestDTO.java (1 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/application/distributionRequest/dto/DistributionRequestResponseDTO.java (1 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/application/distributionRequest/entity/DistributionAssignment.java (1 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/application/distributionRequest/repository/DistributionRequestRepositoryImpl.java (2 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/evaluation/evaluationCriteria/repository/EvaluationCriteriaRepository.java (2 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/evaluation/evaluationCriteria/repository/EvaluationCriteriaRepositoryImpl.java (2 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/recruitment/position/dto/PositionResponseDTO.java (2 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/recruitment/position/service/PositionServiceImpl.java (2 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/template/controller/TemplateController.java (1 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/template/repository/TemplateRepository.java (1 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/template/repository/TemplateRepositoryImpl.java (2 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/template/service/TemplateService.java (1 hunks)
  • src/main/java/KUSITMS/WITHUS/domain/template/service/TemplateServiceImpl.java (3 hunks)
  • src/main/java/KUSITMS/WITHUS/global/exception/ErrorCode.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/main/java/KUSITMS/WITHUS/domain/evaluation/evaluationCriteria/repository/EvaluationCriteriaRepositoryImpl.java (2)
src/main/java/KUSITMS/WITHUS/domain/application/application/repository/ApplicationRepositoryImpl.java (1)
  • Repository (21-183)
src/main/java/KUSITMS/WITHUS/domain/recruitment/recruitment/repository/RecruitmentRepositoryImpl.java (1)
  • Repository (17-98)
src/main/java/KUSITMS/WITHUS/domain/recruitment/position/service/PositionServiceImpl.java (1)
src/main/java/KUSITMS/WITHUS/domain/interview/timeslot/repository/TimeSlotRepositoryImpl.java (1)
  • Service (22-106)
src/main/java/KUSITMS/WITHUS/domain/application/distributionRequest/repository/DistributionRequestRepositoryImpl.java (1)
src/main/java/KUSITMS/WITHUS/domain/application/application/repository/ApplicationRepositoryImpl.java (1)
  • Repository (21-183)
⏰ 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)
  • GitHub Check: build-docker-image
πŸ”‡ Additional comments (26)
src/main/java/KUSITMS/WITHUS/domain/application/application/service/ApplicationService.java (1)

31-31: LGTM!

λ©”μ„œλ“œ μ‹œκ·Έλ‹ˆμ²˜κ°€ κΈ°μ‘΄ μΈν„°νŽ˜μ΄μŠ€ νŒ¨ν„΄κ³Ό 일관성 있게 μ •μ˜λ˜μ—ˆμŠ΅λ‹ˆλ‹€. recruitmentId둜 λ²”μœ„λ₯Ό μ œν•œν•˜κ³  keyword둜 κ²€μƒ‰ν•˜λŠ” ꡬ쑰가 μ μ ˆν•©λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/domain/application/application/repository/ApplicationJpaRepository.java (1)

14-14: LGTM!

Spring Data JPA의 IsNull 접미사λ₯Ό μ‚¬μš©ν•œ 쿼리 λ©”μ„œλ“œ 넀이밍이 μ˜¬λ°”λ₯΄κ²Œ μ μš©λ˜μ—ˆμŠ΅λ‹ˆλ‹€. κΈ°μ‘΄ findByRecruitment_IdAndOrganizationRole_Id λ©”μ„œλ“œμ™€ μΌκ΄€λœ νŒ¨ν„΄μ„ μœ μ§€ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/domain/evaluation/evaluationCriteria/repository/EvaluationCriteriaRepository.java (1)

14-14: LGTM!

λ©”μ„œλ“œ μ‹œκ·Έλ‹ˆμ²˜κ°€ μ μ ˆν•©λ‹ˆλ‹€. OrganizationRole μ—”ν‹°ν‹°λ₯Ό 직접 λ°›μ•„ 곡톡(null) 및 νŠΉμ • μ—­ν•  μΌ€μ΄μŠ€λ₯Ό λͺ¨λ‘ μ²˜λ¦¬ν•  수 μžˆλ„λ‘ μ„€κ³„λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/domain/application/applicationEvaluator/dto/ApplicationEvaluatorRequestDTO.java (1)

29-31: @NotNull μ œμ•½ 쑰건 μ œκ±°μ— λ”°λ₯Έ API λ³€κ²½ 확인 ν•„μš”

organizationRoleIdμ—μ„œ @NotNull μ œμ•½ 쑰건이 μ œκ±°λ˜μ–΄ null 값이 ν—ˆμš©λ©λ‹ˆλ‹€. Swagger μ„€λͺ…에 λͺ…ν™•νžˆ λ¬Έμ„œν™”λ˜μ–΄ μžˆμ§€λ§Œ, κΈ°μ‘΄ API ν΄λΌμ΄μ–ΈνŠΈμ™€μ˜ ν˜Έν™˜μ„±μ„ 확인해 μ£Όμ„Έμš”.

src/main/java/KUSITMS/WITHUS/domain/application/application/service/evaluator/EvaluatorAssignmentService.java (2)

49-51: LGTM!

organizationRoleIdκ°€ null인 경우λ₯Ό 적절히 μ²˜λ¦¬ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. null 체크 ν›„ μ‘°κ±΄λΆ€λ‘œ μ—”ν‹°ν‹°λ₯Ό μ‘°νšŒν•˜κ±°λ‚˜ null을 ν• λ‹Ήν•˜λŠ” 둜직이 λͺ…ν™•ν•©λ‹ˆλ‹€.


82-85: LGTM!

organizationRoleIdκ°€ null인 경우 곡톡(μ—­ν•  λ―Έμ§€μ •) μ§€μ›μ„œλ₯Ό μ‘°νšŒν•˜κ³ , κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ νŠΉμ • μ—­ν• μ˜ μ§€μ›μ„œλ₯Ό μ‘°νšŒν•˜λŠ” λΆ„κΈ° 둜직이 μ˜¬λ°”λ₯΄κ²Œ κ΅¬ν˜„λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/domain/application/application/repository/ApplicationRepository.java (2)

17-17: LGTM!

null OrganizationRole을 κ°€μ§„ μ§€μ›μ„œλ₯Ό μ‘°νšŒν•˜λŠ” λ©”μ„œλ“œκ°€ 적절히 μΆ”κ°€λ˜μ—ˆμŠ΅λ‹ˆλ‹€.


26-26: LGTM!

이름 λ˜λŠ” μ΄λ©”μΌλ‘œ μ§€μ›μžλ₯Ό κ²€μƒ‰ν•˜λŠ” λ©”μ„œλ“œκ°€ μΆ”κ°€λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ©”μ„œλ“œλͺ…이 κΈ°λŠ₯을 λͺ…ν™•νžˆ μ„€λͺ…ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/domain/application/application/dto/ApplicationResponseDTO.java (1)

558-573: LGTM!

ApplicantForMailSms DTOκ°€ 메일/SMS λ°œμ†‘ μš©λ„μ— 맞게 ν•„μš”ν•œ ν•„λ“œλ§Œ ν¬ν•¨ν•˜μ—¬ 잘 μ •μ˜λ˜μ—ˆμŠ΅λ‹ˆλ‹€. from() νŒ©ν† λ¦¬ λ©”μ„œλ“œλ₯Ό ν†΅ν•œ 맀핑도 μ μ ˆν•©λ‹ˆλ‹€.

μ°Έκ³ : λ‹€λ₯Έ DTO듀은 id ν•„λ“œλͺ…을 μ‚¬μš©ν•˜λŠ” 반면 이 DTOλŠ” applicationIdλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€. μ˜λ„λœ 섀계라면 λ¬Έμ œμ—†μ§€λ§Œ, 일관성을 μœ„ν•΄ κ²€ν† ν•΄ 보싀 수 μžˆμŠ΅λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/domain/evaluation/evaluationCriteria/repository/EvaluationCriteriaRepositoryImpl.java (1)

44-59: LGTM!

QueryDSL κ΅¬ν˜„μ΄ μ˜¬λ°”λ¦…λ‹ˆλ‹€. organizationRole이 null일 λ•ŒλŠ” 곡톡 κΈ°μ€€(null role)만 μ‘°νšŒν•˜κ³ , 값이 μžˆμ„ λ•ŒλŠ” 곡톡 κΈ°μ€€κ³Ό νŠΉμ • μ—­ν•  기쀀을 λͺ¨λ‘ μ‘°νšŒν•˜λŠ” 둜직이 μ •ν™•ν•©λ‹ˆλ‹€. λ‹€λ₯Έ Repository κ΅¬ν˜„μ²΄λ“€κ³Ό μΌκ΄€λœ νŒ¨ν„΄μ„ λ”°λ₯΄κ³  μžˆμŠ΅λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/domain/application/application/service/ApplicationServiceImpl.java (2)

209-216: LGTM!

평가 κΈ°μ€€ 쑰회 둜직이 μ˜¬λ°”λ₯΄κ²Œ λ³€κ²½λ˜μ—ˆμŠ΅λ‹ˆλ‹€. findCommonAndByOrganizationRole을 μ‚¬μš©ν•˜μ—¬ 곡톡 κΈ°μ€€(organizationRole이 null)κ³Ό μ§€μ›μ„œμ˜ νŠΉμ • 역할에 ν•΄λ‹Ήν•˜λŠ” 기쀀을 ν•¨κ»˜ μ‘°νšŒν•©λ‹ˆλ‹€. app.getOrganizationRole()이 null인 κ²½μš°λ„ μƒˆ λ©”μ„œλ“œμ—μ„œ μ˜¬λ°”λ₯΄κ²Œ μ²˜λ¦¬λ©λ‹ˆλ‹€.


472-484: LGTM!

메일/문자 λ°œμ†‘μš© μ§€μ›μž 검색 λ©”μ„œλ“œκ°€ κΉ”λ”ν•˜κ²Œ κ΅¬ν˜„λ˜μ—ˆμŠ΅λ‹ˆλ‹€. Javadoc λ¬Έμ„œν™”λ„ 잘 λ˜μ–΄ 있고, κΈ°μ‘΄ μ½”λ“œλ² μ΄μŠ€μ˜ νŒ¨ν„΄μ„ λ”°λ₯΄κ³  μžˆμŠ΅λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/domain/application/application/repository/ApplicationRepositoryImpl.java (2)

65-68: LGTM!

JPA Repository둜의 λ‹¨μˆœ μœ„μž„μ΄λ©°, organizationRole이 null인 μ§€μ›μ„œλ₯Ό μ‘°νšŒν•˜λŠ” κΈ°λŠ₯이 μ˜¬λ°”λ₯΄κ²Œ κ΅¬ν˜„λ˜μ—ˆμŠ΅λ‹ˆλ‹€.


163-182: LGTM!

ν‚€μ›Œλ“œ 기반 검색 κ΅¬ν˜„μ΄ μ˜¬λ°”λ¦…λ‹ˆλ‹€. 같은 파일의 findEligibleCandidates λ©”μ„œλ“œ(lines 97-101)와 λ™μΌν•œ null predicate νŒ¨ν„΄μ„ μ‚¬μš©ν•˜μ—¬ 일관성이 μžˆμŠ΅λ‹ˆλ‹€. QueryDSL의 where() μ ˆμ—μ„œ null predicateλŠ” μžλ™μœΌλ‘œ λ¬΄μ‹œλ˜λ―€λ‘œ, ν‚€μ›Œλ“œκ°€ 없을 λ•Œ 전체 λͺ©λ‘μ΄ μ‘°νšŒλ©λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/domain/recruitment/position/dto/PositionResponseDTO.java (2)

3-3: LGTM!

μƒˆλ‘œμš΄ from(OrganizationRole) λ©”μ„œλ“œλ₯Ό μ§€μ›ν•˜κΈ° μœ„ν•΄ ν•„μš”ν•œ importμž…λ‹ˆλ‹€.


28-38: λ©”μ„œλ“œ κ΅¬ν˜„μ΄ μ˜¬λ°”λ¦…λ‹ˆλ‹€.

OrganizationRole μ—”ν‹°ν‹°λŠ” Lombok의 @Getter μ• λ„ˆν…Œμ΄μ…˜μœΌλ‘œ 인해 getId(), getName(), getColor() λ©”μ„œλ“œκ°€ μžλ™ μƒμ„±λ©λ‹ˆλ‹€. from(OrganizationRole) λ©”μ„œλ“œλŠ” 이 getter λ©”μ„œλ“œλ“€μ„ μ˜¬λ°”λ₯΄κ²Œ μ‚¬μš©ν•˜λ©°, null 체크 μ²˜λ¦¬λ„ 적절히 κ΅¬ν˜„λ˜μ–΄ 있고, κΈ°μ‘΄ from(Position) λ©”μ„œλ“œμ™€ μΌκ΄€λœ νŒ¨ν„΄μ„ λ”°λ¦…λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/domain/application/distributionRequest/repository/DistributionRequestRepositoryImpl.java (1)

4-4: QueryDSL 인프라 μΆ”κ°€ 승인

QueryDSL을 λ„μž…ν•˜μ—¬ νƒ€μž… μ•ˆμ „ν•œ 쿼리와 fetch join μ΅œμ ν™”λ₯Ό κ°€λŠ₯ν•˜κ²Œ ν•˜λŠ” λ³€κ²½μ‚¬ν•­μž…λ‹ˆλ‹€. ν•„μš”ν•œ λͺ¨λ“  Q-ν΄λž˜μŠ€κ°€ μž„ν¬νŠΈλ˜μ—ˆμœΌλ©°, JPAQueryFactoryκ°€ μƒμ„±μž μ£Όμž…μ„ 톡해 μ˜¬λ°”λ₯΄κ²Œ μ£Όμž…λ©λ‹ˆλ‹€.

Also applies to: 10-12, 19-19

src/main/java/KUSITMS/WITHUS/domain/application/distributionRequest/entity/DistributionAssignment.java (1)

24-26: 쑰직 μ—­ν•  nullable 변경은 이미 적절히 μ²˜λ¦¬λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€

organizationRole ν•„λ“œμ˜ nullable 변경에 λŒ€ν•œ 확인이 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€:

  • Null μ•ˆμ „μ„±: EvaluatorAssignmentServiceμ—μ„œ null 체크 ν›„ ν• λ‹Ή (라인 49-51), DistributionRequestResponseDTOμ—μ„œ "곡톡" 폴백 처리 (라인 44) λ“± λͺ¨λ“  호좜 μ§€μ μ—μ„œ 적절히 처리됨
  • λ°μ΄ν„°λ² μ΄μŠ€ μŠ€ν‚€λ§ˆ: application-prod.yml의 spring.jpa.hibernate.ddl-auto: update μ„€μ •μœΌλ‘œ Hibernate이 μžλ™μœΌλ‘œ μŠ€ν‚€λ§ˆ 변경을 관리
  • 쿼리 둜직: DistributionRequestRepositoryImplμ—μ„œ leftJoin으둜 nullable FK μ˜¬λ°”λ₯΄κ²Œ 처리, EvaluatorAssignmentServiceμ—μ„œ organizationRoleId null 여뢀에 따라 별도 쿼리 μ‹€ν–‰

λͺ¨λ“  κ΄€λ ¨ DTO와 μ„œλΉ„μŠ€ κ³„μΈ΅μ—μ„œ null 값을 μ˜¬λ°”λ₯΄κ²Œ μ²˜λ¦¬ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/global/exception/ErrorCode.java (1)

137-137: LGTM!

ν…œν”Œλ¦Ώ κΆŒν•œ 검증을 μœ„ν•œ μ—λŸ¬ μ½”λ“œκ°€ μ μ ˆν•˜κ²Œ μΆ”κ°€λ˜μ—ˆμŠ΅λ‹ˆλ‹€. 넀이밍, HTTP μƒνƒœ μ½”λ“œ(403 FORBIDDEN), 그리고 λ©”μ‹œμ§€ λͺ¨λ‘ κΆŒν•œ κ±°λΆ€ μ‹œλ‚˜λ¦¬μ˜€μ— μ ν•©ν•©λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/domain/template/repository/TemplateRepositoryImpl.java (1)

25-35: 쑰직 eager loading μ²˜λ¦¬κ°€ μ μ ˆν•©λ‹ˆλ‹€.

QueryDSL의 fetchJoin을 μ‚¬μš©ν•˜μ—¬ Organization을 미리 λ‘œλ”©ν•¨μœΌλ‘œμ¨ N+1 쿼리 문제λ₯Ό λ°©μ§€ν•©λ‹ˆλ‹€. μ΄λŠ” μ„œλΉ„μŠ€ λ ˆμ΄μ–΄μ—μ„œ κΆŒν•œ 검증 μ‹œ μΆ”κ°€ 쿼리 없이 쑰직 정보에 μ ‘κ·Όν•  수 있게 ν•΄μ€λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/domain/template/repository/TemplateRepository.java (1)

12-12: LGTM!

Repository μΈν„°νŽ˜μ΄μŠ€μ— delete λ©”μ„œλ“œκ°€ μ μ ˆν•˜κ²Œ μΆ”κ°€λ˜μ—ˆμŠ΅λ‹ˆλ‹€. κΈ°μ‘΄ CRUD λ©”μ„œλ“œλ“€κ³Ό μΌκ΄€λœ μ‹œκ·Έλ‹ˆμ²˜λ₯Ό μœ μ§€ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/domain/template/service/TemplateService.java (1)

14-15: μ‚¬μš©μž 기반 κΆŒν•œ 검증을 μœ„ν•œ μΈν„°νŽ˜μ΄μŠ€ 섀계가 μ μ ˆν•©λ‹ˆλ‹€.

update와 delete λ©”μ„œλ“œ λͺ¨λ‘ User νŒŒλΌλ―Έν„°λ₯Ό μΆ”κ°€ν•˜μ—¬ 쑰직 기반 μ ‘κ·Ό μ œμ–΄λ₯Ό μΌκ΄€λ˜κ²Œ μ μš©ν–ˆμŠ΅λ‹ˆλ‹€. μ΅œμ†Œ κΆŒν•œ 원칙을 λ”°λ₯΄λŠ” 쒋은 μ„€κ³„μž…λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/domain/template/controller/TemplateController.java (2)

54-63: LGTM!

update μ—”λ“œν¬μΈνŠΈκ°€ μ‚¬μš©μž 인증을 ν¬ν•¨ν•˜λ„λ‘ μ μ ˆν•˜κ²Œ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€. @currentuser μ–΄λ…Έν…Œμ΄μ…˜μ„ ν†΅ν•œ μ‚¬μš©μž μΆ”μΆœκ³Ό μ„œλΉ„μŠ€ λ ˆμ΄μ–΄λ‘œμ˜ μœ„μž„μ΄ μ˜¬λ°”λ₯΄κ²Œ κ΅¬ν˜„λ˜μ—ˆμŠ΅λ‹ˆλ‹€.


65-73: μ‚­μ œ μ—”λ“œν¬μΈνŠΈ κ΅¬ν˜„μ΄ μ μ ˆν•©λ‹ˆλ‹€.

RESTful κ·œμΉ™μ„ λ”°λ₯΄λ©°, μ‚¬μš©μž 인증 및 쑰직 기반 μ ‘κ·Ό μ œμ–΄κ°€ update μ—”λ“œν¬μΈνŠΈμ™€ μΌκ΄€λ˜κ²Œ κ΅¬ν˜„λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

src/main/java/KUSITMS/WITHUS/domain/template/service/TemplateServiceImpl.java (2)

67-79: κΆŒν•œ 검증 둜직이 μ˜¬λ°”λ₯΄κ²Œ κ΅¬ν˜„λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

μ‚¬μš©μžκ°€ ν…œν”Œλ¦Ώμ˜ 쑰직에 μ†ν•΄μžˆλŠ”μ§€ ν™•μΈν•˜λŠ” 둜직이 λͺ…ν™•ν•˜κ³  μ •ν™•ν•©λ‹ˆλ‹€. anyMatchλ₯Ό μ‚¬μš©ν•˜μ—¬ 효율적으둜 첫 번째 일치 ν•­λͺ©μ—μ„œ 단락 평가λ₯Ό μˆ˜ν–‰ν•©λ‹ˆλ‹€.


83-95: update λ©”μ„œλ“œμ˜ κΆŒν•œ 검증 톡합이 μ μ ˆν•©λ‹ˆλ‹€.

ν…œν”Œλ¦Ώ 쑰회 ν›„ μ¦‰μ‹œ κΆŒν•œμ„ κ²€μ¦ν•˜μ—¬ λ³€κ²½ μž‘μ—… 전에 μ ‘κ·Ό μ œμ–΄λ₯Ό μˆ˜ν–‰ν•©λ‹ˆλ‹€. νŠΈλžœμž­μ…˜ μ²˜λ¦¬λ„ μ˜¬λ°”λ₯΄κ²Œ μ μš©λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

Comment on lines 37 to 46
public List<DistributionRequest> findAllByRecruitmentIdOrderByCreatedAtDesc(Long recruitmentId) {
return distributionRequestJpaRepository.findAllByRecruitmentIdOrderByCreatedAtDesc(recruitmentId);
return queryFactory
.selectFrom(distributionRequest)
.distinct()
.leftJoin(distributionRequest.assignments, distributionAssignment).fetchJoin()
.leftJoin(distributionAssignment.organizationRole, organizationRole).fetchJoin()
.where(distributionRequest.recruitmentId.eq(recruitmentId))
.orderBy(distributionRequest.createdAt.desc())
.fetch();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟑 Minor

N+1 쿼리 문제 해결을 μœ„ν•œ fetch join μ΅œμ ν™”

QueryDSL을 μ‚¬μš©ν•˜μ—¬ λͺ…μ‹œμ μœΌλ‘œ fetch join을 μˆ˜ν–‰ν•˜λŠ” λ¦¬νŒ©ν† λ§μ€ ꢌμž₯λ©λ‹ˆλ‹€. 이λ₯Ό 톡해 assignments와 organizationRole을 μ§€μ—° λ‘œλ”© μ‹œ λ°œμƒν•  수 μžˆλŠ” N+1 쿼리 문제λ₯Ό λ°©μ§€ν•©λ‹ˆλ‹€.

κ΅¬ν˜„ 상세:

  • fetch join으둜 κ΄€λ ¨ 데이터λ₯Ό μ¦‰μ‹œ λ‘œλ”©ν•˜λ©°, distinct()λŠ” OneToMany μ‘°μΈμ—μ„œ λ°œμƒν•˜λŠ” 쀑볡 μ—”ν‹°ν‹°λ₯Ό μ œκ±°ν•©λ‹ˆλ‹€
  • leftJoin μ‚¬μš©μœΌλ‘œ 아직 할당이 μ—†λŠ” DistributionRequest도 쑰회 κ°€λŠ₯ν•©λ‹ˆλ‹€
  • 이 νŒ¨ν„΄μ€ 업계 ν‘œμ€€ λ°©μ‹μž…λ‹ˆλ‹€

Hibernate 버전 고렀사항:

  • Hibernate 6μ—μ„œλŠ” DISTINCT ν‚€μ›Œλ“œκ°€ λΆˆν•„μš”ν•˜λ©° μžλ™μœΌλ‘œ 쀑볡을 μ œκ±°ν•©λ‹ˆλ‹€
  • Hibernate 5λ₯Ό μ‚¬μš© 쀑이라면 distinct()κ°€ λ°μ΄ν„°λ² μ΄μŠ€ λ ˆλ²¨μ—μ„œ μΆ”κ°€ 처리λ₯Ό μˆ˜ν–‰ν•˜λ―€λ‘œ, ν”„λ‘œμ νŠΈμ˜ Hibernate 버전을 ν™•μΈν•˜μ„Έμš”. ν•„μš”μ‹œ hibernate.query.passDistinctThrough=false 힌트 μ‚¬μš©μ„ κ²€ν† ν•˜μ„Έμš”.
πŸ€– Prompt for AI Agents
In
src/main/java/KUSITMS/WITHUS/domain/application/distributionRequest/repository/DistributionRequestRepositoryImpl.java
around lines 37 to 46, ensure the fetch-join optimization is correct: keep the
leftJoin(...).fetchJoin() calls to eagerly load assignments and organizationRole
(so DistributionRequest without assignments is still returned), but verify your
Hibernate versionβ€”if using Hibernate 6 you can remove distinct() because
Hibernate will deduplicate results automatically; if using Hibernate 5, keep
distinct() but add the query hint hibernate.query.passDistinctThrough=false (or
set the same property globally) to avoid pushing DISTINCT to the DB and causing
extra work.

Comment on lines +65 to 79
// Recruitment의 RecruitmentOrganizationRole λ¦¬μŠ€νŠΈμ™€ 각각의 OrganizationRole을 ν•¨κ»˜ μ‘°νšŒν•˜κΈ° μœ„ν•΄ fetch join μ‚¬μš©
Recruitment found = queryFactory
.selectFrom(recruitment)
.leftJoin(recruitment.positions, recruitmentOrganizationRole).fetchJoin()
.leftJoin(recruitmentOrganizationRole.organizationRole, organizationRole).fetchJoin()
.where(recruitment.id.eq(recruitmentId))
.fetchOne();

if (found == null) {
throw new CustomException(ErrorCode.RECRUITMENT_NOT_EXIST);
}

return found.getPositions().stream()
.map(ror -> PositionResponseDTO.Detail.from(ror.getOrganizationRole()))
.toList();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

organizationRole이 null일 경우 NPE λ°œμƒ κ°€λŠ₯성이 μžˆμŠ΅λ‹ˆλ‹€.

Line 69μ—μ„œ leftJoin을 μ‚¬μš©ν•˜κ³  μžˆμ–΄ organizationRole이 null일 수 μžˆμŠ΅λ‹ˆλ‹€. Line 78μ—μ„œ ror.getOrganizationRole()이 null을 λ°˜ν™˜ν•˜λ©΄ PositionResponseDTO.Detail.from(null) 호좜 μ‹œ NullPointerException이 λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ”Ž null 필터링을 μΆ”κ°€ν•œ μˆ˜μ • μ œμ•ˆ
         return found.getPositions().stream()
+                .filter(ror -> ror.getOrganizationRole() != null)
                 .map(ror -> PositionResponseDTO.Detail.from(ror.getOrganizationRole()))
                 .toList();

λ˜λŠ” organizationRole이 항상 μ‘΄μž¬ν•΄μ•Ό ν•˜λŠ” λΉ„μ¦ˆλ‹ˆμŠ€ μš”κ΅¬μ‚¬ν•­μ΄λΌλ©΄, leftJoin λŒ€μ‹  join을 μ‚¬μš©ν•˜λŠ” 것이 μ μ ˆν•©λ‹ˆλ‹€:

-                .leftJoin(recruitmentOrganizationRole.organizationRole, organizationRole).fetchJoin()
+                .join(recruitmentOrganizationRole.organizationRole, organizationRole).fetchJoin()

Committable suggestion skipped: line range outside the PR's diff.

πŸ€– Prompt for AI Agents
In
src/main/java/KUSITMS/WITHUS/domain/recruitment/position/service/PositionServiceImpl.java
around lines 65 to 79, the current leftJoin on
recruitmentOrganizationRole.organizationRole allows organizationRole to be null
and calling PositionResponseDTO.Detail.from(ror.getOrganizationRole()) may throw
an NPE; either change the fetch join to an inner join (replace leftJoin with
join) if business rules guarantee an OrganizationRole exists, or keep the
leftJoin and filter out null organizationRole values before mapping (e.g.,
stream().map(ror ->
ror.getOrganizationRole()).filter(Objects::nonNull).map(PositionResponseDTO.Detail::from)
or explicitly handle nulls by throwing a CustomException); implement the chosen
approach consistently so no null is passed into PositionResponseDTO.Detail.from.

Comment on lines +97 to +108
/**
* 문자/메일 ν…œν”Œλ¦Ώ μ‚­μ œ
* @param templateId μ‚­μ œν•  ν…œν”Œλ¦Ώ ID
* @param user ν˜„μž¬ μ‚¬μš©μž
*/
@Override
@Transactional
public void delete(Long templateId, User user) {
Template template = templateRepository.getById(templateId);
validateUserHasAccess(template, user);
templateRepository.delete(templateId);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

ν…œν”Œλ¦Ώ μ—”ν‹°ν‹°κ°€ 쀑볡 μ‘°νšŒλ˜λŠ” μ„±λŠ₯ μ΄μŠˆκ°€ μžˆμŠ΅λ‹ˆλ‹€.

delete λ©”μ„œλ“œμ—μ„œ λ‹€μŒκ³Ό 같은 νλ¦„μœΌλ‘œ ν…œν”Œλ¦Ώμ΄ 두 번 μ‘°νšŒλ©λ‹ˆλ‹€:

  1. Line 105: templateRepository.getById(templateId) - 첫 번째 쑰회
  2. Line 107: templateRepository.delete(templateId) 호좜
  3. TemplateRepositoryImpl.delete (line 56)μ—μ„œ λ‚΄λΆ€μ μœΌλ‘œ getById(templateId) 재호좜 - 두 번째 쑰회

λΆˆν•„μš”ν•œ λ°μ΄ν„°λ² μ΄μŠ€ 쿼리가 λ°œμƒν•˜μ—¬ μ„±λŠ₯에 영ν–₯을 μ€λ‹ˆλ‹€.

πŸ”Ž μ œμ•ˆν•˜λŠ” μˆ˜μ • λ°©μ•ˆ

λ°©μ•ˆ 1: 이미 μ‘°νšŒν•œ Template μ—”ν‹°ν‹°λ₯Ό ν™œμš©

TemplateRepositoryImpl.javaλ₯Ό μˆ˜μ •ν•˜μ—¬ Template μ—”ν‹°ν‹°λ₯Ό 직접 받도둝 λ³€κ²½:

 @Override
-public void delete(Long templateId) {
-    Template template = getById(templateId);
+public void delete(Template template) {
     templateJpaRepository.delete(template);
 }

TemplateRepository.java μΈν„°νŽ˜μ΄μŠ€λ„ μˆ˜μ •:

-void delete(Long templateId);
+void delete(Template template);

TemplateServiceImpl.javaμ—μ„œ μ‘°νšŒν•œ μ—”ν‹°ν‹°λ₯Ό 전달:

 public void delete(Long templateId, User user) {
     Template template = templateRepository.getById(templateId);
     validateUserHasAccess(template, user);
-    templateRepository.delete(templateId);
+    templateRepository.delete(template);
 }

λ°©μ•ˆ 2: Repository delete λ©”μ„œλ“œμ—μ„œ 쀑볡 쑰회 제거

TemplateRepositoryImpl.java:

 @Override
 public void delete(Long templateId) {
-    Template template = getById(templateId);
-    templateJpaRepository.delete(template);
+    templateJpaRepository.deleteById(templateId);
 }

단, 이 λ°©μ•ˆμ€ μ‚­μ œ μ „ 쑴재 μ—¬λΆ€ 검증이 μƒλž΅λ˜λ―€λ‘œ μ„œλΉ„μŠ€ λ ˆμ΄μ–΄μ—μ„œ 이미 getByIdλ₯Ό ν˜ΈμΆœν•˜λŠ” ν˜„μž¬ κ΅¬μ‘°μ—μ„œλ§Œ μ ν•©ν•©λ‹ˆλ‹€.

Committable suggestion skipped: line range outside the PR's diff.

πŸ€– Prompt for AI Agents
In src/main/java/KUSITMS/WITHUS/domain/template/service/TemplateServiceImpl.java
around lines 97 to 108, the Template entity is being fetched (getById) and then
delete(templateId) causes a second fetch inside the repository, producing
duplicate DB queries; fix by reworking the delete flow so the already-fetched
Template is used: update the TemplateRepository interface to add a
delete(Template template) overload (or replace delete(Long) with
delete(Template)), update TemplateRepositoryImpl.delete to accept and delete the
passed Template without re-querying, and change TemplateServiceImpl.delete to
call repository.delete(template) after validateUserHasAccess(template, user).
Ensure transactional semantics remain the same and adjust any callers/tests
accordingly.

Copy link
Copy Markdown
Contributor

@KJaeKwan KJaeKwan left a comment

Choose a reason for hiding this comment

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

ν™•μΈν–ˆμŠ΅λ‹ˆλ‹€ !

@EunjinWoo EunjinWoo merged commit 381157b into develop Dec 22, 2025
3 checks passed
@EunjinWoo EunjinWoo changed the title πŸ› Fix: 3μ°¨ μΆ”κ°€ μš”μ²­ μˆ˜μ • πŸ› Fix: 3μ°¨ μΆ”κ°€ μš”μ²­μ„ μˆ˜μ •ν•œλ‹€ Jan 8, 2026
@EunjinWoo EunjinWoo changed the title πŸ› Fix: 3μ°¨ μΆ”κ°€ μš”μ²­μ„ μˆ˜μ •ν•œλ‹€ πŸ› Fix: 3μ°¨ μΆ”κ°€ μš”μ²­μ‚¬ν•­μ„ μˆ˜μ •ν•œλ‹€ Jan 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants