Skip to content

[WTH-247] 유저 필드 nullable 처리#47

Open
hyxklee wants to merge 6 commits intodevfrom
refactor/WTH-247-엔티티-필드-nullable-분석-및-처리

Hidden character warning

The head ref may contain hidden characters: "refactor/WTH-247-\uc5d4\ud2f0\ud2f0-\ud544\ub4dc-nullable-\ubd84\uc11d-\ubc0f-\ucc98\ub9ac"
Open

[WTH-247] 유저 필드 nullable 처리#47
hyxklee wants to merge 6 commits intodevfrom
refactor/WTH-247-엔티티-필드-nullable-분석-및-처리

Conversation

@hyxklee
Copy link
Copy Markdown
Contributor

@hyxklee hyxklee commented Apr 6, 2026

📌 Summary

어떤 작업인지 한 줄 요약해 주세요.

User 엔티티의 이름, 이메일을 제외한 필드를 nullable 처리하고, PATCH 전략에 맞게 수정했습니다.
게시판 관련 수정사항을 반영했습니다

📝 Changes

변경사항을 what, why, how로 구분해 작성해 주세요.

What

""

Why

사용자 정보 중 일부 정보는 nullable하기 때문
게시판 동작을 위해

How

  • 학번, 전화번호, 학교, 학과 nullable 처리

  • PATCH 전략에 맞게 유저 정보 수정 API의 경우 필드가 없으면 수정하지 않도록 변경 -> 제거의 경우는 ""로 값을 넣어서 없애주세요

  • 게시글 리스트 조회시 파일 목록 반환, isNew 필드 추가, 파일 관련 오류 수정

📸 Screenshots / Logs

필요시 스크린샷 or 로그를 첨부해주세요.

💡 Reviewer 참고사항

리뷰에 참고할 내용을 작성해주세요.
clubMember 쪽은 이미 프사랑 자기소개가 nullable하게 되어있음을 확인했습니다
다른 빠진 부분이 있나 확인해주시면 감사하겠습니당
유진님은 내용 확인하면 그냥 승인 눌러주세오

회의 후 수정사항

nullable은 백엔드 쪽에서는 맞는 처리라서 유지했고, 대신 초기에 사용자 정보를 수정할 때 프로필 완성 여부를 검증해서 예외 던져주는 쪽으로 리팩토링 했습니당. 그래서 최초 1회는 모든 필수 필드를 다 넣어야하고, 그 후에는 PATCH 메서드 특징에 맞게 수정될 필드만 넣어주시면 됩니당. 수정 안하는 필드는 null로!

✅ Checklist

  • PR 제목 설정 완료 (WTH-123 인증 필터 설정)
  • 테스트 구현 완료
  • 리뷰어 등록 완료
  • 자체 코드 리뷰 완료

Summary by CodeRabbit

  • 개선 사항
    • 사용자 프로필 업데이트에서 전화번호·학번·학교·학과 입력이 선택 사항으로 변경되어 부분 수정이 가능합니다.
    • 프로필이 초기 설정 상태인 경우에는 필수 항목을 채워야만 완료할 수 있도록 검증이 적용됩니다.
  • 새 기능
    • 게시글 목록/상세에 신규 표시(isNew)가 추가되어 24시간 이내 게시글을 확인할 수 있습니다.
    • 게시글 응답에 첨부파일 목록(fileUrls)이 제공되어 첨부 파일을 직접 확인할 수 있습니다.

@hyxklee hyxklee requested review from JIN921 and soo0711 April 6, 2026 10:47
@hyxklee hyxklee self-assigned this Apr 6, 2026
@hyxklee hyxklee added the 🔨 Refactor 코드 구조 개선 및 리팩토링 label Apr 6, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

📝 Walkthrough

Walkthrough

사용자 프로필 필드들의 null 허용성 확대, 프로필 업데이트 검증의 조건부화, 게시글 응답에 신규 플래그 및 파일 리스트 추가, 그리고 여러 유스케이스에서 파일 삭제 방식이 soft→hard(일괄 삭제)로 변경되었습니다.

Changes

Cohort / File(s) Summary
Response DTOs (nullable/profile)
src/main/kotlin/com/weeth/domain/club/application/dto/response/ClubMemberProfileResponse.kt, src/main/kotlin/com/weeth/domain/club/application/dto/response/ClubMemberResponse.kt
tel, department, studentId 필드를 String → nullable String?로 변경하여 응답에서 null 허용.
Request DTO (nullable + validation)
src/main/kotlin/com/weeth/domain/user/application/dto/request/UpdateUserProfileRequest.kt
프로필 업데이트 요청 필드들을 String? = null로 변경하고 @field:NotBlank 제거; name@field:Size(max = 20) 추가.
User entity & usecase (nullable fields & conditional validation)
src/main/kotlin/com/weeth/domain/user/domain/entity/User.kt, src/main/kotlin/com/weeth/domain/user/application/usecase/command/UpdateUserProfileUseCase.kt
엔티티의 studentId, tel, school, department를 nullable로 변경(JPA nullable 반영). update/create 시 nullable 파라미터 수용 및 존재할 때만 갱신. 업데이트 유스케이스는 기존의 무조건적 검증을 제거하고 프로필 미완료 시에만 필수 필드 검증을 수행하도록 제어 흐름 변경.
New exception & error code
src/main/kotlin/com/weeth/domain/user/application/exception/ProfileRequiredFieldsMissingException.kt, src/main/kotlin/com/weeth/domain/user/application/exception/UserErrorCode.kt
프로필 초기화 시 필수 필드 누락용 ProfileRequiredFieldsMissingException 추가 및 UserErrorCodePROFILE_REQUIRED_FIELDS_MISSING(20912, BAD_REQUEST) 추가.
Post DTOs & Mapper (isNew, files list)
src/main/kotlin/com/weeth/domain/board/application/dto/response/PostDetailResponse.kt, src/main/kotlin/com/weeth/domain/board/application/dto/response/PostListResponse.kt, src/main/kotlin/com/weeth/domain/board/application/mapper/PostMapper.kt
PostDetail에 isNew: Boolean 추가(24시간 기준). PostList는 hasFile: BooleanfileUrls: List<FileResponse>로 변경. Mapper 시그니처 변경으로 now 인수 사용 및 파일 리스트 전달로 매핑 로직 변경.
Post query service (file mapping refactor)
src/main/kotlin/com/weeth/domain/board/application/usecase/query/GetPostQueryService.kt
중복된 리스트 매핑 로직을 toPostListResponses(...)로 추출하고, 파일 존재 맵 → 파일 리스트 맵으로 전환. 상세 조회에서 현재 시간(now)을 캡처해 Mapper에 전달.
File deletion behavior (soft→hard) across usecases
src/main/kotlin/com/weeth/domain/board/application/usecase/command/ManagePostUseCase.kt, src/main/kotlin/com/weeth/domain/club/application/usecase/command/ManageClubMemberUsecase.kt, src/main/kotlin/com/weeth/domain/club/application/usecase/command/ManageClubUseCase.kt, src/main/kotlin/com/weeth/domain/comment/application/usecase/command/ManageCommentUseCase.kt
여러 유스케이스에서 개별 markDeleted() 호출을 제거하고 fileReader.findAll(...) 결과를 리스트로 수집한 뒤 비어있지 않을 때 fileRepository.deleteAll(files)로 일괄 삭제하도록 변경(soft 삭제 → 물리 삭제). 내부 헬퍼명/호출부 조정.
File entity (removed soft-delete method)
src/main/kotlin/com/weeth/domain/file/domain/entity/File.kt
엔티티의 markDeleted() 메서드 제거. status 필드는 TODO 주석과 함께 유지(추후 DB 마이그레이션 후 제거 예정).
Tests (mocks/asserts updated for new flows)
src/test/kotlin/.../ManagePostUseCaseTest.kt, .../ManageClubUseCaseTest.kt, .../ManageClubMemberUseCaseTest.kt, .../ManageCommentUseCaseTest.kt, .../PostMapperTest.kt, etc.
파일 삭제 방식 변경에 따라 테스트에서 markDeleted() 관련 상태 검사 제거 및 대신 fileRepository.deleteAll(...) 호출 검증/스텁 추가. Post mapper 테스트는 now 인자 및 fileUrls 검증으로 업데이트. 일부 테스트 유틸은 리플렉션으로 상태 설정으로 변경.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant UseCase as UseCase (Manage*UseCase)
    participant FileReader as FileReader
    participant FileRepo as FileRepository
    participant DB as Database

    rect rgba(200,200,255,0.5)
    Client->>UseCase: 요청 (예: update/delete with files)
    UseCase->>FileReader: findAll(ownerType, ownerId, status=UPLOADED)
    FileReader-->>UseCase: List<File> (existingFiles)
    alt existingFiles is not empty
        UseCase->>FileRepo: deleteAll(existingFiles)
        FileRepo->>DB: bulk DELETE
        DB-->>FileRepo: 삭제 결과
        FileRepo-->>UseCase: 완료
    else existingFiles is empty
        UseCase-->>UseCase: 아무 작업 없음
    end
    UseCase->>DB: continue other domain ops (save/update/delete)
    DB-->>UseCase: 결과
    UseCase-->>Client: 응답
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • soo0711
  • dalzzy

Poem

🐰 깡총깡총, 코드 고쳐왔네
널도 받아주고 검증은 골라서 해
파일은 한꺼번에 휙 지워버리고
게시글은 새로움 표시 반짝✨
토끼가 박수 쳐요, 흩날리는 당근

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 유저 필드 nullable 처리라는 주요 변경사항을 명확히 요약하고 있으며, 지라 이슈 번호를 포함하고 있어 식별 가능합니다.
Description check ✅ Passed PR 설명은 필수 섹션(Summary, Changes, Checklist)을 포함하고 있으나, Changes 섹션의 What 부분이 비어있고 일부 내용이 불완전합니다.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/WTH-247-엔티티-필드-nullable-분석-및-처리

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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/kotlin/com/weeth/domain/user/domain/entity/User.kt`:
- Around line 103-120: The update function currently treats null as no-op and
empty strings as stored values; change update(...) so that for String parameters
(name, studentId, school, department) you normalize by trimming and converting
empty results to null (i.e., "" -> null means clear the field). In the update
method: for name, if param is provided then trim; if trimmed is empty set
this.name = null else validate not blank and assign trimmed; for studentId,
school, department, trim and if trimmed.isEmpty() set corresponding property to
null else assign trimmed. Keep existing handling for Email and PhoneNumber types
but apply the same clear-if-empty semantics if their string representation is
used. Reference: function update in User.kt and the fields name, studentId,
school, department.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 95ae84c1-e872-4f0e-91bc-105dbd2d5182

📥 Commits

Reviewing files that changed from the base of the PR and between 8030bdc and a2fe048.

📒 Files selected for processing (5)
  • src/main/kotlin/com/weeth/domain/club/application/dto/response/ClubMemberProfileResponse.kt
  • src/main/kotlin/com/weeth/domain/club/application/dto/response/ClubMemberResponse.kt
  • src/main/kotlin/com/weeth/domain/user/application/dto/request/UpdateUserProfileRequest.kt
  • src/main/kotlin/com/weeth/domain/user/application/usecase/command/UpdateUserProfileUseCase.kt
  • src/main/kotlin/com/weeth/domain/user/domain/entity/User.kt

Comment on lines 103 to 120
fun update(
name: String,
email: Email,
studentId: String,
tel: PhoneNumber,
school: String,
department: String,
name: String? = null,
email: Email? = null,
studentId: String? = null,
tel: PhoneNumber? = null,
school: String? = null,
department: String? = null,
) {
require(name.isNotBlank()) { "이름은 공백일 수 없습니다." }
this.name = name.trim()
this.email = email
this.studentId = studentId
this.tel = tel
this.school = school
this.department = department
name?.let {
require(it.isNotBlank()) { "이름은 공백일 수 없습니다." }
this.name = it.trim()
}
email?.let { this.email = it }
studentId?.let { this.studentId = it }
tel?.let { this.tel = it }
school?.let { this.school = it }
department?.let { this.department = it }
}
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

필드 값 삭제(clearing) 로직 검토 필요

PR 설명에 따르면 빈 문자열("")을 보내면 값을 삭제하는 전략입니다. 하지만 현재 구현에서는:

  • null 전송 → 필드 변경 없음 (.let 스킵)
  • "" 전송 → 필드가 빈 문자열로 설정됨

빈 문자열을 null로 변환하여 저장해야 하는 것이 의도라면, 추가 처리가 필요합니다:

💡 빈 문자열을 null로 변환하는 예시
     fun update(
         name: String? = null,
         email: Email? = null,
         studentId: String? = null,
         tel: PhoneNumber? = null,
         school: String? = null,
         department: String? = null,
     ) {
         name?.let {
             require(it.isNotBlank()) { "이름은 공백일 수 없습니다." }
             this.name = it.trim()
         }
         email?.let { this.email = it }
-        studentId?.let { this.studentId = it }
+        studentId?.let { this.studentId = it.ifBlank { null } }
         tel?.let { this.tel = it }
-        school?.let { this.school = it }
-        department?.let { this.department = it }
+        school?.let { this.school = it.ifBlank { null } }
+        department?.let { this.department = it.ifBlank { null } }
     }

또한, nametrim()을 적용하지만 studentId, school, department는 trim 없이 그대로 저장됩니다. 일관성을 위해 동일한 처리를 고려해 주세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/kotlin/com/weeth/domain/user/domain/entity/User.kt` around lines 103
- 120, The update function currently treats null as no-op and empty strings as
stored values; change update(...) so that for String parameters (name,
studentId, school, department) you normalize by trimming and converting empty
results to null (i.e., "" -> null means clear the field). In the update method:
for name, if param is provided then trim; if trimmed is empty set this.name =
null else validate not blank and assign trimmed; for studentId, school,
department, trim and if trimmed.isEmpty() set corresponding property to null
else assign trimmed. Keep existing handling for Email and PhoneNumber types but
apply the same clear-if-empty semantics if their string representation is used.
Reference: function update in User.kt and the fields name, studentId, school,
department.

Copy link
Copy Markdown
Collaborator

@soo0711 soo0711 left a comment

Choose a reason for hiding this comment

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

다 된 것 같아요! 수고하셨습니당!!

Copy link
Copy Markdown

@JIN921 JIN921 left a comment

Choose a reason for hiding this comment

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

프로필 이미지랑 바이오만 선택이고 나머지는 다 필수 입력사항으로 갑시당

@hyxklee hyxklee requested a review from nabbang6 April 6, 2026 13:54
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: 5

🧹 Nitpick comments (1)
src/main/kotlin/com/weeth/domain/board/application/usecase/command/ManagePostUseCase.kt (1)

157-162: 스토리지 정리 경로가 빠져 있습니다.

이 PR 전반의 deleteAll(files) 패턴은 메타데이터 row만 hard-delete하고 끝납니다. 제공된 File 엔티티 컨텍스트상 스토리지 삭제 hook/port는 보이지 않아서, 교체/삭제가 반복되면 orphan object가 누적될 수 있습니다. 파일 삭제를 파일 도메인의 서비스/포트로 올려서 스토리지 정리와 DB 삭제를 함께 묶는 편이 안전합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/kotlin/com/weeth/domain/board/application/usecase/command/ManagePostUseCase.kt`
around lines 157 - 162, The deletePostFiles function currently calls
fileReader.findAll(FileOwnerType.POST, postId) and then
fileRepository.deleteAll(files), which only hard-deletes DB rows and omits
storage cleanup; change this to call the file-domain service/port (e.g. a
FileService or FileStoragePort) that encapsulates both storage object deletion
and DB metadata removal so orphaned objects are not left behind—replace direct
fileRepository.deleteAll(files) with a single call to that file-domain method
(or iterate files and invoke the port's deleteWithStorage(file) operation) and
ensure File entity storage hooks/ports are used.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/main/kotlin/com/weeth/domain/board/application/dto/response/PostListResponse.kt`:
- Around line 27-28: Tests fail because the PostListResponse field and
PostMapper parameter were renamed from hasFile to fileUrls/files; update tests
to use the new names: change calls to PostMapper.toListResponse(...) to pass
files = ... (instead of hasFile = ...) and assert response.fileUrls (instead of
response.hasFile), and update any direct construction of PostListResponse(...)
to use fileUrls = ... (instead of hasFile = ...); specifically fix occurrences
in PostMapperTest.kt and GetPostQueryServiceTest.kt to reference
PostListResponse.fileUrls and the toListResponse parameter files.

In `@src/main/kotlin/com/weeth/domain/board/application/mapper/PostMapper.kt`:
- Line 46: The isNew calculation in PostMapper uses
post.createdAt.isAfter(now.minusHours(24)) which excludes timestamps exactly 24
hours ago; update both occurrences (the isNew assignments at PostMapper lines
where isNew is set) to include the boundary by using a non-strict comparison
(e.g., replace the isAfter check with a check that treats equality as inside 24
hours, such as using !post.createdAt.isBefore(now.minusHours(24)) or an
equivalent isEqual-or-after approach) so that createdAt exactly 24 hours ago
counts as new.

In
`@src/main/kotlin/com/weeth/domain/club/application/usecase/command/ManageClubMemberUsecase.kt`:
- Around line 95-104: The code currently uses
fileRepository.findAllByOwnerTypeAndOwnerIdAndStatus directly when handling
request.profileImage and in the later block; replace these cross-domain reads
with the FileReader.findAll(...) call (filtering by
FileOwnerType.CLUB_MEMBER_PROFILE and FileStatus.UPLOADED) and extract the
deletion logic into a single helper named deleteExistingProfileFiles(userId)
which calls FileRepository.deleteAll(...) for the found files; update both
places (the branch handling request.profileImage and the other block) to call
FileReader.findAll(...) and then invoke deleteExistingProfileFiles(userId) so
reads use the Reader interface and deletions remain via the repository.

In
`@src/main/kotlin/com/weeth/domain/club/application/usecase/command/ManageClubUseCase.kt`:
- Around line 228-236: The deleteExistingFiles method currently reads files via
fileRepository.findAllByOwnerTypeAndOwnerIdAndStatus(...); change the read to
use the cross-domain reader by calling FileReader.findAll(...) (e.g.,
fileReader.findAll(ownerType, ownerId) or the reader's intended signature) and
keep the delete using FileRepository.deleteAll(files); update the method to
inject/use fileReader (symbol: fileReader) for reads and retain
fileRepository.deleteAll(files) for the write so reads go through FileReader and
writes remain on FileRepository (method: deleteExistingFiles).

In `@src/main/kotlin/com/weeth/domain/file/domain/entity/File.kt`:
- Around line 43-46: Tests still reference the removed markDeleted() method;
update FileTest, FileRepositoryTest, and ManageClubUseCaseTest to stop calling
or asserting via markDeleted()—instead create DELETED fixtures by setting the
status value at construction (e.g., use File's constructor/fixture to set
FileStatus.DELETED) and change verifications that relied on markDeleted() to
assert against fileRepository.deleteAll(...) (or check repository state after
deleteAll) so tests no longer depend on the removed method.

---

Nitpick comments:
In
`@src/main/kotlin/com/weeth/domain/board/application/usecase/command/ManagePostUseCase.kt`:
- Around line 157-162: The deletePostFiles function currently calls
fileReader.findAll(FileOwnerType.POST, postId) and then
fileRepository.deleteAll(files), which only hard-deletes DB rows and omits
storage cleanup; change this to call the file-domain service/port (e.g. a
FileService or FileStoragePort) that encapsulates both storage object deletion
and DB metadata removal so orphaned objects are not left behind—replace direct
fileRepository.deleteAll(files) with a single call to that file-domain method
(or iterate files and invoke the port's deleteWithStorage(file) operation) and
ensure File entity storage hooks/ports are used.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b0cfda5c-12b4-4e70-869a-694943efdacf

📥 Commits

Reviewing files that changed from the base of the PR and between a2fe048 and f1807d8.

📒 Files selected for processing (12)
  • src/main/kotlin/com/weeth/domain/board/application/dto/response/PostDetailResponse.kt
  • src/main/kotlin/com/weeth/domain/board/application/dto/response/PostListResponse.kt
  • src/main/kotlin/com/weeth/domain/board/application/mapper/PostMapper.kt
  • src/main/kotlin/com/weeth/domain/board/application/usecase/command/ManagePostUseCase.kt
  • src/main/kotlin/com/weeth/domain/board/application/usecase/query/GetPostQueryService.kt
  • src/main/kotlin/com/weeth/domain/club/application/usecase/command/ManageClubMemberUsecase.kt
  • src/main/kotlin/com/weeth/domain/club/application/usecase/command/ManageClubUseCase.kt
  • src/main/kotlin/com/weeth/domain/comment/application/usecase/command/ManageCommentUseCase.kt
  • src/main/kotlin/com/weeth/domain/file/domain/entity/File.kt
  • src/main/kotlin/com/weeth/domain/user/application/exception/ProfileRequiredFieldsMissingException.kt
  • src/main/kotlin/com/weeth/domain/user/application/exception/UserErrorCode.kt
  • src/main/kotlin/com/weeth/domain/user/application/usecase/command/UpdateUserProfileUseCase.kt
✅ Files skipped from review due to trivial changes (1)
  • src/main/kotlin/com/weeth/domain/user/application/exception/UserErrorCode.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/kotlin/com/weeth/domain/user/application/usecase/command/UpdateUserProfileUseCase.kt

Comment on lines +43 to 46
// TODO: 하드 딜리트로 전환 완료되어 더 이상 사용되지 않음. DB 마이그레이션 후 status 컬럼 및 FileStatus enum 제거 예정
@Enumerated(EnumType.STRING)
@Column(nullable = false, length = 20)
var status: FileStatus = FileStatus.UPLOADED,
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 | 🔴 Critical

markDeleted() 제거에 맞춰 테스트/픽스처도 같이 정리해 주세요.

제공된 스니펫 기준으로 FileTest, FileRepositoryTest, ManageClubUseCaseTest가 아직 markDeleted()를 호출하거나 검증하고 있습니다. 이 상태면 테스트 소스가 깨지므로, DELETED 픽스처는 생성 시점에 직접 만들고 유스케이스 검증은 fileRepository.deleteAll(...) 기준으로 바꿔 주세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/kotlin/com/weeth/domain/file/domain/entity/File.kt` around lines 43
- 46, Tests still reference the removed markDeleted() method; update FileTest,
FileRepositoryTest, and ManageClubUseCaseTest to stop calling or asserting via
markDeleted()—instead create DELETED fixtures by setting the status value at
construction (e.g., use File's constructor/fixture to set FileStatus.DELETED)
and change verifications that relied on markDeleted() to assert against
fileRepository.deleteAll(...) (or check repository state after deleteAll) so
tests no longer depend on the removed method.

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/test/kotlin/com/weeth/domain/board/application/mapper/PostMapperTest.kt (1)

91-97: ⚠️ Potential issue | 🟡 Minor

toDetailResponse에서 isNew 검증이 빠져 있습니다.

Line 91에서 now 인자를 추가했지만, 결과에서 isNew를 확인하지 않아 신규 로직 회귀를 놓칠 수 있습니다. 현재 픽스처 기준으로 true 검증을 추가해 주세요.

테스트 보강 예시
                 response.id shouldBe 1L
                 response.commentCount shouldBe 2
                 response.comments.size shouldBe 1
                 response.fileUrls.size shouldBe 1
+                response.isNew shouldBe true
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test/kotlin/com/weeth/domain/board/application/mapper/PostMapperTest.kt`
around lines 91 - 97, 테스트에서 mapper.toDetailResponse(post, authorMember,
comments, files, isLiked = false, now = now) 호출 결과에 대해 isNew 검증이 빠져 있습니다;
PostMapperTest.kt의 해당 assertion 블록에서 response.isNew이 true인지 확인하는 단언을 추가하여 신규 로직
회귀를 잡아내도록 수정하세요 (즉, response.id/ commentCount/ comments.size/ fileUrls.size 검증들
옆에 response.isNew shouldBe true 추가).
src/test/kotlin/com/weeth/domain/board/application/usecase/query/GetPostQueryServiceTest.kt (1)

146-165: ⚠️ Potential issue | 🟡 Minor

findPost 성공 케이스에서 isNew 검증을 추가해 주세요.

Line 146에서 isNew를 응답 fixture에 추가했지만 결과 검증에는 포함되지 않아, 필드 누락/역직렬화 회귀를 잡기 어렵습니다.

테스트 보강 예시
                 result.id shouldBe 1L
                 result.comments.size shouldBe 1
                 result.fileUrls.size shouldBe 1
+                result.isNew shouldBe false
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/kotlin/com/weeth/domain/board/application/usecase/query/GetPostQueryServiceTest.kt`
around lines 146 - 165, The test sets the response fixture's isNew field (isNew
= false) but never asserts it; update the findPost success test (calling
queryService.findPost) to include an assertion on the returned result's isNew
field (e.g., result.isNew shouldBe false) so that
serialization/deserialization/regression of the isNew property is validated;
locate this in GetPostQueryServiceTest where result is assigned after
queryService.findPost and add the isNew assertion alongside the existing
id/comments/fileUrls checks.
🧹 Nitpick comments (3)
src/test/kotlin/com/weeth/domain/file/domain/repository/FileRepositoryTest.kt (1)

17-17: 리플렉션 대신 타입 안전한 프로퍼티 할당을 사용해 주세요.

Line 106의 ReflectionTestUtils.setField(...)는 필드명 문자열에 의존해서 리네임 시 런타임에만 깨집니다. 현재 구조에서는 status 직접 할당이 더 안전하고 단순합니다.

변경 제안
-import org.springframework.test.util.ReflectionTestUtils
@@
-                ReflectionTestUtils.setField(it, "status", FileStatus.DELETED)
+                it.status = FileStatus.DELETED

Also applies to: 106-106

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/kotlin/com/weeth/domain/file/domain/repository/FileRepositoryTest.kt`
at line 17, FileRepositoryTest uses ReflectionTestUtils.setField(...) to assign
the 'status' field which is fragile; replace the reflective assignment with a
direct, type-safe property assignment to the test subject's status property
(e.g., set the status field directly on the File entity or test object instead
of calling ReflectionTestUtils.setField). Locate usages of
ReflectionTestUtils.setField in FileRepositoryTest and change them to direct
assignments to the 'status' property on the appropriate instance (keeping
existing visibility by adjusting test setup if necessary).
src/test/kotlin/com/weeth/domain/board/application/usecase/command/ManagePostUseCaseTest.kt (1)

261-270: 파일 교체 테스트에 호출 순서 검증을 추가하면 더 안전합니다.

현재는 호출 횟수만 검증하므로, saveAll이 먼저 호출되는 회귀를 잡지 못합니다. 교체 의미를 보장하려면 순서까지 고정하는 편이 좋습니다.

검증 보강 예시
-                verify(exactly = 1) { fileRepository.deleteAll(listOf(oldFile)) }
-                verify(exactly = 1) { fileRepository.saveAll(newFiles) }
+                verifyOrder {
+                    fileRepository.deleteAll(listOf(oldFile))
+                    fileRepository.saveAll(newFiles)
+                }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/kotlin/com/weeth/domain/board/application/usecase/command/ManagePostUseCaseTest.kt`
around lines 261 - 270, The test currently only verifies invocation counts for
fileRepository.deleteAll and fileRepository.saveAll after calling
ManagePostUseCase.update, so a regression where saveAll runs before deleteAll
could slip through; modify the ManagePostUseCaseTest (in the test method that
calls useCase.update(clubId, 1L, request, 1L)) to assert the call order by using
mockk's verifyOrder (or verifySequence) to ensure
fileRepository.deleteAll(listOf(oldFile)) is invoked before
fileRepository.saveAll(newFiles), keeping the existing count verifications and
expectations for fileMapper.toFileList and fileRepository.saveAll.
src/test/kotlin/com/weeth/domain/club/application/usecase/command/ManageClubUseCaseTest.kt (1)

332-333: 중복된 파일 삭제 목킹 패턴은 헬퍼로 추출하면 유지보수가 더 쉬워집니다.

세 곳에서 같은 스텁 구성이 반복되어, ownerType만 받는 테스트 헬퍼로 줄이면 가독성과 변경 내성이 좋아집니다.

♻️ 리팩터 예시
+        fun stubUploadedFileDeletion(ownerType: FileOwnerType, ownerId: Long, existingFile: File) {
+            every {
+                fileRepository.findAllByOwnerTypeAndOwnerIdAndStatus(
+                    ownerType,
+                    ownerId,
+                    FileStatus.UPLOADED,
+                )
+            } returns listOf(existingFile)
+            every { fileRepository.deleteAll(any<List<File>>()) } just Runs
+        }

Also applies to: 450-451, 510-511

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/test/kotlin/com/weeth/domain/club/application/usecase/command/ManageClubUseCaseTest.kt`
around lines 332 - 333, Extract the repeated mocking of fileRepository.deleteAll
into a single test helper to improve maintainability: create a helper method
(e.g., stubDeleteAllFiles(ownerType: OwnerType)) inside ManageClubUseCaseTest
that performs the repeated stubbing every {
fileRepository.deleteAll(any<List<File>>()) } just Runs (and if needed
parameterize the matcher or setup by ownerType), then replace the three
duplicated stubs at the locations referencing fileRepository.deleteAll with
calls to stubDeleteAllFiles(ownerType) to centralize the behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/test/kotlin/com/weeth/domain/board/application/mapper/PostMapperTest.kt`:
- Around line 91-97: 테스트에서 mapper.toDetailResponse(post, authorMember, comments,
files, isLiked = false, now = now) 호출 결과에 대해 isNew 검증이 빠져 있습니다;
PostMapperTest.kt의 해당 assertion 블록에서 response.isNew이 true인지 확인하는 단언을 추가하여 신규 로직
회귀를 잡아내도록 수정하세요 (즉, response.id/ commentCount/ comments.size/ fileUrls.size 검증들
옆에 response.isNew shouldBe true 추가).

In
`@src/test/kotlin/com/weeth/domain/board/application/usecase/query/GetPostQueryServiceTest.kt`:
- Around line 146-165: The test sets the response fixture's isNew field (isNew =
false) but never asserts it; update the findPost success test (calling
queryService.findPost) to include an assertion on the returned result's isNew
field (e.g., result.isNew shouldBe false) so that
serialization/deserialization/regression of the isNew property is validated;
locate this in GetPostQueryServiceTest where result is assigned after
queryService.findPost and add the isNew assertion alongside the existing
id/comments/fileUrls checks.

---

Nitpick comments:
In
`@src/test/kotlin/com/weeth/domain/board/application/usecase/command/ManagePostUseCaseTest.kt`:
- Around line 261-270: The test currently only verifies invocation counts for
fileRepository.deleteAll and fileRepository.saveAll after calling
ManagePostUseCase.update, so a regression where saveAll runs before deleteAll
could slip through; modify the ManagePostUseCaseTest (in the test method that
calls useCase.update(clubId, 1L, request, 1L)) to assert the call order by using
mockk's verifyOrder (or verifySequence) to ensure
fileRepository.deleteAll(listOf(oldFile)) is invoked before
fileRepository.saveAll(newFiles), keeping the existing count verifications and
expectations for fileMapper.toFileList and fileRepository.saveAll.

In
`@src/test/kotlin/com/weeth/domain/club/application/usecase/command/ManageClubUseCaseTest.kt`:
- Around line 332-333: Extract the repeated mocking of fileRepository.deleteAll
into a single test helper to improve maintainability: create a helper method
(e.g., stubDeleteAllFiles(ownerType: OwnerType)) inside ManageClubUseCaseTest
that performs the repeated stubbing every {
fileRepository.deleteAll(any<List<File>>()) } just Runs (and if needed
parameterize the matcher or setup by ownerType), then replace the three
duplicated stubs at the locations referencing fileRepository.deleteAll with
calls to stubDeleteAllFiles(ownerType) to centralize the behavior.

In
`@src/test/kotlin/com/weeth/domain/file/domain/repository/FileRepositoryTest.kt`:
- Line 17: FileRepositoryTest uses ReflectionTestUtils.setField(...) to assign
the 'status' field which is fragile; replace the reflective assignment with a
direct, type-safe property assignment to the test subject's status property
(e.g., set the status field directly on the File entity or test object instead
of calling ReflectionTestUtils.setField). Locate usages of
ReflectionTestUtils.setField in FileRepositoryTest and change them to direct
assignments to the 'status' property on the appropriate instance (keeping
existing visibility by adjusting test setup if necessary).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2683c7c6-0852-4cf3-97f4-90707cd35226

📥 Commits

Reviewing files that changed from the base of the PR and between f1807d8 and 4d7d8e1.

📒 Files selected for processing (8)
  • src/test/kotlin/com/weeth/domain/board/application/mapper/PostMapperTest.kt
  • src/test/kotlin/com/weeth/domain/board/application/usecase/command/ManagePostUseCaseTest.kt
  • src/test/kotlin/com/weeth/domain/board/application/usecase/query/GetPostQueryServiceTest.kt
  • src/test/kotlin/com/weeth/domain/club/application/usecase/command/ManageClubMemberUseCaseTest.kt
  • src/test/kotlin/com/weeth/domain/club/application/usecase/command/ManageClubUseCaseTest.kt
  • src/test/kotlin/com/weeth/domain/comment/application/usecase/command/ManageCommentUseCaseTest.kt
  • src/test/kotlin/com/weeth/domain/file/domain/entity/FileTest.kt
  • src/test/kotlin/com/weeth/domain/file/domain/repository/FileRepositoryTest.kt
💤 Files with no reviewable changes (1)
  • src/test/kotlin/com/weeth/domain/file/domain/entity/FileTest.kt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 Refactor 코드 구조 개선 및 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants