Skip to content

Refactor: TDA 원칙 적용 - #240

Merged
jjh75607 merged 5 commits into
devfrom
refactor/#239
May 28, 2026
Merged

Refactor: TDA 원칙 적용#240
jjh75607 merged 5 commits into
devfrom
refactor/#239

Conversation

@jjh75607

@jjh75607 jjh75607 commented May 28, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

릴리스 노트

  • Refactor

    • 푸드 및 멤버 엔티티의 검증 로직을 개선하여 더욱 효율적인 데이터 처리 구현
    • 카테고리 및 이미지 변경 감지 로직 최적화로 불필요한 조회 감소
    • 냉장고 멤버십 권한 검증 기능 강화
  • Tests

    • 푸드, 멤버, 냉장고 멤버십의 핵심 검증 로직에 대한 단위 테스트 추가
    • 알림 스케줄 만료 기준일 계산에 대한 파라미터화 테스트 추가

Review Change Stack

jjh75607 and others added 4 commits May 28, 2026 15:44
…ve 추가

OWNER 권한 검증 로직을 Facade/Service에서 엔티티로 이동하여
동일 검증의 중복 제거 및 TDA 원칙 적용

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…m 추가

이미지 유무·변경 여부 및 카테고리 변경 여부 판단을 Service에서
엔티티로 이동하여 TDA 원칙 적용

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
프로필 이미지 변경 여부 판단을 MemberService에서 엔티티로 이동하여
TDA 원칙 적용

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
알림 기준 날짜 계산 책임을 NotificationProcessor에서 엔티티로 이동하여
TDA 원칙 적용

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jjh75607 jjh75607 self-assigned this May 28, 2026
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@jjh75607, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 45 minutes. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 279075b7-b48a-429d-b8fd-32f7dc6cf2c7

📥 Commits

Reviewing files that changed from the base of the PR and between d9dbc63 and 074fec7.

📒 Files selected for processing (6)
  • src/main/java/soon/fridgely/domain/food/service/FoodService.java
  • src/main/java/soon/fridgely/domain/notification/entity/AlertSchedule.java
  • src/test/java/soon/fridgely/domain/food/entity/FoodUnitTest.java
  • src/test/java/soon/fridgely/domain/member/entity/MemberUnitTest.java
  • src/test/java/soon/fridgely/domain/notification/entity/AlertScheduleTest.java
  • src/test/java/soon/fridgely/domain/refrigerator/entity/MemberRefrigeratorUnitTest.java
📝 Walkthrough

Walkthrough

이 PR은 Food, Member, MemberRefrigerator, AlertSchedule 엔티티에 상태/권한/날짜 판별 메서드를 추가하고, 서비스 계층의 검증/계산 로직을 이들 메서드로 위임하는 리팩터링입니다. 엔티티가 자신의 상태를 판별하도록 책임을 이전하고, 서비스의 조건 분기를 단순화하며, 모든 새 메서드에 대한 단위 테스트를 추가했습니다.

Changes

Food 엔티티 상태 판별 및 서비스 통합

Layer / File(s) Summary
Food 엔티티 상태 판별 메서드 추가
src/main/java/soon/fridgely/domain/food/entity/Food.java
isCategoryDifferent(long), hasImage(), isImageChangedFrom(String) 공개 메서드를 추가하여 카테고리/이미지 변경 여부를 판별합니다. StringUtils와 Objects 의존을 추가합니다.
FoodService 카테고리/이미지 변경 감지 리팩터링
src/main/java/soon/fridgely/domain/food/service/FoodService.java
updateFood에서 카테고리 변경 감지를 food.isCategoryDifferent(categoryId), 이미지 삭제 이벤트 발행 조건을 food.isImageChangedFrom(newImageUrl)로 처리하도록 변경합니다. 기존 helper 메서드와 import가 정리됩니다.
Food 상태 판별 메서드 단위 테스트
src/test/java/soon/fridgely/domain/food/entity/FoodUnitTest.java
isCategoryDifferent, hasImage, isImageChangedFrom 메서드의 동작을 검증하는 테스트 케이스를 추가합니다(카테고리 일치/불일치, 이미지 유무, 변경 여부 조합).

Member 프로필 이미지 상태 판별 및 서비스 통합

Layer / File(s) Summary
Member 프로필 이미지 변경 판별 메서드 추가
src/main/java/soon/fridgely/domain/member/entity/Member.java
isProfileImageChangedTo(String newUrl) 공개 메서드를 추가하여 profileImageUrl이 유효한 텍스트일 때만 새 URL과의 불일치 여부를 반환합니다. StringUtils import를 추가합니다.
MemberService 프로필 이미지 업데이트 단순화
src/main/java/soon/fridgely/domain/member/service/MemberService.java
updateProfileImage에서 이미지 변경 감지를 member.isProfileImageChangedTo(newImageUrl)로 처리하고, 이미지 삭제 이벤트 발행 흐름을 정리합니다. StringUtils import가 제거됩니다.
Member 프로필 이미지 변경 판별 단위 테스트
src/test/java/soon/fridgely/domain/member/entity/MemberUnitTest.java
isProfileImageChangedTo 메서드를 4가지 케이스(다름/같음/null/빈 문자열)로 검증하는 단위 테스트를 추가합니다.

AlertSchedule 만료 기준일 계산 추출 및 NotificationProcessor 통합

Layer / File(s) Summary
AlertSchedule 만료 기준일 계산 메서드 추가
src/main/java/soon/fridgely/domain/notification/entity/AlertSchedule.java
getExpirationTargetDate(LocalDate now) 공개 메서드를 추가하여 현재 날짜에 daysBeforeExpiration을 더한 날짜를 반환합니다. LocalDate import를 추가합니다.
NotificationProcessor 만료 날짜 계산 단순화
src/main/java/soon/fridgely/domain/notification/service/NotificationProcessor.java
processExpiration에서 만료 기준일 계산을 setting.getAlertSchedule().getExpirationTargetDate(LocalDate.now())로 처리하도록 변경합니다. 기존 plusDays 로직이 AlertSchedule으로 이동합니다.
AlertSchedule 만료 기준일 계산 파라미터화 테스트
src/test/java/soon/fridgely/domain/notification/entity/AlertScheduleTest.java
getExpirationTargetDate 메서드를 다양한 (현재 날짜, 일수) 조합으로 검증하는 @ParameterizedTest를 추가합니다.

MemberRefrigerator 권한 검증 메서드 추출 및 서비스 통합

Layer / File(s) Summary
MemberRefrigerator 권한 검증 메서드 추가
src/main/java/soon/fridgely/domain/refrigerator/entity/MemberRefrigerator.java
validateOwnership()validateCanLeave() 공개 메서드를 추가합니다. validateOwnership은 OWNER가 아니면 ONLY_OWNER_CAN_DELETE_REFRIGERATOR 오류를 던지고, validateCanLeave는 OWNER이면 OWNER_CANNOT_LEAVE_REFRIGERATOR 오류를 던집니다. CoreException과 ErrorType import를 추가합니다.
RefrigeratorFacade 소유권 검증 위임
src/main/java/soon/fridgely/domain/refrigerator/service/RefrigeratorFacade.java
deleteRefrigerator에서 소유권 검증을 memberRefrigerator.validateOwnership()으로 위임합니다. 기존 inline 검증 로직과 exception throw가 제거되고, CoreException/ErrorType import가 정리됩니다.
RefrigeratorService 퇴장 권한 검증 위임
src/main/java/soon/fridgely/domain/refrigerator/service/RefrigeratorService.java
leaveRefrigerator에서 퇴장 권한 검증을 memberRefrigerator.validateCanLeave()로 위임합니다. 기존 OWNER 여부 검사 로직이 제거됩니다.
MemberRefrigerator 권한 검증 메서드 단위 테스트
src/test/java/soon/fridgely/domain/refrigerator/entity/MemberRefrigeratorUnitTest.java
validateOwnershipvalidateCanLeave 메서드를 역할(OWNER/MEMBER)에 따른 예외 발생 조건과 errorType 값으로 검증하는 단위 테스트를 추가합니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • Refactor: TDA 원칙 적용 #239: 이 PR의 주요 변경사항(MemberRefrigerator 검증 메서드 추가, Food 상태 유틸 메서드, Member 이미지 변경 로직, AlertSchedule 날짜 계산)이 해당 이슈에 나열된 TDA 리팩터링 작업을 직접 구현하고 있습니다.

Possibly related PRs

  • Fridgely/Back-End#238: 동일하게 NotificationProcessor.processExpiration(...)을 수정하여 만료 대상 날짜 계산 및 조회 흐름을 변경하므로 코드 레벨에서 직접적인 변경이 겹칩니다.
  • Fridgely/Back-End#218: 두 PR 모두 RefrigeratorFacade.deleteRefrigerator(...) 영역을 수정하며(검색 PR: 소유자 체크를 포함한 삭제 로직 도입, 메인 PR: 그 체크를 MemberRefrigerator.validateOwnership() 위임으로 변경), 코드 레벨에서 직접적으로 연결됩니다.
  • Fridgely/Back-End#216: 메인 PR의 FoodService.updateFood(...)에서 카테고리/이미지 변경 감지 로직을 Food의 새 판별 메서드로 단순화하고 이미지 삭제 이벤트 조건을 조정하는 변경이, 검색 PR #216의 Food 도메인 Service 전환 과정에서 FoodService로 이미지/카테고리 갱신·삭제 처리 로직이 재구성된 흐름과 동일 코드 영역을 다루므로 연관됩니다.

Poem

🐰 엔티티여, 제 모습을 아세요~
카테고리 다르면 말해주고,
이미지 바뀌면 알려주고,
권한 없으면 거부하세요!
서비스는 이제 편하게,
당신에게 묻기만 하면 돼요~ 🥕✨

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 TDA(Tell-Don't-Ask) 원칙 적용이라는 리팩토링의 핵심 목적을 명확하게 전달하며, 풀 리퀘스트의 모든 변경사항(엔티티로의 책임 이동)을 대표하는 주요 변경점을 정확히 요약하고 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/#239

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.

@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: 4

🧹 Nitpick comments (2)
src/test/java/soon/fridgely/domain/food/entity/FoodUnitTest.java (1)

275-360: ⚡ Quick win

신규 테스트가 테스트 네이밍/BDD 주석 규칙을 일부 위반하고 있습니다.

메서드명이 구현 메서드명 중심(isCategoryDifferent 등)으로 작성되어 정책/현상 중심 서술 규칙과 어긋나고, // when & then 결합 주석도 BDD 구분 규칙과 다릅니다. // when, // then을 분리하고, 테스트명은 도메인 행위/결과 문장으로 바꿔주세요.

As per coding guidelines src/test/java/**/*.java: BDD 기반: // given, // when, // then 주석으로 구분한다. 그리고 도메인 정책·현상 중심으로 기술한다. 메서드 구현 관점 금지.

🤖 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/test/java/soon/fridgely/domain/food/entity/FoodUnitTest.java` around
lines 275 - 360, Rename the test methods in FoodUnitTest to describe the domain
behavior/result (avoid referencing implementation method names like
isCategoryDifferent, hasImage, isImageChangedFrom) — e.g.
"카테고리가_다르면_카테고리_변경_판단이_true를_반환한다" — and update each test's in-body BDD comments
by splitting the combined "// when & then" into two separate lines "// when" and
"// then"; keep references to the existing symbols (isCategoryDifferent,
hasImage, isImageChangedFrom) only inside assertions, not in the method names or
comments.
src/test/java/soon/fridgely/domain/notification/entity/AlertScheduleTest.java (1)

79-94: ⚡ Quick win

null 입력 케이스 테스트 추가를 고려하세요.

현재 테스트는 정상 시나리오만 검증합니다. getExpirationTargetDate에 null 검증이 추가되면, 다른 검증 메서드 테스트(lines 48-77)와 동일한 패턴으로 예외 케이스도 테스트하는 것이 좋습니다.

💚 null 케이스 테스트 추가 제안
+    `@Test`
+    void 기준일이_null이면_예외가_발생한다() {
+        // given
+        AlertSchedule schedule = AlertSchedule.of(LocalTime.of(9, 0), 3);
+
+        // expected
+        assertThatThrownBy(() -> schedule.getExpirationTargetDate(null))
+            .isInstanceOf(CoreException.class)
+            .hasMessageContaining("기준일은 null일 수 없습니다.")
+            .extracting("errorType")
+            .isEqualTo(ErrorType.INVALID_REQUEST);
+    }
🤖 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/test/java/soon/fridgely/domain/notification/entity/AlertScheduleTest.java`
around lines 79 - 94, Add a test covering the null input case for
AlertSchedule.getExpirationTargetDate similar to the existing null-validation
tests: create a new unit test (or extend the parameterized test) that calls
AlertSchedule.of(LocalTime.of(9,0),
daysBeforeExpiration).getExpirationTargetDate(null) and asserts the expected
exception (e.g., NullPointerException or the domain-specific exception) is
thrown; use the same assertion pattern as the other null-validation tests to
ensure consistent behavior and messaging.
🤖 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/soon/fridgely/domain/food/service/FoodService.java`:
- Around line 71-73: The code currently treats an empty-string newImageUrl as a
change (triggering ImageDeleteEvent) but later ignores blank URLs when calling
food.update(...), causing DB/entity to keep the old URL while the file was
deleted; fix by normalizing newImageUrl once (e.g., String normalizedNewImage =
(newImageUrl == null || newImageUrl.trim().isEmpty()) ? null : newImageUrl), use
normalizedNewImage when calling food.isImageChangedFrom(...) and when invoking
food.update(...), and only publish new ImageDeleteEvent(food.getImageURL()) if
normalizedNewImage is different from the existing URL (i.e., when
isImageChangedFrom(normalizedNewImage) returns true).

In `@src/main/java/soon/fridgely/domain/notification/entity/AlertSchedule.java`:
- Around line 45-47: In AlertSchedule.getExpirationTargetDate(LocalDate now) add
a defensive null check for the now parameter before calling now.plusDays(...);
for example use Objects.requireNonNull(now, "now must not be null") (or throw an
IllegalArgumentException) at the top of the method so a clear exception is
raised instead of an NPE when now is null while the rest of the logic (return
now.plusDays(this.daysBeforeExpiration)) remains unchanged.

In `@src/test/java/soon/fridgely/domain/member/entity/MemberUnitTest.java`:
- Around line 20-21: Split the combined "// when & then" BDD comment into
separate "// when" and "// then" comments around the action and the assertion in
the MemberUnitTest, e.g., place "// when" immediately above the call that
triggers behavior (the call to member.isProfileImageChangedTo(...)) and "//
then" immediately above the assertion that checks the result (the
assertThat(...) line); apply the same change for the other occurrences noted
(the blocks around lines with member.isProfileImageChangedTo and their
corresponding assertThat calls at the other locations).

In
`@src/test/java/soon/fridgely/domain/refrigerator/entity/MemberRefrigeratorUnitTest.java`:
- Around line 28-66: The test methods that reference implementation names
(OWNER는_validateOwnership을_호출해도_예외가_발생하지_않는다,
MEMBER가_validateOwnership을_호출하면_예외가_발생한다,
MEMBER는_validateCanLeave를_호출해도_예외가_발생하지_않는다,
OWNER가_validateCanLeave를_호출하면_예외가_발생한다) should be renamed to
domain/policy-centric names (e.g., OWNER_cannot_be_deleted_by_non_owner →
ownerCanDeleteRefrigerator / memberCannotDeleteRefrigerator style in your Korean
naming convention) and the BDD comments must follow // given, // when, // then
for normal flows and use // expected only for exception assertions; locate the
tests that call MemberRefrigerator.link(...) and invoke
MemberRefrigerator.validateOwnership or MemberRefrigerator.validateCanLeave and
update the method names and comment blocks accordingly so non-exception tests
contain explicit // when and // then sections while exception tests keep //
expected.

---

Nitpick comments:
In `@src/test/java/soon/fridgely/domain/food/entity/FoodUnitTest.java`:
- Around line 275-360: Rename the test methods in FoodUnitTest to describe the
domain behavior/result (avoid referencing implementation method names like
isCategoryDifferent, hasImage, isImageChangedFrom) — e.g.
"카테고리가_다르면_카테고리_변경_판단이_true를_반환한다" — and update each test's in-body BDD comments
by splitting the combined "// when & then" into two separate lines "// when" and
"// then"; keep references to the existing symbols (isCategoryDifferent,
hasImage, isImageChangedFrom) only inside assertions, not in the method names or
comments.

In
`@src/test/java/soon/fridgely/domain/notification/entity/AlertScheduleTest.java`:
- Around line 79-94: Add a test covering the null input case for
AlertSchedule.getExpirationTargetDate similar to the existing null-validation
tests: create a new unit test (or extend the parameterized test) that calls
AlertSchedule.of(LocalTime.of(9,0),
daysBeforeExpiration).getExpirationTargetDate(null) and asserts the expected
exception (e.g., NullPointerException or the domain-specific exception) is
thrown; use the same assertion pattern as the other null-validation tests to
ensure consistent behavior and messaging.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2cfb334b-03b4-42a1-a318-1b330189ed82

📥 Commits

Reviewing files that changed from the base of the PR and between 37f1b9b and d9dbc63.

📒 Files selected for processing (13)
  • src/main/java/soon/fridgely/domain/food/entity/Food.java
  • src/main/java/soon/fridgely/domain/food/service/FoodService.java
  • src/main/java/soon/fridgely/domain/member/entity/Member.java
  • src/main/java/soon/fridgely/domain/member/service/MemberService.java
  • src/main/java/soon/fridgely/domain/notification/entity/AlertSchedule.java
  • src/main/java/soon/fridgely/domain/notification/service/NotificationProcessor.java
  • src/main/java/soon/fridgely/domain/refrigerator/entity/MemberRefrigerator.java
  • src/main/java/soon/fridgely/domain/refrigerator/service/RefrigeratorFacade.java
  • src/main/java/soon/fridgely/domain/refrigerator/service/RefrigeratorService.java
  • src/test/java/soon/fridgely/domain/food/entity/FoodUnitTest.java
  • src/test/java/soon/fridgely/domain/member/entity/MemberUnitTest.java
  • src/test/java/soon/fridgely/domain/notification/entity/AlertScheduleTest.java
  • src/test/java/soon/fridgely/domain/refrigerator/entity/MemberRefrigeratorUnitTest.java

Comment thread src/main/java/soon/fridgely/domain/food/service/FoodService.java
Comment thread src/test/java/soon/fridgely/domain/member/entity/MemberUnitTest.java Outdated
- FoodService: 빈 문자열 imageURL을 null로 정규화하여 파일 삭제 후
  DB는 이전 URL이 남는 데이터 불일치 버그 수정
- AlertSchedule.getExpirationTargetDate: null 가드 추가
- 테스트 메서드명을 구현 메서드명이 아닌 도메인 행위 중심으로 변경
- BDD 주석 스타일 통일 (when & then 분리)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jjh75607
jjh75607 merged commit a4679ae into dev May 28, 2026
2 checks passed
@jjh75607
jjh75607 deleted the refactor/#239 branch May 28, 2026 07:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor: TDA 원칙 적용

1 participant