Skip to content

Conversation

@yerinchun
Copy link
Contributor

@yerinchun yerinchun commented Jan 3, 2026

#️⃣ 연관된 이슈

관련된 이슈 번호를 적어주세요.
Close #180

✨ 작업 내용 (Summary)

이번 PR에서 작업한 내용을 간략히 설명해주세요. (이미지 첨부 가능)

누락된 코드 다시 추가

  • 사진 수정 시 기존 파일 삭제 로직 추가

✅ 변경 사항 체크리스트

다음 항목들을 확인하고 체크해주세요.

  • 코드에 영향이 있는 모든 부분에 대한 테스트를 작성하고 실행했나요?
  • 문서를 작성하거나 수정했나요? (필요한 경우)
  • 중요한 변경 사항이 팀에 공유되었나요?

🧪 테스트 결과

코드 변경에 대해 테스트를 수행한 결과를 요약해주세요.

  • 테스트 환경: 개발 서버
  • 테스트 방법: 스웨거
  • 결과 요약: 테스트 통과

📸 스크린샷

관련된 스크린샷 또는 GIF가 있다면 여기에 첨부해주세요.


💬 리뷰 요구사항

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요.


📎 참고 자료

관련 문서, 레퍼런스 링크 등이 있다면 여기에 첨부해주세요.

Summary by CodeRabbit

  • 새로운 기능
    • 사용자가 자신의 기본 정보(이름 등)과 프로필 이미지를 업데이트할 수 있는 신규 API 추가.
    • 프로필 이미지 업데이트 시 이전 이미지는 트랜잭션 커밋 이후 안전하게 삭제되도록 자동 처리되어 불필요한 저장소 파일이 정리됨.

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

@yerinchun yerinchun self-assigned this Jan 3, 2026
@yerinchun yerinchun added 🐞 bug 버그 이슈 예린 labels Jan 3, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 3, 2026

📝 Walkthrough

Walkthrough

인증된 사용자의 기본 정보(닉네임·프로필 사진·공개여부)를 PATCH /me로 갱신하는 API를 추가하고, 프로필 변경 시 기존 이미지 삭제를 트랜잭션 커밋 후 이벤트 기반으로 S3에서 처리하도록 구현했습니다. (약 50자)

Changes

Cohort / File(s) 요약
컨트롤러: 사용자 엔드포인트
src/main/java/com/hrr/backend/domain/user/controller/UserController.java
PATCH /me 엔드포인트 추가: UpdateUserInfoRequestDto 수신 후 인증된 사용자 ID로 userService.updateUserInfo 호출, UpdateUserInfoResponseDtoUSER_UPDATE_OK 반환
서비스: 비즈니스 로직 및 이벤트 발행
src/main/java/com/hrr/backend/domain/user/service/UserServiceImpl.java
ApplicationEventPublisher 주입 및 사용: 프로필 이미지가 교체되면 이전 이미지 키가 존재할 때 ProfileImageDeletedEvent 발행(트랜잭션 커밋 이후 처리 의도)
이벤트: 정의 및 리스너
src/main/java/com/hrr/backend/domain/user/event/ProfileImageDeletedEvent.java, src/main/java/com/hrr/backend/domain/user/event/ProfileImageEventListener.java
ProfileImageDeletedEvent 클래스 추가(이전 이미지 키 보유). ProfileImageEventListener 추가: AFTER_COMMIT 방식으로 이벤트 수신 시 S3Service.deleteFileByKey(oldImageKey) 호출 및 로깅 수행

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as 클라이언트
    participant Controller as UserController
    participant Service as UserServiceImpl
    participant DB as Database/Transaction
    participant EventPub as ApplicationEventPublisher
    participant Listener as ProfileImageEventListener
    participant S3 as S3Service

    Client->>Controller: PATCH /me (UpdateUserInfoRequestDto)
    Controller->>Service: updateUserInfo(userId, dto)
    Service->>DB: 사용자 정보 갱신 (새 이미지 키 저장)
    DB-->>Service: 트랜잭션(커밋 준비)
    alt 이전 이미지 키 존재
        Service->>EventPub: publish(ProfileImageDeletedEvent(oldImageKey))
    end
    Service-->>Controller: UpdateUserInfoResponseDto + USER_UPDATE_OK
    DB->>DB: 트랜잭션 커밋
    Note right of DB: AFTER_COMMIT 트리거
    EventPub->>Listener: 전달(ProfileImageDeletedEvent) 
    Listener->>S3: deleteFileByKey(oldImageKey)
    S3-->>Listener: 삭제 완료
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • [FEAT] 사용자 정보 수정 API #167: 동일한 PATCH /me 엔드포인트 및 사용자 정보 업데이트와 S3 프로필 이미지 처리 로직을 구현한 PR로 코드 수준에서 직접적인 관련 있음.

Poem

잃었던 엔드포인트 되찾아,
닉네임과 사진 고쳐두면,
옛 사진은 커밋 뒤에 사라지고,
마이페이지엔 새 얼굴이 빛나네 ✨
작은 이벤트가 큰 정리 🎈

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.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 제목이 누락된 사용자 기본정보 수정 API를 복구하는 변경사항을 명확히 설명하고 있으며, PR의 핵심 목표와 일치합니다.
Linked Issues check ✅ Passed PR이 #180의 모든 코딩 요구사항을 충족합니다: 사용자 기본정보 수정 API 엔드포인트 구현, 프로필 사진 수정 시 기존 파일 삭제 로직(이벤트 기반)이 모두 포함되어 있습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 issue #180의 요구사항 범위 내입니다: UpdateUserInfoRequestDto/ResponseDto 엔드포인트, 프로필 이미지 삭제 이벤트 처리가 핵심 기능 복구와 관련이 있습니다.
✨ Finishing touches
  • 📝 Generate docstrings

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

@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

📜 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 5b5873e and 989b614.

📒 Files selected for processing (2)
  • src/main/java/com/hrr/backend/domain/user/controller/UserController.java
  • src/main/java/com/hrr/backend/domain/user/service/UserServiceImpl.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-06T08:06:38.145Z
Learnt from: yerinchun
Repo: Hrr-HabbitRoutineResult/hrr-server PR: 26
File: src/main/java/com/hrr/backend/domain/user/entity/User.java:42-44
Timestamp: 2025-11-06T08:06:38.145Z
Learning: In the hrr-server project User entity (src/main/java/com/hrr/backend/domain/user/entity/User.java), the password field is nullable because the application uses social login (OAuth), and social login users do not have passwords stored in the database.

Applied to files:

  • src/main/java/com/hrr/backend/domain/user/service/UserServiceImpl.java
🔇 Additional comments (2)
src/main/java/com/hrr/backend/domain/user/controller/UserController.java (1)

165-177: 잘 복구하셨네요! 🎯

누락된 API가 깔끔하게 복원되었습니다. 인증, 검증, 응답 구조 모두 기존 패턴과 일관성 있게 구현되었어요.

src/main/java/com/hrr/backend/domain/user/service/UserServiceImpl.java (1)

18-18: 의존성 주입이 올바르게 구성되었습니다.

S3Service가 적절히 추가되었고, Lombok의 @requiredargsconstructor를 통해 생성자 주입이 자동으로 처리됩니다.

Also applies to: 56-56

Comment on lines 309 to 313
// 기존 이미지가 있으면 S3에서 삭제
String oldImageKey = user.getProfileImage();
if (oldImageKey != null && !oldImageKey.isBlank()) {
s3Service.deleteFileByKey(oldImageKey);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

S3 삭제 로직에 개선이 필요합니다.

현재 구현에는 몇 가지 잠재적 문제가 있습니다:

  1. 동일 키 체크 누락: 사용자가 동일한 이미지를 다시 설정하면 해당 이미지가 삭제된 후 다시 설정됩니다.
  2. 트랜잭션 경계 문제: S3 삭제가 실패하면 전체 트랜잭션이 롤백되어 프로필 업데이트 자체가 불가능해집니다. 외부 시스템(S3) 호출을 트랜잭션 내부에서 수행하면 이런 문제가 발생할 수 있습니다.
🔎 개선 방안

방안 1 (권장): 동일 키 체크 추가 + 삭제 실패 시 로깅만 수행

 // 프로필 이미지 Key가 제공된 경우 업데이트
 if (requestDto.getProfileImageKey() != null) {
+    String newImageKey = requestDto.getProfileImageKey();
     // 기존 이미지가 있으면 S3에서 삭제
     String oldImageKey = user.getProfileImage();
-    if (oldImageKey != null && !oldImageKey.isBlank()) {
+    if (oldImageKey != null && !oldImageKey.isBlank() && !oldImageKey.equals(newImageKey)) {
-        s3Service.deleteFileByKey(oldImageKey);
+        try {
+            s3Service.deleteFileByKey(oldImageKey);
+        } catch (Exception e) {
+            // S3 삭제 실패 시 로깅만 하고 계속 진행
+            log.warn("Failed to delete old profile image: {}", oldImageKey, e);
+        }
     }
 
-    user.updateProfileImage(requestDto.getProfileImageKey());
+    user.updateProfileImage(newImageKey);
 }

방안 2 (대안): 트랜잭션 커밋 후 삭제 (비동기 또는 @TransactionalEventListener 활용)

S3 삭제를 DB 커밋 이후로 지연시켜 트랜잭션 독립성을 보장하는 방법입니다. 이 경우 일시적으로 불필요한 파일이 S3에 남을 수 있지만, 사용자 경험에는 영향을 주지 않습니다.

참고 자료:

🤖 Prompt for AI Agents
In src/main/java/com/hrr/backend/domain/user/service/UserServiceImpl.java around
lines 309 to 313, the S3 delete logic should avoid deleting when the new image
key is identical and must not perform S3 deletion inside the DB transaction;
update the code to first compare the existing profileImage key with the
incoming/new key and skip deletion if they are equal or blank, and if deletion
is needed either (a) attempt deletion but catch exceptions and only log failures
(do not let S3 errors propagate and rollback the transaction) or (b) move the
deletion to execute after transaction commit (e.g., publish an event or run
asynchronously via @TransactionalEventListener or a separate background task) so
S3 removals occur outside the transactional boundary.

@yerinchun yerinchun changed the title Bug/180 user info update deleted [BUG] 사용자 기본정보 수정 API 누락 Jan 3, 2026
Copy link
Contributor

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

@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

♻️ Duplicate comments (1)
src/main/java/com/hrr/backend/domain/user/service/UserServiceImpl.java (1)

309-319: [중요] 동일한 이미지 키 체크가 누락되었습니다.

현재 코드는 사용자가 동일한 이미지로 "업데이트"할 경우, 해당 이미지를 S3에서 삭제해버리는 문제가 있습니다.

발생 시나리오:

  1. 사용자가 현재 프로필 이미지와 동일한 이미지 키를 다시 제출
  2. Line 310에서 oldImageKey는 현재 이미지 키를 저장
  3. Line 313에서 동일한 키로 업데이트 (실질적 변경 없음)
  4. Line 316-318에서 oldImageKey가 null이 아니므로 삭제 이벤트 발행
  5. 트랜잭션 커밋 후 리스너가 현재 사용 중인 이미지를 S3에서 삭제
  6. 결과: DB에는 올바른 키가 있지만 S3에 파일이 없어 이미지 깨짐

이 문제는 이전 리뷰에서도 지적되었으나 해결되지 않았습니다.

🔎 수정 방안
 // 프로필 이미지 Key가 제공된 경우 업데이트
 if (requestDto.getProfileImageKey() != null) {
+    String newImageKey = requestDto.getProfileImageKey();
     String oldImageKey = user.getProfileImage();
 
     // 새 이미지로 업데이트
-    user.updateProfileImage(requestDto.getProfileImageKey());
+    user.updateProfileImage(newImageKey);
 
     // 트랜잭션 커밋 후 기존 이미지 삭제 이벤트 발행
-    if (oldImageKey != null && !oldImageKey.isBlank()) {
+    // 동일한 이미지인 경우 삭제하지 않음
+    if (oldImageKey != null && !oldImageKey.isBlank() && !oldImageKey.equals(newImageKey)) {
         eventPublisher.publishEvent(new ProfileImageDeletedEvent(oldImageKey));
     }
 }

참고: 이벤트 기반 접근으로 트랜잭션 경계 문제는 잘 해결되었지만, 동일 키 검증은 여전히 필요합니다.

🧹 Nitpick comments (1)
src/main/java/com/hrr/backend/domain/user/event/ProfileImageEventListener.java (1)

27-36: 트랜잭션 커밋 후 처리 로직이 올바르게 구현되었습니다.

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)를 사용하여 이전 리뷰에서 지적된 트랜잭션 경계 문제를 제대로 해결했습니다.

다만, S3 삭제 실패 시 예외 처리가 없어 운영상 리스크가 있을 수 있습니다. DB는 이미 커밋된 상태이므로 사용자에게는 문제가 없지만, S3에 불필요한 파일이 남거나 모니터링이 어려울 수 있습니다.

🔎 예외 처리 추가 제안
 @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
 public void handleProfileImageDeleted(ProfileImageDeletedEvent event) {
     String oldImageKey = event.getOldImageKey();
 
     if (oldImageKey != null && !oldImageKey.isBlank()) {
         log.info("프로필 이미지 삭제 시작 (트랜잭션 커밋 후): {}", oldImageKey);
-        s3Service.deleteFileByKey(oldImageKey);
-        log.info("프로필 이미지 삭제 완료: {}", oldImageKey);
+        try {
+            s3Service.deleteFileByKey(oldImageKey);
+            log.info("프로필 이미지 삭제 완료: {}", oldImageKey);
+        } catch (Exception e) {
+            log.error("프로필 이미지 삭제 실패 (S3 오류): {}", oldImageKey, e);
+            // 필요시 재시도 큐에 추가하거나 알림 발송
+        }
     }
 }

참고: 현재 구조에서는 S3 실패가 사용자 경험에 영향을 주지 않지만, 장애 추적 및 재처리를 위해 예외 로깅을 권장합니다.

📜 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 989b614 and 6a1d4f7.

📒 Files selected for processing (3)
  • src/main/java/com/hrr/backend/domain/user/event/ProfileImageDeletedEvent.java
  • src/main/java/com/hrr/backend/domain/user/event/ProfileImageEventListener.java
  • src/main/java/com/hrr/backend/domain/user/service/UserServiceImpl.java
🔇 Additional comments (1)
src/main/java/com/hrr/backend/domain/user/event/ProfileImageDeletedEvent.java (1)

1-14: 이벤트 클래스 설계가 깔끔합니다! 👍

불변 이벤트 객체로 설계되어 있고, 트랜잭션 커밋 후 S3 삭제를 위한 명확한 목적을 가지고 있습니다. Lombok 애노테이션도 적절하게 사용되었습니다.

Copy link
Contributor

@yc3697 yc3697 left a comment

Choose a reason for hiding this comment

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

확인했습니다 수고하셨어요~

@yerinchun yerinchun merged commit b696cda into develop Jan 3, 2026
2 checks passed
@yerinchun yerinchun deleted the bug/180-user-info-update-deleted branch January 3, 2026 07:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

예린 🐞 bug 버그 이슈

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] 사용자 기본정보 수정 API 누락

3 participants