package shop.mtcoding.blog.board;
import lombok.Data;
import shop.mtcoding.blog.reply.Reply;
import shop.mtcoding.blog.user.User;
import java.util.ArrayList;
import java.util.List;
public class BoardResponse {
// 게시글 상세보기 화면
@Data
public static class DetailDTO {
private Integer id;
private String title;
private String content;
private UserDTO user;
private Boolean isOwner; // 좋아요도 마찬가지
private List<ReplyDTO> replies = new ArrayList<>();
@Data
public class UserDTO {
private int id;
private String username;
public UserDTO(User user) {
this.id = user.getId();
this.username = user.getUsername();
}
}
// 응답할 때에는 생성자를 만들어야함
public DetailDTO(Board board, User sessionUser) {
this.id = board.getId();
this.title = board.getTitle();
this.content = board.getContent();
this.user = new UserDTO(board.getUser());
this.isOwner = false;
if (sessionUser != null) {
if (sessionUser.getId() == board.getUser().getId()) {
isOwner = true;
}
}
this.replies = board.getReplies().stream().map(reply -> new ReplyDTO(reply, sessionUser)).toList();
}
}
}
3. BoardResponse 에 ReplyDTO 만들기
package shop.mtcoding.blog.board;
import lombok.Data;
import shop.mtcoding.blog.reply.Reply;
import shop.mtcoding.blog.user.User;
import java.util.ArrayList;
import java.util.List;
public class BoardResponse {
// 게시글 상세보기 화면
@Data
public static class DetailDTO {
private Integer id;
private String title;
private String content;
private UserDTO user;
private Boolean isOwner; // 좋아요도 마찬가지
private List<ReplyDTO> replies = new ArrayList<>();
@Data
public class UserDTO {
private int id;
private String username;
public UserDTO(User user) {
this.id = user.getId();
this.username = user.getUsername();
}
}
@Data
public class ReplyDTO {
private Integer id;
private String comment;
private Integer userId; // 댓글 작성자 아이디
private String username; // 댓글 작성자 이름
private Boolean isOwner;
public ReplyDTO(Reply reply, User sessionUser) {
this.id = reply.getId();
this.comment = reply.getComment(); // lazyloading 발동
this.userId = reply.getUser().getId();
this.username = reply.getUser().getUsername(); // lazyloading 발동
this.isOwner = false;
if (sessionUser != null) {
if (sessionUser.getId() == reply.getUser().getId()) {
isOwner = true;
}
}
}
}
// 응답할 때에는 생성자를 만들어야함
public DetailDTO(Board board, User sessionUser) {
this.id = board.getId();
this.title = board.getTitle();
this.content = board.getContent();
this.user = new UserDTO(board.getUser());
this.isOwner = false;
if (sessionUser != null) {
if (sessionUser.getId() == board.getUser().getId()) {
isOwner = true;
}
}
this.replies = board.getReplies().stream().map(reply -> new ReplyDTO(reply, sessionUser)).toList();
}
}
}
4. BoardService 에 detail() 수정하기
package shop.mtcoding.blog.board;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import shop.mtcoding.blog._core.errors.exception.Exception403;
import shop.mtcoding.blog._core.errors.exception.Exception404;
import shop.mtcoding.blog.user.User;
import java.util.List;
@RequiredArgsConstructor
@Service
public class BoardService {
private final BoardJPARepository boardJPARepository;
// board와 isOwner를 응답해야하나 method는 하나밖에 응답할 수 없음 -> 하나의 덩어리로 만들어서 줘야 함
public BoardResponse.DetailDTO detail(int boardId, User sessionUser) {
Board board = boardJPARepository.findByIdJoinUser(boardId)
.orElseThrow(() -> new Exception404("게시글을 찾을 수 없습니다"));
return new BoardResponse.DetailDTO(board, sessionUser);
}
}
5. BoardController 에 detail 수정하기
총 쿼리가 3번 돌아감
댓글 작성자가 2명이라 2번 날아가야하나 default match size로 1번만 날아감
join → 댓글 select(getuser) → 댓글 select(getid)
lazy loading할때 단건이 아니면, many to one 관계인 애들은 인쿼리에 둠
→ 보드랑 유저 조인, 댓글 유저 조인 → 셀렉트 2번 →dto 만들어서 들고오기
한방 쿼리 / lazy loading이 발동하지 않음 → 다 join 했으니까!
@Query("select b from Board b join fetch b.user left join fetch b.replies r join fetch r.user ru where b.id = :id")
Board findDetail(@Param("id") int id);
package shop.mtcoding.blog.board;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import shop.mtcoding.blog._core.utils.ApiUtil;
import shop.mtcoding.blog.user.User;
import java.util.List;
@RequiredArgsConstructor
@RestController
public class BoardController {
private final HttpSession session;
private final BoardService boardService;
@GetMapping("/api/boards/{id}/detail")
public ResponseEntity<?> detail(@PathVariable Integer id) {
User sessionUser = (User) session.getAttribute("sessionUser");
BoardResponse.DetailDTO respDTO = boardService.detail(id, sessionUser);
return ResponseEntity.ok(new ApiUtil<>(respDTO)); // board에 연관된 객체가 있기에 위험함 / 무한 참조가 일어날 수 있음
}
}