7. Spring 게시판 무작정 따라하기

Spring에서 @PathVariable을 활용한 동적 라우팅 및 Mustache를 사용한 데이터 출력 방법
HootJem's avatar
Aug 19, 2024
7. Spring 게시판 무작정 따라하기
Spring에서 @PathVariable을 활용한 동적 라우팅 및 Mustache를 사용한 데이터 출력 방법
@GetMapping("/board/{id}") public String detail(@PathVariable("id") Integer id) { return "board/detail"; }
notion image
list 에서 클릭된 id 로 매핑되어 이동된다.
notion image
이거 덕분에 가능한것
 

1.1 @PathVariable 란?

Get 요청 → DB select 요청이다 주소에는 규칙이 있음.
(1) /boards?title=제목1
select * from board where title = '제목1'; ← 얘는 거진 컬렉션 타입임
(2) /boards?title=제목1&content=내용1
select * from board where title = '제목1' and content='내용1';
(3) /boards/1
select * from board where id = 1;
(4) /boards
select * from board
(5) /users/1/comment ← user 1이 쓴 코멘트
(6) /boards/2/comments ← 2번 게시글에 있는 코멘트들
  • 다음과 같이 쿼리문을 유추 가능하게 된다.
  • unique 거나 pk 는 쿼리 스트링으로 받을 수 있지만 아닐 시 PathVariable 사용 해야함. (3번)
 

1.2 boardRepository 쿼리문 작성

public Board findById(int id) { Query query = em.createNativeQuery("select * from board_tb where id = ?", Board.class); query.setParameter(1, id); Board board = (Board) query.getSingleResult(); // 다운캐스팅 필요 return board; }
id(pk) 로 조회하기 때문에 하이버네이트 까지 갈 필요없이 바로 매핑 가능
notion image
 

1.2.1 findById 단위테스트

@Test public void findById_test() { // given int id = 1; // when Board board = boardRepository.findById(id); // eye(then : 원래는 검증이 필요함) System.out.println(board.getId()); System.out.println(board.getTitle()); System.out.println(board.getContent()); }
notion image
 
notion image
더미 데이터가 5건이 있는데 만약 id = 6을 넣게 되면 다음과 같은 에러가 난다.
jakarta.persistence.NoResultException: No result found for query [select * from board_tb where id = ?] at org.hibernate.query.spi.AbstractSelectionQuery.getSingleResult(AbstractSelectionQuery.java:558) at shop.mtcoding.blog.board.BoardRepository.findById(BoardRepository.java:20) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:354) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:716) at shop.mtcoding.blog.board.BoardRepository$$SpringCGLIB$$0.findById(<generated>) at shop.mtcoding.blog.board.BoardRepositoryTest.findById_test(BoardRepositoryTest.java:27) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
 
에러를 잡아야한다.
public Board findById(int id) { Query query = em.createNativeQuery("select * from board_tb where id = ?", Board.class); query.setParameter(1, id); try { Board board = (Board) query.getSingleResult(); // 다운캐스팅 필요 return board; } catch (Exception e) { throw new RuntimeException("게시글 id를 찾을 수 없습니다"); } }
try ~ catch 절을 추가하여 적절한 에러 처리를 해줘야함.
notion image
notion image
throw 를 통해 에러를 비교적 쉽게 관리할 수 있음(본인이 해결하거나, 나를 호출한 메서드에게 넘기거나)
Global Exception Handler 를 통해 한번에 처리할 수 있어진다. (나중에 다룰것)
 

1.3 컨트롤러 수정

@GetMapping("/board/{id}") public String detail(@PathVariable("id") Integer id, HttpServletRequest request) { Board board = boardRepository.findById(id); request.setAttribute("model", board); return "board/detail"; }
 

1.4 mustache 동적 출력

  • 기존
<div class="container p-5"> <!-- 수정삭제버튼 --> <div class="d-flex justify-content-end"> <a href="/board/1/update-form" class="btn btn-warning me-1">수정</a> <form> <button class="btn btn-danger">삭제</button> </form> </div> <div class="d-flex justify-content-end"> <b>작성자</b> : 익명 </div> <!-- 게시글내용 --> <div> <h2><b>제목1</b></h2> <hr/> <div class="m-4 p-2"> 내용1 </div> </div> </div>
  • 변경
<div class="container p-5"> <!-- 수정삭제버튼 --> <div class="d-flex justify-content-end"> <a href="/board/{{model.id}}/update-form" class="btn btn-warning me-1">수정</a> <form action="/board/{{model.id}}/delete" method="post"> <button class="btn btn-danger">삭제</button> </form> </div> <div class="d-flex justify-content-end"> <b>작성자</b> : 익명 </div> <!-- 게시글내용 --> <div> <h2><b>{{model.title}}</b></h2> <hr/> <div class="m-4 p-2"> {{model.content}} </div> </div> </div>
 
나중에 할 delete 의 경우 ajaxc , 자바 스크립트 사용하지 않고 내용에 집중하기 위해 post 사용
만약 method 가 정삭적으로 delete 면 요청 주소가 아래와 같은게 정석
<form action="/board/{{model.id}}/delete" method="post"> <form action="/board/{{model.id}}" method="delete">
 

결과

notion image
notion image
 

Share article

[HootJem] 개발 기록 블로그