Skip to content

Commit b12e545

Browse files
authored
Merge pull request #321 from prgrms-web-devcourse-final-project/chore#320
[chore] 브랜치 병합 후 테스트
2 parents adc72fd + 4b7a62d commit b12e545

File tree

7 files changed

+100
-50
lines changed

7 files changed

+100
-50
lines changed

src/main/java/com/back/domain/cocktail/comment/service/CocktailCommentService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public CocktailCommentResponseDto createCocktailComment(Long cocktailId, Cocktai
4747
.cocktail(cocktail)
4848
.user(user)
4949
.content(reqBody.content())
50-
.status(reqBody.status())
50+
.status(reqBody.status() != null ? reqBody.status() : CommentStatus.PUBLIC)
5151
.build();
5252

5353
return new CocktailCommentResponseDto(cocktailCommentRepository.save(cocktailComment));

src/main/java/com/back/domain/post/post/controller/PostController.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,17 @@ public RsData<PostLikeResponseDto> toggleLike(
120120
) {
121121
return RsData.successOf(postService.toggleLike(postId)); // code=200, message="success"
122122
}
123+
124+
/**
125+
* 사용자가 해당 게시글을 추천(좋아요)했는지 여부 확인 API
126+
* @param postId 확인할 게시글 ID
127+
* @return 사용자의 추천 여부 (true/false)
128+
*/
129+
@GetMapping("/{postId}/like")
130+
@Operation(summary = "사용자가 해당 게시글 추천 여부 확인")
131+
public RsData<Boolean> getLike(
132+
@PathVariable Long postId
133+
) {
134+
return RsData.successOf(postService.getLike(postId)); // code=200, message="success"
135+
}
123136
}

src/main/java/com/back/domain/post/post/dto/request/PostUpdateRequestDto.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ public record PostUpdateRequestDto(
88
PostStatus status,
99
String title,
1010
String content,
11-
// 기존 이미지 중 유지할 이미지 ID 목록
12-
List<Long> keepImageIds,
11+
// 기존 이미지 중 유지할 이미지 URL 목록
12+
List<String> keepImageUrls,
1313
String videoUrl,
1414
List<String> tags
1515
) {

src/main/java/com/back/domain/post/post/repository/PostLikeRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@
77
import org.springframework.data.jpa.repository.JpaRepository;
88

99
public interface PostLikeRepository extends JpaRepository<PostLike, Long> {
10+
Boolean existsByPostAndUser(Post post, User user);
11+
1012
Optional<PostLike> findByPostAndUser(Post post, User user);
1113
}

src/main/java/com/back/domain/post/post/service/PostService.java

Lines changed: 67 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,18 @@
2424
import com.back.global.file.dto.UploadedFileDto;
2525
import com.back.global.file.service.FileService;
2626
import com.back.global.rq.Rq;
27+
import java.util.ArrayList;
28+
import java.util.List;
29+
import java.util.NoSuchElementException;
30+
import java.util.Optional;
31+
import java.util.stream.Collectors;
2732
import lombok.RequiredArgsConstructor;
2833
import org.springframework.stereotype.Service;
2934
import org.springframework.transaction.annotation.Transactional;
3035
import org.springframework.transaction.support.TransactionSynchronization;
3136
import org.springframework.transaction.support.TransactionSynchronizationManager;
3237
import org.springframework.web.multipart.MultipartFile;
3338

34-
import java.util.*;
35-
import java.util.function.Function;
36-
import java.util.stream.Collectors;
37-
3839
@Service
3940
@RequiredArgsConstructor
4041
public class PostService {
@@ -146,63 +147,73 @@ public PostResponseDto updatePost(Long postId, PostUpdateRequestDto reqBody, Lis
146147
if (reqBody.content() != null && !reqBody.content().isBlank()) {
147148
post.updateContent(reqBody.content());
148149
}
150+
151+
List<String> addedImgUrls = List.of();
152+
List<String> uploadedFileNames = List.of();
153+
149154
if (images != null && !images.isEmpty()) {
150155
// 새 이미지 업로드
151156
List<UploadedFileDto> uploaded = fileService.uploadFiles(images);
152-
List<String> uploadedFileNames = uploaded.stream().map(UploadedFileDto::fileName).toList();
157+
addedImgUrls = uploaded.stream().map(UploadedFileDto::url).toList();
158+
uploadedFileNames = uploaded.stream().map(UploadedFileDto::fileName).toList();
159+
}
160+
161+
// 요청 DTO에서 "유지할 이미지 URL 목록" 꺼내기
162+
List<String> keepImageUrls = new ArrayList<>(
163+
Optional.ofNullable(reqBody.keepImageUrls()).orElse(List.of()));
164+
keepImageUrls.addAll(addedImgUrls);
153165

154-
// 요청 DTO에서 "유지할 이미지 ID 목록" 꺼내기
155-
List<Long> keepIds = Optional.ofNullable(reqBody.keepImageIds()).orElse(List.of());
166+
// 🔹 현재 게시글의 모든 이미지 가져오기
167+
List<PostImage> existingImages = new ArrayList<>(post.getImages());
156168

157-
// 현재 게시글의 이미지들을 (id -> 객체) 매핑으로 변환
158-
Map<Long, PostImage> existingById = post.getImages().stream()
159-
.collect(Collectors.toMap(PostImage::getId, Function.identity()));
169+
// 삭제될 이미지 (DB + S3)
170+
List<PostImage> toRemove = post.getImages().stream()
171+
.filter(img -> !keepImageUrls.contains(img.getUrl()))
172+
.toList();
160173

161-
// 삭제할 이미지 찾기
162-
List<PostImage> toDelete = post.getImages().stream()
163-
.filter(img -> !keepIds.contains(img.getId()))
174+
List<String> deleteKeysAfterCommit = toRemove.stream()
175+
.map(PostImage::getFileName)
164176
.toList();
165177

166-
// 최종 이미지 리스트 구성
167-
List<PostImage> finalImages = new ArrayList<>();
178+
toRemove.forEach(img -> img.updatePost(null)); // 관계 해제
179+
post.getImages().removeAll(toRemove); // orphanRemoval 트리거
180+
181+
// 유지할 이미지 정렬
168182
int order = 0;
169-
for (Long keepId : keepIds) {
170-
PostImage img = existingById.get(keepId);
171-
if (img != null) {
172-
img.updateSortOrder(order++);
173-
finalImages.add(img);
183+
for (String url : keepImageUrls) {
184+
// 기존 이미지인지 확인
185+
PostImage existing = existingImages.stream()
186+
.filter(img -> img.getUrl().equals(url))
187+
.findFirst()
188+
.orElse(null);
189+
190+
if (existing != null) {
191+
existing.updateSortOrder(order++);
192+
} else {
193+
// 새로 추가된 이미지
194+
post.getImages().add(PostImage.builder()
195+
.post(post)
196+
.fileName(extractFileNameFromUrl(url)) // URL에서 파일명 추출 함수 아래 참고
197+
.url(url)
198+
.sortOrder(order++)
199+
.build());
174200
}
175201
}
176-
for (UploadedFileDto u : uploaded) {
177-
finalImages.add(PostImage.builder()
178-
.post(post)
179-
.fileName(u.fileName())
180-
.url(u.url())
181-
.sortOrder(order++)
182-
.build()
183-
);
184-
}
185-
186-
// 삭제 예정 key 모음
187-
List<String> deleteKeysAfterCommit = toDelete.stream()
188-
.map(PostImage::getFileName)
189-
.toList();
190202

191-
// DB에 반영
192-
post.updateImages(finalImages);
203+
List<String> uploadedNames = new ArrayList<>(uploadedFileNames);
193204

194205
// 트랜잭션 완료 후 처리
195206
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
196207
@Override
197208
public void afterCompletion(int status) {
198209
if (status == STATUS_ROLLED_BACK) {
199-
uploadedFileNames.forEach(fileService::deleteFile);
210+
uploadedNames.forEach(fileService::deleteFile);
200211
} else if (status == STATUS_COMMITTED) {
201212
deleteKeysAfterCommit.forEach(fileService::deleteFile);
202213
}
203214
}
204215
});
205-
}
216+
206217
if (reqBody.videoUrl() != null && !reqBody.videoUrl().isBlank()) {
207218
post.updateVideo(reqBody.videoUrl());
208219
}
@@ -214,6 +225,13 @@ public void afterCompletion(int status) {
214225
return new PostResponseDto(post);
215226
}
216227

228+
private String extractFileNameFromUrl(String url) {
229+
if (url == null) return null;
230+
int lastSlash = url.lastIndexOf('/');
231+
return (lastSlash != -1) ? url.substring(lastSlash + 1) : url;
232+
}
233+
234+
217235
// 게시글 삭제 로직
218236
@Transactional
219237
public void deletePost(Long postId) {
@@ -272,6 +290,17 @@ public PostLikeResponseDto toggleLike(Long postId) {
272290
}
273291
}
274292

293+
// 사용자가 해당 게시글 여부 확인 로직
294+
@Transactional(readOnly = true)
295+
public Boolean getLike(Long postId) {
296+
User user = rq.getActor();
297+
298+
Post post = postRepository.findById(postId)
299+
.orElseThrow(() -> new NoSuchElementException("해당 게시글을 찾을 수 없습니다. ID: " + postId));
300+
301+
return postLikeRepository.existsByPostAndUser(post, user);
302+
}
303+
275304
// 태그 추가 메서드
276305
private void addTag(List<String> tagNames, Post post) {
277306
for (String tagName : tagNames) {

src/main/java/com/back/domain/user/controller/UserAuthController.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@ public RsData<Void> logout(HttpServletRequest request, HttpServletResponse respo
5555

5656
@Operation(summary = "현재 로그인한 유저 정보 조회", description = "세션 유효성 검증 및 사용자 정보 반환")
5757
@ApiResponses(value = {
58-
@ApiResponse(responseCode = "200", description = "인증된 유저 정보 반환 성공"),
59-
@ApiResponse(responseCode = "401", description = "인증되지 않은 사용자"),
60-
@ApiResponse(responseCode = "500", description = "서버 내부 오류")
58+
@ApiResponse(responseCode = "200", description = "사용자 정보 조회"),
6159
})
6260

6361
@GetMapping("/me")

src/main/java/com/back/domain/user/service/UserAuthService.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -234,21 +234,28 @@ public void setFirstLoginFalse(Long id) {
234234
}
235235

236236
// 현재 로그인한 사용자 정보 조회 (세션 검증용)
237+
// 변경: 항상 200 응답, 비로그인 시 user: null 반환
237238
public UserMeResDto getCurrentUser() {
238239
try {
239240
User actor = rq.getActor();
240241

242+
// 비로그인 상태: user null 반환
241243
if (actor == null) {
242-
log.debug("인증되지 않은 사용자");
243-
throw new ServiceException(401, "인증되지 않은 사용자");
244+
log.debug("인증되지 않은 사용자 - user: null 반환");
245+
return UserMeResDto.builder()
246+
.user(null)
247+
.build();
244248
}
245249

246250
Optional<User> userOpt = userRepository.findById(actor.getId());
247251
if (userOpt.isEmpty()) {
248252
log.warn("사용자 ID {}를 DB에서 찾을 수 없음 (토큰은 유효하나 사용자 삭제됨)", actor.getId());
249-
throw new ServiceException(401, "인증되지 않은 사용자");
253+
return UserMeResDto.builder()
254+
.user(null)
255+
.build();
250256
}
251257

258+
// 로그인 상태: user 정보 반환
252259
User user = userOpt.get();
253260
String provider = extractProvider(user.getOauthId());
254261

@@ -263,11 +270,12 @@ public UserMeResDto getCurrentUser() {
263270
.build())
264271
.build();
265272

266-
} catch (ServiceException e) {
267-
throw e;
268273
} catch (Exception e) {
269274
log.error("사용자 정보 조회 중 서버 오류 발생: {}", e.getMessage(), e);
270-
throw new ServiceException(500, "서버 내부 오류");
275+
// 예외 발생 시에도 user: null 반환
276+
return UserMeResDto.builder()
277+
.user(null)
278+
.build();
271279
}
272280
}
273281

0 commit comments

Comments
 (0)