1. 게시글 상세보기 화면

2. 게시글 상세보기
1. BoardController
@GetMapping("/board/{id}")
public String detail(@PathVariable("id") int id, HttpServletRequest request) {
Board board = boardService.글상세보기(id);
request.setAttribute("model", board);
return "board/detail";
}2. BoardService
public Board 글상세보기(int id) {
// Board board = boardRepository.findById(id); // LAZY 로딩이므로 Board만 조회해서 board 정보 밖에 없다.
// board.getUser().getEmail(); // 원래 null인데 lazy로딩이 발동해서 해당 유저 id로 select가 발동해서 값을 넣어준다. -> 비효율적이므로 안쓴다!
return boardRepository.findByIdJoinUser(id);
}3. BoardRepository
public Board findByIdJoinUser(Integer id) { // Board를 조회할건데 User를 join
// 객체지향쿼리 - board 객체 안에 있는 user와 join(relation해서 join) 해야하므로 User가 아닌 b.user과 join해야한다.
// select b : board에 있는 필드만 projection
// join fetch : b.user 안에 있는 것까지 전부 projection
Query query = em.createQuery("select b from Board b join fetch b.user u where b.id = :id", Board.class); // inner join의 경우 pk, fk 연결 시 on절 생략 가능
query.setParameter("id", id);
return (Board) query.getSingleResult();
}4. board/detail.mustache

3. Join Query Test
0. BoardRepositoryTest
/*
* 1. 쿼리가 어떻게 동작하는지
* 2. fetch가 없으면
* 3. b.user가 아니면
*/
@Test
public void findByIdJoinUser_test() {
// given
Integer boardId = 1;
// when
boardRepository.findByIdJoinUser(boardId);
// eye
} 1 . BoardRepositoryTest (1) - 쿼리가 어떻게 동작할까?


2 . BoardRepositoryTest (2) - fetch가 없으면 어떻게 동작할까?


3. BoardRepositoryTest (2) - b.user가 아니라 User로 join하면 어떻게 동작할까?
이때는 fetch가 있든 없든 동일한 에러가 발생한다!



4. 자기 게시글만 수정, 삭제 버튼이 보이도록 게시글 상세보기

→ 게시글 주인인지에 대한 새로운 필드가 하나 필요하다!
→ Board를 건들면 안됨 ⇒ DTO가 필요!

public class BoardResponse {
@Data
public static class DetailDTO {
private Integer id;
private String title;
private String content;
private Boolean isPublic;
private Boolean isOwner; // 값이 안 들어갈 경우: Boolean - null / boolean - 0
private String username; // User 객체를 다 들고 갈 필요X
private Timestamp createdAt;
// model에 있는 것을 옮기는 것
// 깊은 복사 : 객체를 그대로 가져와서 getId 등으로 넣는게 낫다!
public DetailDTO(Board board, Integer sessionUserId) {
this.id = board.getId();
this.title = board.getTitle();
this.content = board.getContent();
this.isPublic = board.getIsPublic();
this.isOwner = sessionUserId == board.getUser().getId();
this.username = board.getUser().getUsername();
this.createdAt = board.getCreatedAt();
}
}
}@GetMapping("/board/{id}")
public String detail(@PathVariable("id") int id, HttpServletRequest request) {
User sessionUser = (User) session.getAttribute("sessionUser");
BoardResponse.DetailDTO detailDTO = boardService.글상세보기(id, sessionUser.getId());
request.setAttribute("model", detailDTO);
return "board/detail";
}public BoardResponse.DetailDTO 글상세보기(int id, Integer userId) {
// Board board = boardRepository.findById(id); // LAZY 로딩이므로 Board만 조회해서 board 정보 밖에 없다.
// board.getUser().getEmail(); // 원래 null인데 lazy로딩이 발동해서 해당 유저 id로 select가 발동해서 값을 넣어준다. -> 비효율적이므로 안쓴다!
Board board = boardRepository.findByIdJoinUser(id);
BoardResponse.DetailDTO detailDTO = new BoardResponse.DetailDTO(board, userId);
return detailDTO;
}5. 로그인하지 않은 상태로 게시글 상세보기를 할 경우
sessionUser가 null이 되므로 글상세보기(id, sessionUser.getId())중 sessionUser.getId()에서 터진다.
따라서 sessionUser가 null일 경우를 미리 처리하는 것이 좋다.

@GetMapping("/board/{id}")
public String detail(@PathVariable("id") int id, HttpServletRequest request) {
User sessionUser = (User) session.getAttribute("sessionUser");
Integer sessionUserId = (sessionUser == null) ? null : sessionUser.getId();
BoardResponse.DetailDTO detailDTO = boardService.글상세보기(id, sessionUserId);
request.setAttribute("model", detailDTO);
return "board/detail";
}Share article