![[블로그 만들기] 16. 게시판 구현 : 상세보기](https://image.inblog.dev?url=https%3A%2F%2Finblog.ai%2Fapi%2Fog%3Ftitle%3D%255B%25EB%25B8%2594%25EB%25A1%259C%25EA%25B7%25B8%2520%25EB%25A7%258C%25EB%2593%25A4%25EA%25B8%25B0%255D%252016.%2520%25EA%25B2%258C%25EC%258B%259C%25ED%258C%2590%2520%25EA%25B5%25AC%25ED%2598%2584%2520%253A%2520%25EC%2583%2581%25EC%2584%25B8%25EB%25B3%25B4%25EA%25B8%25B0%26logoUrl%3Dhttps%253A%252F%252Finblog.ai%252Finblog_logo.png%26blogTitle%3DCoding_study&w=2048&q=75)
1. detail.mustache 폴더 확인하기
- board_tb 테이블과 user_tb 조인해야 함
- 필요한 정보 board_tb : id, title, content
user_tb : id, username
{{> /layout/header}}
<div class="container p-5"
<!-- 수정삭제버튼 -->
<div class="d-flex justify-content-end">
<button class="btn btn-warning me-1">수정</button>
<button class="btn btn-danger">삭제</button>
</div>
<div class="d-flex justify-content-end mt-2">
<b>작성자</b> : ssar
</div>
<!-- 게시글내용 -->
<div>
<h2><b>제목자리</b></h2>
<hr/>
<div class="m-4 p-2">
내용입니다
</div>
</div>
<!-- 댓글 -->
<div class="card mt-3">
<!-- 댓글등록 -->
<div class="card-body">
<form action="/reply/save" method="post">
<textarea class="form-control" rows="2" name="comment"></textarea>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-outline-primary mt-1">댓글등록</button>
</div>
</form>
</div>
<!-- 댓글목록 -->
<div class="card-footer">
<b>댓글리스트</b>
</div>
<div class="list-group">
<!-- 댓글아이템 -->
<div class="list-group-item d-flex justify-content-between align-items-center">
<div class="d-flex">
<div class="px-1 me-1 bg-primary text-white rounded">cos</div>
<div>댓글 내용입니다</div>
</div>
<form action="/reply/1/delete" method="post">
<button class="btn">🗑</button>
</form>
</div>
<!-- 댓글아이템 -->
<div class="list-group-item d-flex justify-content-between align-items-center">
<div class="d-flex">
<div class="px-1 me-1 bg-primary text-white rounded">ssar</div>
<div>댓글 내용입니다</div>
</div>
<form action="/reply/1/delete" method="post">
<button class="btn">🗑</button>
</form>
</div>
</div>
</div>
</div>
{{> /layout/footer}}


2. BoardController에서 detail로 들어오게 만들기
- 테이블 URL에 들어오는 값은 PK OR Unique
- 나머지는 쿼리 스트링으로 적어야 함
- 주소에 들어가는 모든 것은 where 절에 들어감 / 쿼리는 같음
package shop.mtcoding.blog.board;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import shop.mtcoding.blog.user.User;
import java.util.List;
@RequiredArgsConstructor
@Controller
public class BoardController {
// HttpSession 객체를 참조
private final HttpSession session;
private final BoardRepository boardRepository; // DI
@GetMapping({"/", "/board"})
public String index(HttpServletRequest request) {
User sessionUser = (User) session.getAttribute("sessionUser");
if (sessionUser == null) {
System.out.println("로그인 안된 상태입니다");
} else {
System.out.println("로그인 된 상태입니다");
}
List<Board> boardList = boardRepository.findAll();
request.setAttribute("boardList", boardList); // ("key", value)
return "index";
}
@GetMapping("/board/saveForm")
public String saveForm() {
return "board/saveForm";
}
// select b.id, b.title, b.content, b.user_id, u.username from board_tb b inner join user_tb u on b.user_id = u.id where b.id = ?
@GetMapping("/board/1") // 1이 프라이머리키 -> 뭐든 넣어도 실행시키려면 변수화시켜서 {}
public String detail() {
System.out.println("상세보기 페이지 호출됨");
// 바디 데이터가 없으면 유효성 검사할 필요가 없음
return "board/detail";
}
}

package shop.mtcoding.blog.board;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import shop.mtcoding.blog.user.User;
import java.util.List;
@RequiredArgsConstructor
@Controller
public class BoardController {
// HttpSession 객체를 참조
private final HttpSession session;
private final BoardRepository boardRepository; // DI
@GetMapping({"/", "/board"})
public String index(HttpServletRequest request) {
User sessionUser = (User) session.getAttribute("sessionUser");
if (sessionUser == null) {
System.out.println("로그인 안된 상태입니다");
} else {
System.out.println("로그인 된 상태입니다");
}
List<Board> boardList = boardRepository.findAll();
request.setAttribute("boardList", boardList); // ("key", value)
return "index";
}
@GetMapping("/board/saveForm")
public String saveForm() {
return "board/saveForm";
}
// select b.id, b.title, b.content, b.user_id, u.username from board_tb b inner join user_tb u on b.user_id = u.id where b.id = ?
@GetMapping("/board/{id}")
public String detail(@PathVariable int id, HttpServletRequest request) {
System.out.println("상세보기 페이지 호출됨");
System.out.println("id : " + id);
// 바디 데이터가 없으면 유효성 검사 안해도 됨
BoardResponse.DetailDTO responseDTO = boardRepository.findById(id);
request.setAttribute("board", responseDTO);
return "board/detail";
}
}


3. 응답 DTO 만들기
- BoardResponse를 만들어서 DetailDTO() 만들기
package shop.mtcoding.blog.board;
import lombok.Data;
public class BoardResponse {
@Data
public static class DetailDTO {
private int id;
private String title;
private String content;
private int userId;
private String username;
}
}
4. findById() 만들어 테스트하기

package shop.mtcoding.blog.board;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import java.util.List;
@RequiredArgsConstructor
@Repository
public class BoardRepository {
private final EntityManager em; // jpa가 제공해줌
// 조회니까 트랜잭션 필요없음
public List<Board> findAll() { // 보드 테이블의 모든 것 가지고 오기
Query query = em.createNativeQuery("select * from board_tb order by id desc", Board.class);
System.out.println("BoardRepository에 findAll 메서드 호출됨");
return query.getResultList(); // 여러건
} // 이 결과를 리퀘스트에 담고 뷰 화면 가서 뿌리기
public BoardResponse.DetailDTO findById(int idx) {
Query query = em.createNativeQuery("select b.id, b.title, b.content, b.user_id, u.username from board_tb b inner join user_tb u on b.user_id = u.id where b.id = ?");
query.setParameter(1, idx);
Object[] row = (Object[]) query.getSingleResult();
Integer id = (Integer) row[0];
String title = (String) row[1];
String content = (String) row[2];
int userId = (Integer) row[3];
String username = (String) row[4];
System.out.println("id : " + id);
System.out.println("title : " + title);
System.out.println("content : " + content);
System.out.println("userId : " + userId);
System.out.println("username : " + username);
BoardResponse.DetailDTO responseDTO = new BoardResponse.DetailDTO();
responseDTO.setId(id);
responseDTO.setTitle(title);
responseDTO.setContent(content);
responseDTO.setUserId(userId);
responseDTO.setUsername(username);
return responseDTO;
}
}
5. DTO에 담아서 화면에 전달하기
package shop.mtcoding.blog.board;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import shop.mtcoding.blog.user.User;
import java.util.List;
@RequiredArgsConstructor
@Controller
public class BoardController {
// HttpSession 객체를 참조
private final HttpSession session;
private final BoardRepository boardRepository; // DI
@GetMapping({"/", "/board"})
public String index(HttpServletRequest request) {
User sessionUser = (User) session.getAttribute("sessionUser");
if (sessionUser == null) {
System.out.println("로그인 안된 상태입니다");
} else {
System.out.println("로그인 된 상태입니다");
}
List<Board> boardList = boardRepository.findAll();
request.setAttribute("boardList", boardList); // ("key", value)
return "index";
}
@GetMapping("/board/saveForm")
public String saveForm() {
return "board/saveForm";
}
// select b.id, b.title, b.content, b.user_id, u.username from board_tb b inner join user_tb u on b.user_id = u.id where b.id = ?
@GetMapping("/board/{id}") // 1이 프라이머리키 -> 뭐든 넣어도 실행시키려면 변수화시켜서 {}
public String detail(@PathVariable int id, HttpServletRequest request) {
System.out.println("id" +id);
// 바디 데이터가 없으면 유효성 검사할 필요가 없음
BoardResponse.DetailDTO responseDTO = boardRepository.findById(id);
request.setAttribute("board", responseDTO);
return "board/detail";
}
}

6. 화면에 랜더링하기
- detail 폴더에서 자바 코드 변수 집어넣기
{{> /layout/header}}
<div class="container p-5"
<!-- 수정삭제버튼 -->
<div class="d-flex justify-content-end">
<button class="btn btn-warning me-1">수정</button>
<button class="btn btn-danger">삭제</button>
</div>
<div class="d-flex justify-content-end mt-2">
<b>작성자</b> : {{board.username}}
</div>
<!-- 게시글내용 -->
<div>
<h2><b>{{board.title}}</b></h2>
<hr/>
<div class="m-4 p-2">
{{board.content}}
</div>
</div>
<!-- 댓글 -->
<div class="card mt-3">
<!-- 댓글등록 -->
<div class="card-body">
<form action="/reply/save" method="post">
<textarea class="form-control" rows="2" name="comment"></textarea>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-outline-primary mt-1">댓글등록</button>
</div>
</form>
</div>
<!-- 댓글목록 -->
<div class="card-footer">
<b>댓글리스트</b>
</div>
<div class="list-group">
<!-- 댓글아이템 -->
<div class="list-group-item d-flex justify-content-between align-items-center">
<div class="d-flex">
<div class="px-1 me-1 bg-primary text-white rounded">cos</div>
<div>댓글 내용입니다</div>
</div>
<form action="/reply/1/delete" method="post">
<button class="btn">🗑</button>
</form>
</div>
<!-- 댓글아이템 -->
<div class="list-group-item d-flex justify-content-between align-items-center">
<div class="d-flex">
<div class="px-1 me-1 bg-primary text-white rounded">ssar</div>
<div>댓글 내용입니다</div>
</div>
<form action="/reply/1/delete" method="post">
<button class="btn">🗑</button>
</form>
</div>
</div>
</div>
</div>
{{> /layout/footer}}




Share article