2929import org .springframework .stereotype .Service ;
3030import org .springframework .transaction .annotation .Transactional ;
3131
32+ import java .util .Objects ;
3233import java .util .Optional ;
3334
3435@ Service
@@ -75,35 +76,40 @@ public UserDetailResponse updateUserProfile(Long userId, UserProfileRequest requ
7576 throw new CustomException (ErrorCode .NICKNAME_DUPLICATED );
7677 }
7778
79+
7880 // UserProfile 업데이트
7981 UserProfile profile = user .getUserProfile ();
8082 profile .setNickname (request .nickname ());
81- profile .setProfileImageUrl (request .profileImageUrl ());
8283 profile .setBio (request .bio ());
8384 profile .setBirthDate (request .birthDate ());
8485
85- // 프로필 이미지 및 매핑 업데이트
86- updateProfileImage (userId , request .profileImageUrl ());
86+ // TODO: 프로필 이미지 및 매핑 업데이트 리팩토링 필요
87+ // 프로필 이미지 변경이 있는 경우만 수행
88+ String newUrl = request .profileImageUrl ();
89+ String oldUrl = profile .getProfileImageUrl ();
90+ if (!Objects .equals (newUrl , oldUrl )) {
91+ // 외부 이미지(S3 외부 URL)는 매핑 로직 제외
92+ if (isExternalImageUrl (newUrl )) {
93+ // 기존 매핑만 제거 (소셜 이미지로 바뀌면 내부 매핑 필요 없음)
94+ removeExistingMapping (userId );
95+ } else {
96+ updateProfileImage (userId , newUrl );
97+ }
98+ profile .setProfileImageUrl (newUrl );
99+ }
87100
88101 // UserDetailResponse로 변환하여 반환
89102 return UserDetailResponse .from (user );
90103 }
91104
92105 /**
93- * 프로필 이미지 및 매핑 교체 로직
94- * - 기존 이미지(S3 + FileAttachment + Mapping) 삭제 후 새 매핑 생성
106+ * 내부 저장소(S3) 이미지 교체 로직
107+ * - 기존 매핑 및 파일 삭제 후 새 매핑 생성
95108 */
96109 private void updateProfileImage (Long userId , String newImageUrl ) {
97110
98- // 기존 매핑 및 파일 삭제
99- attachmentMappingRepository .findByEntityTypeAndEntityId (EntityType .PROFILE , userId )
100- .ifPresent (existingMapping -> {
101- FileAttachment oldAttachment = existingMapping .getFileAttachment ();
102- if (oldAttachment != null ) {
103- fileService .deleteFile (oldAttachment .getId (), userId );
104- }
105- attachmentMappingRepository .delete (existingMapping );
106- });
111+ // 기존 매핑 제거
112+ removeExistingMapping (userId );
107113
108114 // 새 이미지가 없는 경우
109115 if (newImageUrl == null || newImageUrl .isBlank ()) {
@@ -124,6 +130,33 @@ private void updateProfileImage(Long userId, String newImageUrl) {
124130 attachmentMappingRepository .save (newMapping );
125131 }
126132
133+ /**
134+ * 기존 프로필 이미지 매핑 및 파일 삭제
135+ */
136+ private void removeExistingMapping (Long userId ) {
137+ attachmentMappingRepository .findByEntityTypeAndEntityId (EntityType .PROFILE , userId )
138+ .ifPresent (mapping -> {
139+ FileAttachment oldAttachment = mapping .getFileAttachment ();
140+ if (oldAttachment != null ) {
141+ fileService .deleteFile (oldAttachment .getId (), userId );
142+ }
143+ attachmentMappingRepository .delete (mapping );
144+ });
145+ }
146+
147+ /**
148+ * 외부 이미지 URL 판별
149+ * - 우리 S3 또는 CDN이 아니면 true
150+ * - 필요 시 application.yml에서 환경변수로 관리
151+ */
152+ private boolean isExternalImageUrl (String url ) {
153+ if (url == null || url .isBlank ()) return true ;
154+
155+ // TODO: 하드 코딩 제거
156+ return !(url .startsWith ("https://team5-s3-1.s3.ap-northeast-2.amazonaws.com" ));
157+ // || url.contains("cdn.our-domain.com"));
158+ }
159+
127160 /**
128161 * 비밀번호 변경 서비스
129162 * 1. 사용자 조회 및 상태 검증
0 commit comments