Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] 게시글 조회시 각종 이미지 조회 추가 #119 #136

Merged
merged 11 commits into from
Jan 23, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ public interface FileService {
*/
String getProfileImgUrl(String referenceId);

/**
* 게시글 대표 이미지 url 반환
* @param referenceId 참조된 게시글 id
* @return 해당 게시글의 대표 이미지
*/
String getPostThumbnailImgUrl(Long referenceId);

/**
* 게시글 연관 이미지 url 리스트 반환
* @param referenceId 참조된 게시글 id
* @return 해당 게시글에 연관된 이미지 리스트
*/
List<String> getPostImgUrls(Long referenceId);

/**
* 파일 url 리스트 반환
* @param fileRequest ReferenceType(참조 테이블명)과 ReferenceId(참조 id) 입력
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,35 @@ public String getProfileImgUrl(String referenceId) {
return s3Service.createPresignedGetUrl(key);
}

@Override
public String getPostThumbnailImgUrl(Long referenceId) {
FileRequest fileRequest = FileRequest.of(FileReferenceType.COMMUNITY_BOARD, referenceId);

List<File> files = fileRepository.findFilesByFileReference(fileRequest);
if (files.isEmpty()) {
log.warn("파일이 없습니다. 파일 참조 정보: {}", fileRequest);
return null;
}

return s3Service.createPresignedGetUrl(files.get(0).getS3Key());
}

public List<String> getPostImgUrls(Long referenceId) {
FileRequest fileRequest = FileRequest.of(FileReferenceType.COMMUNITY_BOARD, referenceId);

List<File> files = fileRepository.findFilesByFileReference(fileRequest);
if (files.isEmpty()) {
log.warn("파일이 없습니다. 파일 참조 정보: {}", fileRequest);
return List.of();
}

List<String> paths = new ArrayList<>(files.size());
for (File f : files) {
paths.add(s3Service.createPresignedGetUrl(f.getS3Key()));
}
return paths;
}

@Override
public List<String> getFileUrls(FileRequest fileRequest) {

Expand Down Expand Up @@ -151,6 +180,8 @@ public void updateImagesAtOnce(List<MultipartFile> file, FileRequest fileRequest

List<File> files = fileRepository.findFilesByFileReference(fileRequest);

validFilesNotEmpty(files, fileRequest);

}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public interface PostController {
@ApiResponse(responseCode = "201", description = "이미지 업로드 및 게시글 등록 성공")
@ApiResponse(responseCode = "413", description = "이미지 용량 초과로 등록 실패")
@ApiResponse(responseCode = "400", description = "권한이 없는 회원이거나 유효하지 않는 데이터로 등록 실패")
ResponseEntity<PostRegisterResponse> write(
ResponseEntity<PostRegisterResponse> writeCommunityPost(
CustomOAuth2User auth,
List<MultipartFile> fileList,
PostRegisterRequest postRegisterRequest
Expand All @@ -43,7 +43,7 @@ ResponseEntity<PostRegisterResponse> write(
summary = "커뮤니티 게시글 수정"
)
@PutMapping(path = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity modify(
ResponseEntity modifyCommunityPost(
@AuthenticationPrincipal CustomOAuth2User auth,
@PathVariable("id") Long postId,
@Valid @RequestBody PostUpdateRequest postUpdateRequest
Expand All @@ -55,7 +55,7 @@ ResponseEntity modify(
summary = "커뮤니티 게시글 삭제"
)
@DeleteMapping(path = "/{id}")
ResponseEntity delete(
ResponseEntity deleteCommunityPost(
@AuthenticationPrincipal CustomOAuth2User auth,
@PathVariable("id") Long postId
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class PostControllerImpl implements PostController {
/* 게시글 등록 */
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Override
public ResponseEntity<PostRegisterResponse> write(
public ResponseEntity<PostRegisterResponse> writeCommunityPost(
@AuthenticationPrincipal CustomOAuth2User auth,
@RequestPart(value = "imageFiles", required = false) List<MultipartFile> imageFileList,
@Valid @RequestPart(value = "postDetail") PostRegisterRequest postRegisterRequest
Expand All @@ -58,7 +58,7 @@ public ResponseEntity<PostRegisterResponse> write(
/* 게시글 수정 */
@PutMapping(path = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE)
@Override
public ResponseEntity modify(
public ResponseEntity modifyCommunityPost(
@AuthenticationPrincipal CustomOAuth2User auth,
@PathVariable("id") Long postId,
@Valid @RequestBody PostUpdateRequest postUpdateRequest
Expand All @@ -76,7 +76,7 @@ public ResponseEntity modify(
/* 게시글 삭제 */
@DeleteMapping(path = "/{id}")
@Override
public ResponseEntity delete(
public ResponseEntity deleteCommunityPost(
@AuthenticationPrincipal CustomOAuth2User auth,
@PathVariable("id") Long postId
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public ResponseEntity<PostDetailResponse> getPostDetail(

PostDetailResponse response = postDetailService.getPostDetail(auth.getId(), id);

postDetailService.incrementViewCount(id);

return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.palbang.unsemawang.community.dto.response;

import java.time.LocalDateTime;
import java.util.List;

import com.palbang.unsemawang.community.constant.CommunityCategory;

Expand Down Expand Up @@ -31,6 +32,12 @@ public class PostDetailResponse {
@Schema(description = "작성자 닉네임 (익명일 경우 '익명')", required = true, example = "닉네임")
private String nickname;

@Schema(description = "작성자 프로필 사진 URL", required = false, example = "https://...")
private String profileImageUrl;

@Schema(description = "이미지 url 목록", required = true, example = "[\"https://...\" , ... ]")
private List<String> postImageUrls;

@Schema(description = "익명 여부(익명 O : true / 익명 X : false)", required = true, example = "false")
private Boolean isAnonymous;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.time.LocalDateTime;

import com.palbang.unsemawang.common.util.file.service.FileService;
import com.palbang.unsemawang.community.constant.CommunityCategory;
import com.palbang.unsemawang.community.entity.Post;

Expand Down Expand Up @@ -37,6 +36,9 @@ public class PostListResponse {
@Schema(description = "사용자 닉네임", required = true, example = "유저 닉네임")
private String nickname;

@Schema(description = "작성자 프로필 사진 URL", required = false, example = "https://...")
private String profileImageUrl;

@Schema(description = "게시판 카테고리", required = true, example = "자유 게시판")
private CommunityCategory communityCategory;

Expand All @@ -46,12 +48,10 @@ public class PostListResponse {
@Schema(description = "게시글 수정 시각", required = true, example = "2023-12-02T10:00:00")
private LocalDateTime updatedAt;

// @Schema(description = "게시글 이미지 URL", required = false)
// private String imageUrl;

public static PostListResponse fromEntity(Post post, FileService fileService) {
// String imageUrl = getImageUrl(post, fileService);
@Schema(description = "게시글 이미지 URL", required = false)
private String imageUrl;

public static PostListResponse fromEntity(Post post, String imageUrl, String profileImageUrl) {
return PostListResponse.builder()
.cursorId(post.getId())
.id(post.getId())
Expand All @@ -61,20 +61,12 @@ public static PostListResponse fromEntity(Post post, FileService fileService) {
.likeCount(post.getLikeCount())
.commentCount(post.getCommentCount())
.nickname(post.getIsAnonymous() ? "익명" : post.getMember().getNickname())
.profileImageUrl(profileImageUrl)
.communityCategory(post.getCommunityCategory())
.registeredAt(post.getRegisteredAt())
.updatedAt(post.getUpdatedAt())
// .imageUrl(imageUrl)
.imageUrl(imageUrl)
.build();
}

// private static String getImageUrl(Post post, FileService fileService) {
// FileRequest fileRequest = FileRequest.of(FileReferenceType.COMMUNITY_BOARD, post.getId().toString());
//
// // FileService를 통해 파일 URL 리스트 가져옴
// List<String> fileUrls = fileService.getFileUrls(fileRequest);
//
// // 썸네일 URL로 첫 번째 파일 선택 (없을 경우 기본 이미지 반환)
// return fileUrls.isEmpty() ? "https://default-image-url.com/default.jpg" : fileUrls.get(0);
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ public class Post extends BaseEntity {
@Column(name = "deleted_at")
private LocalDateTime deletedAt;

public String getWriterId() {
return this.member.getId();
}

public void deletePost() {
this.isDeleted = true;
this.deletedAt = LocalDateTime.now();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
package com.palbang.unsemawang.community.service;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.palbang.unsemawang.common.constants.ResponseCode;
import com.palbang.unsemawang.common.exception.GeneralException;
import com.palbang.unsemawang.common.util.file.service.FileService;
import com.palbang.unsemawang.community.dto.response.PostDetailResponse;
import com.palbang.unsemawang.community.entity.Post;
import com.palbang.unsemawang.community.repository.PostRepository;

import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class PostDetailService {

private final FileService fileService;
private final PostRepository postRepository;

@Transactional
@Transactional(readOnly = true)
public PostDetailResponse getPostDetail(String memberId, Long postId) {
Post post = postRepository.findById(postId)
.orElseThrow(() -> new GeneralException(ResponseCode.RESOURCE_NOT_FOUND));
Expand All @@ -27,25 +31,31 @@ public PostDetailResponse getPostDetail(String memberId, Long postId) {
throw new GeneralException(ResponseCode.FORBIDDEN); // 적절한 응답 코드 사용
}

// DB 레벨에서 조회수 증가
incrementViewCount(postId);
// 게시글에 포함된 이미지 리스트 조회
List<String> imageUrls = fileService.getPostImgUrls(post.getId());

return toResponseDto(post);
// 작성자의 프로필 이미지 조회
String profileImage = post.getIsAnonymous() ? null : fileService.getProfileImgUrl(memberId);

return toResponseDto(post, imageUrls, profileImage);
}

// 조회수 증가 - DB 레벨 처리
private void incrementViewCount(Long postId) {
@Transactional
public void incrementViewCount(Long postId) {
postRepository.incrementViewCount(postId);
}

// Dto 컨버터
private PostDetailResponse toResponseDto(Post post) {
private PostDetailResponse toResponseDto(Post post, List<String> imageUrls, String profileImageUrl) {
return PostDetailResponse.builder()
.id(post.getId())
.memberId(post.getMember().getId())
.title(post.getTitle())
.content(post.getContent())
.nickname(post.getIsAnonymous() ? "익명" : post.getMember().getNickname())
.profileImageUrl(profileImageUrl)
.postImageUrls(imageUrls)
.isAnonymous(post.getIsAnonymous())
.isVisible(post.getIsVisible())
.viewCount(post.getViewCount())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ public LongCursorResponse<PostListResponse> getPostList(CommunityCategory catego

// 응답 데이터 매핑 (FileService를 통해 썸네일 URL 포함)
List<PostListResponse> data = posts.stream()
.map(post -> PostListResponse.fromEntity(post, fileService)) // `FileService` 추가 전달
.map(post -> PostListResponse.fromEntity(
post,
fileService.getPostThumbnailImgUrl(post.getId()),
post.getIsAnonymous() ? null : fileService.getProfileImgUrl(post.getWriterId())))
.toList();

// 다음 커서 생성
Expand Down
5 changes: 2 additions & 3 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,5 @@ cloud.aws.s3.bucket=${S3_BUCKET_NAME}
# MultipartFile
spring.servlet.multipart.max-file-size=3MB
spring.servlet.multipart.max-request-size=15MB
# HTTP form ?? ???
server.tomcat.max-http-form-post-size=15MB

# HTTP form request size
server.tomcat.max-http-form-post-size=15MB
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import com.palbang.unsemawang.common.constants.ResponseCode;
import com.palbang.unsemawang.common.exception.GeneralException;
import com.palbang.unsemawang.common.util.file.service.FileService;
import com.palbang.unsemawang.community.dto.response.PostDetailResponse;
import com.palbang.unsemawang.community.entity.Post;
import com.palbang.unsemawang.community.repository.PostRepository;
Expand All @@ -23,6 +24,9 @@ class PostDetailServiceTest {
@InjectMocks
private PostDetailService postDetailService;

@Mock
private FileService fileService;

@Mock
private PostRepository postRepository;

Expand Down Expand Up @@ -54,7 +58,6 @@ void getPostDetail_SuccessfullyIncrementsViewCount() {

// Assert
assertNotNull(response);
verify(postRepository).incrementViewCount(postId); // 조회수 증가 메서드 호출 확인
assertEquals(postId, response.getId());
}

Expand Down Expand Up @@ -82,7 +85,6 @@ void getPostDetail_SuccessForPrivatePostByOwner() {

// Assert
assertNotNull(response);
verify(postRepository).incrementViewCount(postId); // 조회수 증가 메서드 호출 확인
assertEquals(postId, response.getId());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.mockito.MockitoAnnotations;
import org.springframework.data.domain.PageRequest;

import com.palbang.unsemawang.common.util.file.service.FileService;
import com.palbang.unsemawang.common.util.pagination.CursorRequest;
import com.palbang.unsemawang.common.util.pagination.LongCursorResponse;
import com.palbang.unsemawang.community.constant.CommunityCategory;
Expand All @@ -25,6 +26,9 @@ public class PostListServiceTest {
@InjectMocks
private PostListService postListService; // 테스트 대상 클래스

@Mock
private FileService fileService;

@Mock
private PostRepository postRepository; // Mock 객체로 설정

Expand Down
Loading