Skip to content

Conversation

@lvalentine6
Copy link
Member

@lvalentine6 lvalentine6 commented Dec 14, 2025

✨ 개요

  • 응원 등록 시 임시 이미지 복사와 DB 저장이 하나의 트랜잭션에 포함되어 있던 구조를 개선했습니다.
  • 이미지 이동 로직을 DB 트랜잭션에서 분리하고, 응원 등록 유스케이스 전반을 Facade 계층에서 조합하도록 리팩터링했습니다.
  • 응원 생성 → 이미지 이동 → 이미지 메타데이터 저장 단계를 명확히 분리하여 책임을 정리했습니다.
  • 이미지 이동 처리는 비동기 보상 트랜잭션 구현 시 별도의 상태 저장이 필요하므로, 현재 트래픽 규모를 고려해 복잡도를 낮춘 동기 방식으로 전환했습니다.
  • 이미지 이동 실패, DB 저장 실패 등 중간 단계 오류 발생 시 응원 데이터와 이동된 파일을 모두 정리하는 보상 트랜잭션을 추가했습니다.
  • 이를 통해 외부 I/O(S3) 실패 상황에서도 응원 데이터 정합성을 보장할 수 있도록 개선했습니다.
  • 변경된 구조에 맞춰 응원 등록 흐름에 대한 Facade 테스트와 실패 시나리오 테스트를 추가했습니다.

🧾 관련 이슈

Close #213

🔍 참고 사항 (선택)

Summary by CodeRabbit

  • 새로운 기능

    • 응원을 생성하는 신규 등록 플로우(Facade) 도입으로 등록 안정성 향상
    • 응원 생성 결과를 반환하는 간단한 결과 객체 추가
    • 특정 응원을 조회/삭제하는 공개 API 추가
  • 개선 사항

    • 이미지 업로드/이동 신뢰성 향상 및 장애 시 자동 롤백/정리
    • 업로드 실패로 인한 임시 파일 누수 방지
  • 버그 픽스

    • 응원을 찾을 수 없을 때 전용 오류(CHE005) 추가
  • 테스트

    • 등록 플로우와 롤백 동작을 검증하는 통합 테스트 추가

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

@coderabbitai
Copy link

coderabbitai bot commented Dec 14, 2025

Walkthrough

Cheer 등록 흐름을 CheerRegisterFacade로 분리하고, FileClient의 비동기 파일 이동을 동기 단일 스레드로 전환했으며 이미지 이동 실패 시 파일 및 DB 롤백(보상 트랜잭션)을 추가했습니다. 서비스는 CheerCreationResult 반환으로 분리되고 테스트 인프라가 업데이트되었습니다.

Changes

응집도 / 파일(s) 변경 요약
파일 시스템 관리
src/main/java/eatda/client/file/FileClient.java
비동기 CompletableFuture 흐름 제거 후 동기 단일 스레드로 파일 이동 구현, PATH_DELIMITER 상수 추가, public void deleteFiles(List<String> keys) 추가, S3 이동 실패 시 이동된 키 삭제로 롤백하고 BusinessException(FAIL_TEMP_IMAGE_PROCESS) 발생 처리 및 SdkException 로깅.
응원 등록 파사드 + 결과 DTO
src/main/java/eatda/facade/CheerRegisterFacade.java, src/main/java/eatda/facade/CheerCreationResult.java
CheerRegisterFacade 추가: Cheer 생성 위임, 이미지 이동(동기), 실패 시 파일·DB 보상(롤백) 수행. CheerCreationResult record 추가(cheer, store).
응원 서비스 리팩토링
src/main/java/eatda/service/cheer/CheerService.java
registerCheercreateCheer로 변경하여 CheerCreationResult 반환. 이미지 이동 로직 제거, 대신 saveCheerImages, deleteCheer, getCheerResponse 등 이미지 관련 퍼시스트 책임 분리. 정렬 속성 상수화(SORTED_PROPERTIES).
컨트롤러 및 응답 DTO
src/main/java/eatda/controller/cheer/CheerController.java, src/main/java/eatda/controller/cheer/CheerResponse.java
CheerControllerCheerRegisterFacade를 주입·사용하도록 변경. CheerResponse 생성자에서 Store 파라미터 제거하고 cheer에서 storeId를 추출하도록 수정.
에러 코드
src/main/java/eatda/exception/BusinessErrorCode.java
CHEER_NOT_FOUND("CHE005", "응원을 찾을 수 없습니다.", HttpStatus.NOT_FOUND) 상수 추가.
테스트 인프라 및 테스트 케이스
src/test/java/eatda/document/BaseDocumentTest.java, src/test/java/eatda/facade/BaseFacadeTest.java, src/test/java/eatda/facade/cheer/CheerRegisterFacadeTest.java, src/test/java/eatda/service/cheer/CheerServiceTest.java, src/test/java/eatda/document/cheer/CheerDocumentTest.java
테스트 베이스에 CheerRegisterFacade 목 추가; BaseFacadeTest 신규 추가로 통합 의존성 설정. CheerRegisterFacadeTest로 파사드 시나리오(성공/부분 실패/롤백/이미지 없음 등) 추가. 기존 테스트들은 cheerRegisterFacade 사용 및 CheerCreationResult 기반으로 검증 로직 수정.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant CheerController
    participant CheerRegisterFacade
    participant CheerService
    participant FileClient
    participant Repository

    Client->>CheerController: POST /cheers (request)
    CheerController->>CheerRegisterFacade: registerCheer(request, result, memberId, domain)
    CheerRegisterFacade->>CheerService: createCheer(request, result, memberId)
    CheerService->>Repository: save(cheer)
    CheerService-->>CheerRegisterFacade: CheerCreationResult

    rect rgb(200,220,255)
        Note over CheerRegisterFacade,FileClient: 동기 이미지 이동 처리
        CheerRegisterFacade->>FileClient: moveTempFilesToPermanent(tempKeys)
        FileClient-->>CheerRegisterFacade: movedKeys / exception
    end

    alt 이동 성공
        CheerRegisterFacade->>CheerService: saveCheerImages(cheerId, images, movedKeys)
        CheerService->>Repository: save(cheerImages)
        CheerRegisterFacade-->>CheerController: CheerResponse
        CheerController->>Client: 200 OK
    else 이동 중 예외
        rect rgb(255,200,200)
            Note over CheerRegisterFacade,FileClient: 롤백: 삭제 및 DB 정리
            CheerRegisterFacade->>FileClient: deleteFiles(movedKeys)
            CheerRegisterFacade->>CheerService: deleteCheer(cheerId)
            CheerService->>Repository: delete(cheer)
        end
        CheerRegisterFacade-->>CheerController: throw BusinessException(FAIL_TEMP_IMAGE_PROCESS)
        CheerController->>Client: Error Response
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • 집중 검토 대상:
    • CheerRegisterFacade의 예외 처리·보상 트랜잭션 순서 및 경계
    • FileClient의 동기 이동/삭제(rollback) 경로와 SdkException 처리 및 로그/예외 메시지 일관성
    • CheerService.createCheer vs saveCheerImages 책임 분리와 트랜잭션 범위
    • CheerResponse 생성자 변경에 따른 cheer.getStore() 널 안정성 검증
    • 신규/수정된 통합 테스트(CheerRegisterFacadeTest)의 모킹이 실제 예외·부분 성공 경로를 정확히 반영하는지

Poem

🐰
파사드가 춤추니 책임이 나뉘고,
파일은 한 줄씩 조용히 옮겨지고,
실패하면 토끼는 빠르게 되돌리고,
응원은 안전히, 데이터는 반짝이고,
테스트는 깡총깡총 검증하네 🥕✨

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 변경의 핵심 목표인 '응원 등록 이미지 트랜잭션 분리 및 보상 트랜잭션 적용'을 명확하게 요약하고 있습니다.
Linked Issues check ✅ Passed PR의 모든 코드 변경사항이 #213의 요구사항을 충족합니다: S3 이미지 로직과 DB 트랜잭션 분리 완료, 보상 트랜잭션 구현 완료.
Out of Scope Changes check ✅ Passed 모든 변경사항이 응원 등록 이미지 트랜잭션 분리와 보상 트랜잭션 구현이라는 범위 내에 있습니다.
✨ 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 refactor/PRODUCT-287

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.

@github-actions github-actions bot requested a review from leegwichan December 14, 2025 16:36
@lvalentine6 lvalentine6 changed the title Refactor/product 287 [Refactor] 응원 등록 이미지 트랜잭션 분리 및 보상 트랜잭션 적용 Dec 14, 2025
Copy link

@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 (4)
src/main/java/eatda/client/file/FileClient.java (1)

53-72: 멱등성 고려사항

현재 구현은 동일한 파일을 두 번 이동하려고 하면 실패합니다(소스 키가 이미 삭제됨). 재시도 시나리오나 네트워크 실패 후 복구 시 이는 문제가 될 수 있습니다.

필요한 경우, 대상 키가 이미 존재하는지 확인하거나 copyObject 전에 소스 키의 존재 여부를 확인하여 멱등성을 개선할 수 있습니다. 다만 현재 파사드 계층에서 재시도를 하지 않는다면 이는 추후 개선사항으로 미뤄도 됩니다.

src/test/java/eatda/facade/cheer/CheerRegisterFacadeTest.java (2)

121-132: 예외 발생을 명시적으로 검증하는 것을 권장합니다.

현재 try-catch로 예외를 무시하고 있어, 예외가 발생하지 않아도 테스트가 통과할 수 있습니다. assertThrows를 사용하면 예외 발생과 롤백을 모두 검증할 수 있습니다.

-            try {
-                cheerRegisterFacade.registerCheer(
-                        request,
-                        storeResult,
-                        member.getId(),
-                        ImageDomain.CHEER
-                );
-            } catch (Exception ignored) {
-            }
+            assertThrows(Exception.class, () ->
+                    cheerRegisterFacade.registerCheer(
+                            request,
+                            storeResult,
+                            member.getId(),
+                            ImageDomain.CHEER
+                    )
+            );

             assertThat(cheerRepository.count()).isZero();

257-295: 헬퍼 메서드 네이밍을 더 명확하게 하는 것을 권장합니다.

getRegisterRequest()getCheerRegisterRequest()가 매우 유사한 이름이지만 다른 목적으로 사용됩니다. 의도를 명확히 하면 가독성이 향상됩니다.

예시:

  • getRegisterRequest()createTwoImageRequest()
  • getCheerRegisterRequest()createThreeImageRequest()
src/main/java/eatda/service/cheer/CheerService.java (1)

71-91: sortedImagespermanentKeys 크기 불일치 시 예외가 발생할 수 있습니다.

IntStream.range(0, sortedImages.size())로 순회하면서 permanentKeys.get(i)에 접근하므로, 두 리스트의 크기가 다르면 IndexOutOfBoundsException이 발생합니다. 현재 Facade에서 동일한 리스트를 사용하므로 문제없지만, 방어적 검증을 추가하면 안전합니다.

     @Transactional
     public void saveCheerImages(Long cheerId,
                                 List<CheerRegisterRequest.UploadedImageDetail> sortedImages,
                                 List<String> permanentKeys) {

+        if (sortedImages.size() != permanentKeys.size()) {
+            throw new IllegalArgumentException("이미지 정보와 키 목록의 크기가 일치하지 않습니다.");
+        }
+
         Cheer cheer = cheerRepository.findById(cheerId)
                 .orElseThrow(() -> new BusinessException(BusinessErrorCode.CHEER_NOT_FOUND));
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1997898 and 3766b08.

📒 Files selected for processing (12)
  • src/main/java/eatda/client/file/FileClient.java (2 hunks)
  • src/main/java/eatda/controller/cheer/CheerController.java (2 hunks)
  • src/main/java/eatda/controller/cheer/CheerResponse.java (1 hunks)
  • src/main/java/eatda/exception/BusinessErrorCode.java (1 hunks)
  • src/main/java/eatda/facade/CheerCreationResult.java (1 hunks)
  • src/main/java/eatda/facade/CheerRegisterFacade.java (1 hunks)
  • src/main/java/eatda/service/cheer/CheerService.java (8 hunks)
  • src/test/java/eatda/document/BaseDocumentTest.java (2 hunks)
  • src/test/java/eatda/document/cheer/CheerDocumentTest.java (2 hunks)
  • src/test/java/eatda/facade/BaseFacadeTest.java (1 hunks)
  • src/test/java/eatda/facade/cheer/CheerRegisterFacadeTest.java (1 hunks)
  • src/test/java/eatda/service/cheer/CheerServiceTest.java (9 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/eatda/client/file/FileClient.java (1)
src/main/java/eatda/facade/CheerRegisterFacade.java (1)
  • Slf4j (17-76)
⏰ 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: test
🔇 Additional comments (19)
src/main/java/eatda/exception/BusinessErrorCode.java (1)

26-32: CHEER_NOT_FOUND 추가는 적절합니다.

CHE005 코드는 현재 이 정의 한 곳에서만 사용되며, CheerService의 두 위치(77번, 137번 줄)에서 Cheer 엔티티 조회 실패 시에만 던져집니다. HttpStatus.NOT_FOUND로 설정되어 있어 404 응답이 명확하게 표현되므로, 에러 코드 중복도 없고 의미도 정확하게 일치합니다.

src/test/java/eatda/document/cheer/CheerDocumentTest.java (1)

102-102: LGTM! 파사드 기반 아키텍처로의 전환이 올바르게 반영되었습니다.

테스트가 새로운 CheerRegisterFacade를 올바르게 모킹하고 있으며, 성공 및 실패 시나리오 모두 적절하게 커버하고 있습니다.

Also applies to: 140-140

src/test/java/eatda/document/BaseDocumentTest.java (1)

10-10: LGTM! 테스트 베이스 클래스에 파사드 모킹 지원이 추가되었습니다.

새로운 CheerRegisterFacade 의존성이 문서 테스트에서 모킹 가능하도록 올바르게 추가되었습니다.

Also applies to: 70-71

src/main/java/eatda/controller/cheer/CheerController.java (1)

9-9: LGTM! 컨트롤러가 파사드 패턴으로 올바르게 위임하도록 개선되었습니다.

컨트롤러가 복잡한 다단계 플로우를 직접 오케스트레이션하는 대신 CheerRegisterFacade에 위임하는 것은 관심사 분리 측면에서 좋은 접근입니다. 파사드 계층에서 트랜잭션 경계와 보상 트랜잭션을 처리할 수 있게 되었습니다.

Also applies to: 33-33, 40-40

src/test/java/eatda/facade/BaseFacadeTest.java (1)

1-36: LGTM! 파사드 테스트를 위한 베이스 클래스가 잘 구성되었습니다.

외부 의존성(OauthClient, MapClient, FileClient)을 적절히 모킹하고, 필요한 생성자와 리포지토리를 주입하며, DatabaseCleaner를 통해 테스트 격리를 보장합니다. NONE 웹 환경 설정도 파사드 레이어 테스트에 적합합니다.

src/main/java/eatda/facade/CheerCreationResult.java (1)

1-7: LGTM! 결과 타입의 명확한 캡슐화입니다.

CheerCreationResult 레코드는 서비스 계층이 컨트롤러 응답 타입에 결합되지 않고 도메인 엔티티를 반환할 수 있게 해줍니다. 깔끔한 관심사 분리입니다.

src/test/java/eatda/service/cheer/CheerServiceTest.java (2)

19-19: LGTM! 테스트가 새로운 서비스 시그니처에 맞춰 올바르게 업데이트되었습니다.

CheerCreationResult 기반의 새로운 플로우를 반영하여 테스트가 잘 수정되었습니다. 모든 테스트 시나리오가 여전히 적절하게 커버되고 있으며, 멀티라인 생성자 형식과 var 사용으로 가독성도 향상되었습니다.

Also applies to: 47-62, 70-85, 88-119, 122-145, 148-165, 168-188


296-309: LGTM! 헬퍼 메서드로 테스트 코드 중복이 제거되었습니다.

storeSearchResult() 헬퍼 메서드는 테스트 전반에서 반복되는 StoreSearchResult 생성 로직을 효과적으로 제거합니다.

src/main/java/eatda/client/file/FileClient.java (1)

23-23: LGTM! 매직 스트링 제거로 코드 유지보수성이 향상되었습니다.

PATH_DELIMITER 상수를 도입하여 경로 구분자를 일관되게 사용하고 있습니다.

Also applies to: 59-59, 82-83

src/main/java/eatda/facade/CheerRegisterFacade.java (3)

57-62: LGTM!

sortImages 헬퍼 메서드가 명확하고 간결하게 구현되어 있습니다.


64-75: LGTM!

파일 이동 로직이 FileClient에 적절히 위임되어 있으며, FileClient 내부에서 부분 실패 시 이미 이동된 파일을 정리하는 것을 확인했습니다.


25-55: 파일 I/O와 DB 트랜잭션의 분리가 잘 구현되었습니다.

PR 목표대로 createCheer가 먼저 커밋되고, 이미지 이동은 트랜잭션 외부에서 수행되며, 실패 시 보상 트랜잭션으로 정리하는 설계가 적절합니다.

src/test/java/eatda/facade/cheer/CheerRegisterFacadeTest.java (3)

36-81: LGTM!

정상 경로 테스트가 잘 작성되어 있습니다. 응답 검증과 mock 상호작용 검증이 모두 포함되어 있습니다.


134-174: 테스트명과 실제 동작의 차이를 확인해 주세요.

테스트명은 "부분적으로 성공한 후 실패"를 의미하지만, FileClient.moveTempFilesToPermanent는 내부적으로 부분 실패 시 이미 이동된 파일을 정리합니다. Facade 입장에서는 부분 성공 여부를 알 수 없으므로, 이미지_이동_중_실패하면_응원을_삭제한다 테스트와 동일한 시나리오를 검증하고 있습니다.

테스트 의도에 따라 중복 테스트를 제거하거나, 테스트명을 수정하는 것을 고려해 주세요.


218-255: LGTM!

이미지가 없는 경우의 테스트가 잘 작성되어 있습니다. Mockito.never()를 사용하여 파일 이동이 호출되지 않음을 검증하는 것이 적절합니다.

src/main/java/eatda/service/cheer/CheerService.java (4)

47-60: LGTM!

createCheerCheerCreationResult를 반환하도록 리팩토링되어 이미지 처리가 Facade로 분리되었습니다. 책임 분리가 명확합니다.


129-132: LGTM!

보상 트랜잭션을 위한 deleteCheer 메서드가 간결하게 구현되어 있습니다.


134-140: LGTM!

단일 응원 조회 메서드가 명확하게 구현되어 있습니다.


107-115: LGTM!

응답 매핑 로직이 간결하게 리팩토링되었습니다.

Copy link

@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

♻️ Duplicate comments (2)
src/test/java/eatda/facade/cheer/CheerRegisterFacadeTest.java (2)

202-217: 이전 리뷰에서 지적된 문제가 여전히 남아있습니다.

이 코드 영역은 이전 리뷰 코멘트에서 이미 지적되었지만 수정되지 않았습니다. try-catch 블록 외부의 assertion과 verify로 인해 예외가 발생하지 않으면 테스트가 false positive로 통과할 수 있습니다.

다음 diff를 적용하여 예외 발생을 명시적으로 검증하세요:

-            try {
-                cheerRegisterFacade.registerCheer(
-                        request,
-                        storeResult,
-                        member.getId(),
-                        ImageDomain.CHEER
-                );
-            } catch (Exception ignored) {
-            }
-
-            assertThat(cheerRepository.count())
-                    .as("DB 에러(컬럼 길이 초과) 발생 시 응원글은 삭제되어야 한다.")
-                    .isZero();
-
-            verify(fileClient).deleteFiles(movedKeys);
+            assertThrows(Exception.class, () ->
+                    cheerRegisterFacade.registerCheer(
+                            request,
+                            storeResult,
+                            member.getId(),
+                            ImageDomain.CHEER
+                    )
+            );
+
+            assertThat(cheerRepository.count())
+                    .as("DB 에러(컬럼 길이 초과) 발생 시 응원글은 삭제되어야 한다.")
+                    .isZero();
+
+            verify(fileClient).deleteFiles(movedKeys);

161-174: try-catch 블록 외부의 assertion으로 인한 false positive 위험이 있습니다.

예외가 발생하지 않아도 테스트가 통과하므로, 부분 성공 후 실패 시 보상 트랜잭션이 제대로 동작하지 않아도 테스트가 성공할 수 있습니다.

다음 diff를 적용하여 예외 발생을 명시적으로 검증하세요:

-            try {
-                cheerRegisterFacade.registerCheer(
-                        request,
-                        storeResult,
-                        member.getId(),
-                        ImageDomain.CHEER
-                );
-            } catch (Exception ignored) {
-            }
-
-            assertThat(cheerRepository.count())
-                    .as("부분 성공 후 실패 시 Cheer는 삭제되어야 한다.")
-                    .isZero();
+            assertThrows(SdkException.class, () ->
+                    cheerRegisterFacade.registerCheer(
+                            request,
+                            storeResult,
+                            member.getId(),
+                            ImageDomain.CHEER
+                    )
+            );
+
+            assertThat(cheerRepository.count())
+                    .as("부분 성공 후 실패 시 Cheer는 삭제되어야 한다.")
+                    .isZero();
🧹 Nitpick comments (1)
src/test/java/eatda/facade/cheer/CheerRegisterFacadeTest.java (1)

258-296: 헬퍼 메서드 이름이 명확하게 구분되지 않습니다.

getRegisterRequest()getCheerRegisterRequest()는 유사한 이름으로 각 메서드가 어떤 시나리오에 사용되는지 명확하지 않습니다. 특히 getCheerRegisterRequest()는 부분 성공 테스트에만 사용되므로, 용도를 반영한 이름으로 변경하는 것을 고려해보세요.

예시:

-        private CheerRegisterRequest getRegisterRequest() {
+        private CheerRegisterRequest createRequestWithTwoImages() {
             // ...
         }
 
-        private CheerRegisterRequest getCheerRegisterRequest() {
+        private CheerRegisterRequest createRequestWithThreeImages() {
             // ...
         }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3766b08 and 12ac1bc.

📒 Files selected for processing (1)
  • src/test/java/eatda/facade/cheer/CheerRegisterFacadeTest.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/test/java/eatda/facade/cheer/CheerRegisterFacadeTest.java (1)
src/test/java/eatda/document/cheer/CheerDocumentTest.java (3)
  • Nested (42-154)
  • Nested (156-241)
  • Nested (243-312)
⏰ 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: test
🔇 Additional comments (2)
src/test/java/eatda/facade/cheer/CheerRegisterFacadeTest.java (2)

36-81: 정상 시나리오 테스트가 잘 작성되었습니다.

응원 등록 성공 케이스를 명확하게 검증하고 있으며, 이미지 이동과 응답 데이터 검증이 적절합니다.


219-256: 이미지 없는 등록 케이스가 적절하게 검증되었습니다.

엣지 케이스를 잘 다루고 있으며, fileClient 호출이 없음을 Mockito.never()로 명확하게 검증하고 있습니다.

Copy link

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

🧹 Nitpick comments (3)
src/test/java/eatda/facade/cheer/CheerRegisterFacadeTest.java (3)

134-173: 테스트 이름과 실제 동작이 일치하지 않습니다.

"부분적으로 성공한 후 실패"를 테스트한다고 명시되어 있지만, mock 설정(lines 153-159)이 즉시 예외를 던지므로 실제 부분 성공 시나리오를 검증하지 못합니다. 이전 테스트(이미지_이동_중_실패하면_응원을_삭제한다)와 본질적으로 동일한 동작을 테스트합니다.

부분 성공 시나리오를 실제로 테스트하려면:

  1. fileClient.moveTempFilesToPermanent가 일부 키를 반환한 후 내부적으로 실패하는 상황을 시뮬레이션하거나
  2. 파일 이동 후 후속 작업에서 실패하는 시나리오를 구성해야 합니다.

만약 FileClient 내부에서 부분 성공/롤백을 처리한다면, 해당 로직은 FileClient 단위 테스트에서 검증하는 것이 더 적합합니다.


201-208: 예외 타입이 너무 광범위합니다.

Exception.class는 너무 포괄적이어서 의도하지 않은 예외도 테스트를 통과시킬 수 있습니다. DB 제약 조건 위반에 대한 구체적인 예외 타입(예: DataIntegrityViolationException 또는 ConstraintViolationException)을 사용하면 테스트의 정확성이 향상됩니다.

-            assertThrows(Exception.class, () ->
+            assertThrows(DataIntegrityViolationException.class, () ->
                     cheerRegisterFacade.registerCheer(
                             request,
                             storeResult,
                             member.getId(),
                             ImageDomain.CHEER
                     )
             );

257-295: 헬퍼 메서드 통합을 고려해 보세요.

getRegisterRequest()getCheerRegisterRequest() 메서드가 유사한 구조를 가지고 있습니다. 이미지 개수와 설명을 파라미터로 받는 단일 헬퍼 메서드로 통합하면 중복을 줄이고 테스트 의도를 더 명확히 표현할 수 있습니다.

@NonNull
private CheerRegisterRequest createRegisterRequest(String description, int imageCount) {
    List<CheerRegisterRequest.UploadedImageDetail> images = IntStream.rangeClosed(1, imageCount)
            .mapToObj(i -> new CheerRegisterRequest.UploadedImageDetail(
                    "temp/key" + i + ".jpg", (long) i, "image/jpeg", 1000L
            ))
            .toList();

    return new CheerRegisterRequest(
            "kakao-1",
            "농민백암순대",
            description,
            images,
            List.of(CheerTagName.GOOD_FOR_DATING)
    );
}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12ac1bc and 06562cc.

📒 Files selected for processing (1)
  • src/test/java/eatda/facade/cheer/CheerRegisterFacadeTest.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: test
🔇 Additional comments (4)
src/test/java/eatda/facade/cheer/CheerRegisterFacadeTest.java (4)

1-33: 클래스 구조 및 임포트가 적절합니다.

테스트 클래스 설정이 적절하며, BaseFacadeTest를 확장하여 필요한 의존성을 상속받고 있습니다.


37-82: 성공 시나리오 테스트가 잘 구성되어 있습니다.

응답 내용 검증과 mock 상호작용 검증이 적절히 포함되어 있습니다.


84-132: 이전 리뷰 피드백이 반영되어 assertThrows를 사용하고 있습니다.

예외 발생을 명시적으로 검증하고, 이후 cleanup 검증을 수행하는 구조로 개선되었습니다.


218-255: 이미지 없는 경우의 엣지 케이스가 잘 테스트되었습니다.

이미지가 없을 때 파일 이동이 호출되지 않음을 검증하고, 응원이 정상적으로 등록됨을 확인하는 적절한 테스트입니다.

@sonarqubecloud
Copy link

@lvalentine6 lvalentine6 merged commit 3d9dbe2 into develop Dec 15, 2025
6 checks passed
@lvalentine6 lvalentine6 deleted the refactor/PRODUCT-287 branch December 15, 2025 13:30
@github-actions
Copy link

🎉 This PR is included in version 1.8.0-develop.20 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions
Copy link

🎉 This PR is included in version 1.9.4 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[PRODUCT-287] [Refactor] 응원 등록 이미지 트랜잭션 분리 및 보상 트랜잭션 적용

3 participants