[트러블 슈팅] 9. 게시판 삭제가 왜 안됐을까?

귤's avatar
Mar 18, 2025
[트러블 슈팅] 9. 게시판 삭제가 왜 안됐을까?
Contents
문제

문제

package com.metacoding.blogv1.board; import jakarta.transaction.Transactional; import org.springframework.stereotype.Service; import java.util.List; // 책임 : 트랜잭션 처리, 비지니스 로직 처리 (ex. 잔액 검사) @Service // IOC public class BoardService { private BoardRepository boardRepository; // DI : 의존성 주임 -> IOC로 부터 들고옴 public BoardService(BoardRepository boardRepository) { this.boardRepository = boardRepository; } @Transactional // 트랜잭션 시작 -> 함수 내부가 다 수행되면 commit, 실패하면 rollback public void 게시글쓰기(String title, String content) { boardRepository.insert(title, content); } public List<Board> 게시글목록() { List<Board> boardList = boardRepository.findAll(); return boardList; } public Board 게시글상세보기(int id) { return boardRepository.findById(id); } public void 게시글삭제(int id) { // 1. 게시글이 존재하는지 확인 Board board = boardRepository.findById(id); // 2. 삭제 if (board == null) { throw new RuntimeException("게시글이 없는데 왜 삭제해"); } boardRepository.deleteById(id); } // commit }
BoardService.java
package com.metacoding.blogv1.board; import jakarta.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import java.util.List; // 책임 : 요청 잘받고, 응답 잘하고 @Controller // 컴퍼넌트 스캔 -> DS가 활용 public class BoardController { private BoardService boardService; public BoardController(BoardService boardService) { this.boardService = boardService; } @PostMapping("/board/{id}/update") public String update(@PathVariable("id") int id, String title, String content) { // update boar_tb set title=?, content=? where id = ? // 주소로 받는 데이터는 전부 다 where에 걸린다. System.out.println("id : " + id); System.out.println("title : " + title); System.out.println("content : " + content); return "redirect:/board/" + id; } @PostMapping("/board/{id}/delete") public String delete(@PathVariable("id") int id) { boardService.게시글삭제(id); return "redirect:/"; } @PostMapping("/board/save") // action / ~ 수행해줘 public String save(String title, String content) { System.out.println("title: " + title + " content: " + content); boardService.게시글쓰기(title, content); return "redirect:/"; // 주소가 만들어져있으면 무조건 리다이렉션 } // Controller -> Model -> View : MVC 패턴 @GetMapping("/") public String list(HttpServletRequest request) { List<Board> boardList = boardService.게시글목록(); request.setAttribute("models", boardList); // Request 담기 return "list"; // forward } @GetMapping("/board/{id}") // 패턴 매칭 /board/1,2,3,4 public String detail(@PathVariable("id") int id, HttpServletRequest request) { Board board = boardService.게시글상세보기(id); request.setAttribute("model", board); return "detail"; } @GetMapping("/board/save-form") // 주소는 하이픈(-) 사용 public String saveForm() { return "save-form"; } @GetMapping("/board/{id}/update-form") // URI 주소 public String updateForm(@PathVariable("id") int id) { return "update-form"; // 파일명 } }
BoardController.java
package com.metacoding.blogv1.board; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import org.springframework.stereotype.Repository; import java.util.List; // 책임 : DB와 소통하는 친구 @Repository // IOC 컬렉션(저장소)에 뜬다. public class BoardRepository { private EntityManager em; // DI -> IOC 순회해서 타입으로 찾아서 전달해준다. public BoardRepository(EntityManager em) { System.out.println("BoardRepository new 됨"); this.em = em; } // 나중에 쿼리, 셀렉트 필요하면 여기에 만들면 됨 public void insert(String title, String content) { Query query = em.createNativeQuery("insert into board_tb(title, content, created_at) values(?,?,now())"); query.setParameter(1, title); query.setParameter(2, content); query.executeUpdate(); // insert, update, delete } public List<Board> findAll() { Query query = em.createNativeQuery("select * from board_tb order by id desc", Board.class); List<Board> boardList = query.getResultList(); // 여러 건 조회 return boardList; } 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) { return null; } } public void deleteById(int id) { Query query = em.createNativeQuery("delete from board_tb where id = ?"); query.setParameter(1, id); query.executeUpdate(); } }
BoardRepository.java
{{> layout/header}} <section> <a href="/board/{{model.id}}/update-form">수정화면가기</a> <form action="/board/{{model.id}}/delete" method="post"> <button type="submit">삭제</button> </form> <div> 번호 : {{model.id}} <br> 제목 : {{model.title}} <br> 내용 : {{model.content}} <br> 작성일 : {{model.createdAt}} <br> </div> </section> </body> </html>
detail.mustache
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>blog</title> </head> <body> <nav> <ul> <li> <a href="/">홈</a> </li> <li> <a href="/board/save-form">글쓰기</a> </li> </ul> </nav> <hr>
header.mustache
{{> layout/header}} <section> <table border="1"> <tr> <th>번호</th> <th>제목</th> <th></th> </tr> {{#models}} <tr> <td>{{id}}</td> <td>{{title}}</td> <td><a href="/board/{{id}}">상세보기</a></td> </tr> {{/models}} </table> </section> </body> </html>
list.mustache
{{> layout/header}} <section> <!-- http body : title=제목6&content=내용6 http header : application/x-www-form-urlencoded key값은 input태그의 name, value값은 input태그에 사용자가 입력하는 값--> <form action="/board/save" method="post" enctype="application/x-www-form-urlencoded"> <input type="text" name="title" placeholder="제목"><br> <input type="text" name="content" placeholder="내용"><br> <button type="submit">글쓰기</button> </form> </section> </body> </html>
save-form.mustache
{{> layout/header}} <section> <form action="/board/1/update" method="post" enctype="application/x-www-form-urlencoded"> <input type="text" name="title" value="제목1"><br> <input type="text" name="content" value="내용1"><br> <button type="submit">글수정</button> </form> </section> </body> </html>
update-form.mustache
# utf-8 한글 인코딩 server.servlet.encoding.charset=utf-8 server.servlet.encoding.force=true # DB 연결 코드 (EntityManager 만들어냄) spring.datasource.driver-class-name=org.h2.Driver spring.datasource.url=jdbc:h2:mem:test spring.datasource.username=sa spring.datasource.password= spring.h2.console.enabled=true # JPA @Entity 스캔해서 테이블 생성 spring.jpa.hibernate.ddl-auto=create # 콘솔에 쿼리 표시 spring.jpa.show-sql=true # 더미데이터 sql문 실행 spring.sql.init.data-locations=classpath:db/data.sql # ddl-auto가 실행된 후에 sql문 실행하는 법 spring.jpa.defer-datasource-initialization=true # mustache에서 request 객체 접근하게 설정하는 법 spring.mustache.servlet.expose-request-attributes=true
application.properties
notion image

해결법

package com.metacoding.blogv1.board; import jakarta.transaction.Transactional; import org.springframework.stereotype.Service; import java.util.List; // 책임 : 트랜잭션 처리, 비지니스 로직 처리 (ex. 잔액 검사) @Service // IOC public class BoardService { private BoardRepository boardRepository; // DI : 의존성 주임 -> IOC로 부터 들고옴 public BoardService(BoardRepository boardRepository) { this.boardRepository = boardRepository; } @Transactional // 트랜잭션 시작 -> 함수 내부가 다 수행되면 commit, 실패하면 rollback public void 게시글쓰기(String title, String content) { boardRepository.insert(title, content); } public List<Board> 게시글목록() { List<Board> boardList = boardRepository.findAll(); return boardList; } public Board 게시글상세보기(int id) { return boardRepository.findById(id); } @Transactional public void 게시글삭제(int id) { // 1. 게시글이 존재하는지 확인 Board board = boardRepository.findById(id); // 2. 삭제 if (board == null) { throw new RuntimeException("게시글이 없는데 왜 삭제해"); } boardRepository.deleteById(id); } // commit }
BoardService.java
notion image
💡
BoardService.java 코드에서 @Transactional을 붙이지 않아서 삭제가 되지 않았던 것 이었다.
잘 삭제 됨
잘 삭제 됨
 
Share article

gyul