diff --git a/backend/src/main/java/org/sejongisc/backend/board/dto/BoardResponse.java b/backend/src/main/java/org/sejongisc/backend/board/dto/BoardResponse.java index b7a32c94..b96c7bb8 100644 --- a/backend/src/main/java/org/sejongisc/backend/board/dto/BoardResponse.java +++ b/backend/src/main/java/org/sejongisc/backend/board/dto/BoardResponse.java @@ -8,7 +8,7 @@ import lombok.Setter; import lombok.ToString; import org.sejongisc.backend.board.entity.Board; -import org.sejongisc.backend.user.entity.User; +import org.sejongisc.backend.user.dto.UserInfoResponse; @ToString @AllArgsConstructor @@ -22,15 +22,15 @@ public class BoardResponse { private String boardName; - private User createdBy; + private UserInfoResponse createdBy; private UUID parentBoardId; - public static BoardResponse of(Board board) { + public static BoardResponse from(Board board) { return BoardResponse.builder() .boardId(board.getBoardId()) .boardName(board.getBoardName()) - .createdBy(board.getCreatedBy()) + .createdBy(UserInfoResponse.from(board.getCreatedBy())) .parentBoardId(board.getParentBoard() != null ? board.getParentBoard().getBoardId() : null) .build(); } diff --git a/backend/src/main/java/org/sejongisc/backend/board/dto/CommentResponse.java b/backend/src/main/java/org/sejongisc/backend/board/dto/CommentResponse.java index 3e8f4092..eb2a7879 100644 --- a/backend/src/main/java/org/sejongisc/backend/board/dto/CommentResponse.java +++ b/backend/src/main/java/org/sejongisc/backend/board/dto/CommentResponse.java @@ -10,7 +10,7 @@ import lombok.Setter; import lombok.ToString; import org.sejongisc.backend.board.entity.Comment; -import org.sejongisc.backend.user.entity.User; +import org.sejongisc.backend.user.dto.UserInfoResponse; @ToString @AllArgsConstructor @@ -20,17 +20,17 @@ @Builder public class CommentResponse { private UUID commentId; - private User user; + private UserInfoResponse user; private UUID postId; private String content; private LocalDateTime createdDate; private LocalDateTime updatedDate; private List replies; - public static CommentResponse of(Comment comment) { + public static CommentResponse from(Comment comment) { return CommentResponse.builder() .commentId(comment.getCommentId()) - .user(comment.getUser()) + .user(UserInfoResponse.from(comment.getUser())) .postId(comment.getPost().getPostId()) .content(comment.getContent()) .createdDate(comment.getCreatedDate()) @@ -38,10 +38,10 @@ public static CommentResponse of(Comment comment) { .build(); } - public static CommentResponse of(Comment comment, List replies) { + public static CommentResponse from(Comment comment, List replies) { return CommentResponse.builder() .commentId(comment.getCommentId()) - .user(comment.getUser()) + .user(UserInfoResponse.from(comment.getUser())) .postId(comment.getPost().getPostId()) .content(comment.getContent()) .createdDate(comment.getCreatedDate()) diff --git a/backend/src/main/java/org/sejongisc/backend/board/dto/PostResponse.java b/backend/src/main/java/org/sejongisc/backend/board/dto/PostResponse.java index 013b4d3e..780ce15c 100644 --- a/backend/src/main/java/org/sejongisc/backend/board/dto/PostResponse.java +++ b/backend/src/main/java/org/sejongisc/backend/board/dto/PostResponse.java @@ -9,8 +9,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; -import org.sejongisc.backend.board.entity.Board; -import org.sejongisc.backend.user.entity.User; +import org.sejongisc.backend.user.dto.UserInfoResponse; import org.springframework.data.domain.Page; @ToString @@ -22,8 +21,8 @@ public class PostResponse { private UUID postId; - private Board board; - private User user; + private BoardResponse board; + private UserInfoResponse user; private String title; private String content; private Integer bookmarkCount; diff --git a/backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java b/backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java index 5d00a363..a0818442 100644 --- a/backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java +++ b/backend/src/main/java/org/sejongisc/backend/board/service/PostServiceImpl.java @@ -23,6 +23,7 @@ import org.sejongisc.backend.common.exception.CustomException; import org.sejongisc.backend.common.exception.ErrorCode; import org.sejongisc.backend.user.dao.UserRepository; +import org.sejongisc.backend.user.dto.UserInfoResponse; import org.sejongisc.backend.user.entity.User; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -235,11 +236,11 @@ public PostResponse getPostDetail(UUID postId, int pageNumber, int pageSize) { // 자식 댓글 목록을 CommentResponse DTO 리스트로 변환 List replyResponses = childComments.stream() - .map(CommentResponse::of) + .map(CommentResponse::from) .toList(); // 부모 댓글 DTO를 생성하며, 자식 DTO 리스트를 주입 - return CommentResponse.of(parent, replyResponses); + return CommentResponse.from(parent, replyResponses); }); // 첨부 파일 조회 @@ -251,8 +252,8 @@ public PostResponse getPostDetail(UUID postId, int pageNumber, int pageSize) { // PostResponse DTO를 직접 빌드하여 반환 return PostResponse.builder() .postId(post.getPostId()) - .board(post.getBoard()) - .user(post.getUser()) + .board(BoardResponse.from(post.getBoard())) + .user(UserInfoResponse.from(post.getUser())) .title(post.getTitle()) .content(post.getContent()) .bookmarkCount(post.getBookmarkCount()) @@ -302,15 +303,15 @@ public List getParentBoards() { List parentBoards = boardRepository.findAllByParentBoardIsNull(); return parentBoards.stream() - .map(BoardResponse::of) + .map(BoardResponse::from) .toList(); } private PostResponse mapToPostResponse(Post post) { return PostResponse.builder() .postId(post.getPostId()) - .user(post.getUser()) - .board(post.getBoard()) + .user(UserInfoResponse.from(post.getUser())) + .board(BoardResponse.from(post.getBoard())) .title(post.getTitle()) .content(post.getContent()) .bookmarkCount(post.getBookmarkCount()) diff --git a/backend/src/main/java/org/sejongisc/backend/user/dto/UserInfoResponse.java b/backend/src/main/java/org/sejongisc/backend/user/dto/UserInfoResponse.java index 1c89f847..262631e3 100644 --- a/backend/src/main/java/org/sejongisc/backend/user/dto/UserInfoResponse.java +++ b/backend/src/main/java/org/sejongisc/backend/user/dto/UserInfoResponse.java @@ -1,11 +1,12 @@ package org.sejongisc.backend.user.dto; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Getter; - import java.util.Collection; import java.util.UUID; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.sejongisc.backend.common.auth.springsecurity.CustomUserDetails; +import org.sejongisc.backend.user.entity.User; @Getter @AllArgsConstructor @@ -56,4 +57,30 @@ public class UserInfoResponse { example = "[\"ROLE_USER\"]" ) private Collection authorities; + + public static UserInfoResponse from(CustomUserDetails user) { + if (user == null) { + return null; + } + + return new UserInfoResponse( + user.getUserId(), + user.getName(), + user.getEmail(), + user.getPhoneNumber(), + user.getPoint(), + user.getRole().name(), + user.getAuthorities() + ); + } + + public static UserInfoResponse from(User user) { + if (user == null) { + return null; + } + + CustomUserDetails userDetails = new CustomUserDetails(user); + + return from(userDetails); + } } diff --git a/backend/src/test/java/org/sejongisc/backend/board/service/PostServiceImplTest.java b/backend/src/test/java/org/sejongisc/backend/board/service/PostServiceImplTest.java index d94c7f77..d7cedc68 100644 --- a/backend/src/test/java/org/sejongisc/backend/board/service/PostServiceImplTest.java +++ b/backend/src/test/java/org/sejongisc/backend/board/service/PostServiceImplTest.java @@ -38,6 +38,7 @@ import org.sejongisc.backend.common.exception.CustomException; import org.sejongisc.backend.common.exception.ErrorCode; import org.sejongisc.backend.user.dao.UserRepository; +import org.sejongisc.backend.user.entity.Role; // Role Enum import 필요 import org.sejongisc.backend.user.entity.User; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -86,9 +87,28 @@ void setUp() { boardId = UUID.randomUUID(); postId = UUID.randomUUID(); - mockUser = User.builder().userId(userId).build(); - mockParentBoard = Board.builder().boardId(UUID.randomUUID()).parentBoard(null).build(); - mockBoard = Board.builder().boardId(boardId).parentBoard(mockParentBoard).build(); + // DTO 변환(UserInfoResponse.from)시 NPE 방지를 위해 Role, Name, Email 등 필수 필드 세팅 + mockUser = User.builder() + .userId(userId) + .email("test@example.com") + .name("Tester") + .role(Role.TEAM_MEMBER) // 또는 Role.USER (프로젝트 Enum 정의에 맞게) + .build(); + + mockParentBoard = Board.builder() + .boardId(UUID.randomUUID()) + .boardName("Parent Board") + .parentBoard(null) + .createdBy(mockUser) + .build(); + + mockBoard = Board.builder() + .boardId(boardId) + .boardName("Child Board") + .parentBoard(mockParentBoard) + .createdBy(mockUser) // BoardResponse.from 호출 시 필요 + .build(); + mockPost = Post.builder() .postId(postId) .user(mockUser) @@ -118,7 +138,7 @@ void savePost_Success() { when(userRepository.findById(userId)).thenReturn(Optional.of(mockUser)); when(fileUploadService.store(any(MultipartFile.class))).thenReturn("saved_filename.txt"); when(fileUploadService.getRootLocation()).thenReturn(Paths.get("test/path")); - when(postRepository.save(any(Post.class))).thenReturn(mockPost); // 저장된 post 반환 + when(postRepository.save(any(Post.class))).thenReturn(mockPost); // when postService.savePost(request, userId); @@ -153,7 +173,7 @@ void updatePost_Success() { PostRequest request = PostRequest.builder() .title("Updated Title") .content("Updated Content") - .files(Collections.emptyList()) // 테스트 편의상 새 파일은 없음 + .files(Collections.emptyList()) .build(); PostAttachment oldAttachment = PostAttachment.builder().savedFilename("old_file.txt").build(); @@ -180,11 +200,11 @@ void updatePost_Fail_InvalidOwner() { PostRequest request = PostRequest.builder().build(); // Mocking - when(postRepository.findById(postId)).thenReturn(Optional.of(mockPost)); // mockPost의 user는 'userId' + when(postRepository.findById(postId)).thenReturn(Optional.of(mockPost)); // when & then CustomException exception = assertThrows(CustomException.class, () -> { - postService.updatePost(request, postId, otherUserId); // 다른 'otherUserId'로 수정 시도 + postService.updatePost(request, postId, otherUserId); }); assertThat(exception.getErrorCode()).isEqualTo(ErrorCode.INVALID_POST_OWNER); @@ -228,11 +248,16 @@ void getPosts_Success() { when(postRepository.findAllByBoard(mockBoard, pageable)).thenReturn(postPage); // when + // 여기서 mapToPostResponse 내부적으로 UserInfoResponse.from(), BoardResponse.from()이 호출됨 + // mockUser에 Role 정보가 없으면 NPE 발생 가능 (setUp에서 처리함) Page result = postService.getPosts(boardId, page, size); // then assertThat(result.getTotalElements()).isEqualTo(1); assertThat(result.getContent().get(0).getPostId()).isEqualTo(postId); + // DTO 변환 확인 + assertThat(result.getContent().get(0).getUser().getName()).isEqualTo(mockUser.getName()); + assertThat(result.getContent().get(0).getBoard().getBoardName()).isEqualTo(mockBoard.getBoardName()); } @Test @@ -244,8 +269,21 @@ void getPostDetail_Success_WithReplies() { Pageable commentPageable = PageRequest.of(page, size, Sort.by(Sort.Direction.ASC, "createdDate")); // 댓글 Mock 데이터 생성 - Comment parentComment = Comment.builder().commentId(UUID.randomUUID()).post(mockPost).content("부모댓글1").parentComment(null).build(); - Comment childComment = Comment.builder().commentId(UUID.randomUUID()).post(mockPost).content("대댓글1").parentComment(parentComment).build(); + Comment parentComment = Comment.builder() + .commentId(UUID.randomUUID()) + .post(mockPost) + .user(mockUser) // 댓글 작성자 필요 (DTO 변환 위해) + .content("부모댓글1") + .parentComment(null) + .build(); + + Comment childComment = Comment.builder() + .commentId(UUID.randomUUID()) + .post(mockPost) + .user(mockUser) // 댓글 작성자 필요 + .content("대댓글1") + .parentComment(parentComment) + .build(); Page parentCommentPage = new PageImpl<>(List.of(parentComment), commentPageable, 1); @@ -293,8 +331,9 @@ void createBoard_Success() { // Mocking when(userRepository.findById(userId)).thenReturn(Optional.of(mockUser)); + when(boardRepository.findById(mockParentBoard.getBoardId())).thenReturn(Optional.of(mockParentBoard)); - // ArgumentCaptor: save(board)에 실제 어떤 board 객체가 전달되었는지 잡기 위함 + // ArgumentCaptor ArgumentCaptor boardCaptor = ArgumentCaptor.forClass(Board.class); // when