inblog logo
|
[HootJem] 개발 기록 블로그
    springboot

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

    Spring에서 @PathVariable을 활용한 동적 라우팅 및 Mustache를 사용한 데이터 출력 방법
    HootJem's avatar
    HootJem
    Aug 19, 2024
    7. Spring 게시판 무작정 따라하기
    Contents
    1.1 @PathVariable 란?1.2 boardRepository 쿼리문 작성1.3 컨트롤러 수정1.4 mustache 동적 출력결과
    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
     

    스프링부트 게시판 시리즈 v1 - 1. https://inblog.ai/hj/1-spring-게시판-무작정-따라하기-26526 - 2. https://inblog.ai/hj/2-spring-게시판-무작정-따라하기-26560 - 3. https://inblog.ai/hj/3-spring-게시판-무작정-따라하기-26757 - 4. https://inblog.ai/hj/4-spring-게시판-무작정-따라하기-26758 - 5. https://inblog.ai/hj/5-spring-게시판-무작정-따라하기-26934 - 6. https://inblog.ai/hj/6-spring-게시판-무작정-따라하기-26937 - 7. https://inblog.ai/hj/7-spring-게시판-무작정-따라하기-26940 - 8. https://inblog.ai/hj/8-spring-게시판-무작정-따라하기-27065 - 9. https://inblog.ai/hj/9-spring-게시판-무작정-따라하기끝-27066
    Share article
    Contents
    1.1 @PathVariable 란?1.2 boardRepository 쿼리문 작성1.3 컨트롤러 수정1.4 mustache 동적 출력결과

    [HootJem] 개발 기록 블로그

    RSS·Powered by Inblog