블로그 만들기 22. 게시글 구현 : 삭제하기

Feb 06, 2024
블로그 만들기 22. 게시글 구현 : 삭제하기

💡
자바 스크립트를 사용하지 않고 HTTP 1.0을 가지고 블로그 제작하기
  • GET, POST만 지원
POST → INSERT, DELETE, UPDATE
  • HTML5 (form 태그 : get, post) (a태그 : get 요청)

1. 삭제가 가능한 페이지 찾기 → 상세페이지(detail)

  • 로그인을 해야 삭제가 가능함
  • 삭제 버튼을 누르면 삭제되고 목록에 반영되야 함
notion image
{{> layout/header}} <div class="container p-5"> {{#pageOwner}} <!-- 수정삭제버튼 --> <div class="d-flex justify-content-end"> <button class="btn btn-warning me-1">수정</button> <button class="btn btn-danger">삭제</button> </div> {{/pageOwner}} <div class="d-flex justify-content-end"> <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}}
 

2. detail에 <form>삭제</form>로 감싸기

  • "/board/{{board.id/delete}} 추가하기
  • 요청 방법은 post
  • <form>태그 안에 button의 기본 디폴트는 submit
notion image
notion image
{{> layout/header}} <div class="container p-5"> {{#pageOwner}} <!-- 수정삭제버튼 --> <div class="d-flex justify-content-end"> <button class="btn btn-warning me-1">수정</button> <form action="/board/{{board.id}}/delete" method="post"> <button class="btn btn-danger">삭제</button> </form> </div> {{/pageOwner}} <div class="d-flex justify-content-end"> <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}}
 

3. 인증, 권한 체크하기

  • 기존에 findById의 이름을 findByIdWithUser로 변경하기 / 헷갈릴 수 있음
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 org.springframework.web.bind.annotation.PostMapping; 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() { // session 영역에 접근하기 위한 // 1. session 영역에 sessionUser 키 값에 user 객체가 있는지 체크하기 User sessionUser = (User) session.getAttribute("sessionUser"); // 2. 값이 null이면 로그인 페이지로 리다이렉션 if (sessionUser == null) { return "redirect:/loginForm"; } // 3. null이 아니면 /board/saveForm으로 이동 return "board/saveForm"; } @PostMapping("board/save") //save 주소 만들기 public String save(BoardRequest.SaveDTO requestDTO, HttpServletRequest request) { // 1. 인증 체크 User sessionUser = (User) session.getAttribute("sessionUser"); System.out.println("sessionUser:" + sessionUser); if (sessionUser == null) { return "redirect:/loginForm"; } // 2. 바디 데이터 확인 및 유효성 검사 System.out.println(requestDTO); if (requestDTO.getTitle().length() > 30) { request.setAttribute("status", 400); request.setAttribute("msg", "title의 길이가 30자를 초과해서는 안되요"); return "error/40x"; // BadRequest } // 3. 모델 위임 // insert into board_tb(title, content, user_id, created_at) values(?,?,?, now()); boardRepository.save(requestDTO, sessionUser.getId()); return "redirect:/"; } // 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("id : " + id); // 1. 바로 모델 진입 -> 상세보기 데이터 가져오기 // body 데이터가 없으면 유효성 검사할 필요 없음 BoardResponse.DetailDTO reponseDTO = boardRepository.findByIdWithUser(id); //메서드 이름 변경 // user 객체를 가져와서 session 값 받기 : object라 다운 캐스팅 해야함 User sessionUser = (User) session.getAttribute("sessionUser"); //System.out.println("sessionUser: " + sessionUser); // 2. 페이지 주인 여부 체크(board의 userId와 sessionId의 값 비교) boolean pageOwner = false; if (reponseDTO.getUserId() == sessionUser.getId()) { //System.out.println("getUserId:" + reponseDTO.getUserId()); pageOwner = true; } request.setAttribute("board", reponseDTO); request.setAttribute("pageOwner", pageOwner); // 이 값을 mustache에게 줘야함! return "board/detail"; } }
  • BoardRepository 작성하기
findById → findByIdWithUser 이름 변경
  • findById() 구현하기
삭제할 데이터가 있는지 없는지 확인한 후에 삭제하는 것이 더 좋음!
package shop.mtcoding.blog.board; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import java.util.List; @RequiredArgsConstructor @Repository public class BoardRepository { private final EntityManager em; public List<Board> findAll() { Query query = em.createNativeQuery("select * from board_tb order by id desc", Board.class); return query.getResultList(); } 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; } public BoardResponse.DetailDTO findByIdWithUser(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; } @Transactional public void save(BoardRequest.SaveDTO requestDTO, int userId) { Query query = em.createNativeQuery("insert into board_tb(title, content, user_id, created_at) values(?,?,?, now())"); query.setParameter(1, requestDTO.getTitle()); query.setParameter(2, requestDTO.getContent()); query.setParameter(3, userId); query.executeUpdate(); } }
 

4. 삭제하기

  • deleteById() 구현하기
package shop.mtcoding.blog.board; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import java.util.List; @RequiredArgsConstructor @Repository public class BoardRepository { private final EntityManager em; public List<Board> findAll() { Query query = em.createNativeQuery("select * from board_tb order by id desc", Board.class); return query.getResultList(); } 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; } public BoardResponse.DetailDTO findByIdWithUser(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; } @Transactional public void save(BoardRequest.SaveDTO requestDTO, int userId) { Query query = em.createNativeQuery("insert into board_tb(title, content, user_id, created_at) values(?,?,?, now())"); query.setParameter(1, requestDTO.getTitle()); query.setParameter(2, requestDTO.getContent()); query.setParameter(3, userId); query.executeUpdate(); } @Transactional public void deleteById(int id) { Query query = em.createNativeQuery("delete from board_tb where id = ?"); query.setParameter(1, id); query.executeUpdate(); } }
  • BoardController에서 deleteById() 호출하기
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 org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import shop.mtcoding.blog.user.User; import java.util.List; @RequiredArgsConstructor @Controller public class BoardController { private final HttpSession session; // DI private final BoardRepository boardRepository; // DI @PostMapping("/board/{id}/delete") // body데이터가 없어서 유효성 검사 안해도 됨 public String delete(@PathVariable int id, HttpServletRequest request) { // 1. 인증 검사하기 User sessionUser = (User) session.getAttribute("sessionUser"); if(sessionUser == null) { return "redirect:/loginForm"; } // 2. 권한 검사하기 Board board = boardRepository.findById(id); if(board.getUserId() != sessionUser.getId()){ request.setAttribute("status", 403); request.setAttribute("msg", "게시글을 삭제할 권한이 없습니다"); return "error/40x"; // 리다이렉트 하면 데이터 사라지니까 하면 안됨 } boardRepository.deleteById(id); return "redirect:/"; } @PostMapping("/board/save") public String save(BoardRequest.SaveDTO requestDTO, HttpServletRequest request) { // 1. 인증 체크 User sessionUser = (User) session.getAttribute("sessionUser"); System.out.println("sessionUser:" + sessionUser); if (sessionUser == null) { return "redirect:/loginForm"; } // 2. 바디 데이터 확인 및 유효성 검사 System.out.println(requestDTO); if (requestDTO.getTitle().length() > 30) { request.setAttribute("status", 400); request.setAttribute("msg", "title의 길이가 30자를 초과해서는 안되요"); return "error/40x"; // BadRequest // 리다이렉션하면 데이터가 삭제되서 하면 안됨 } // 3. 모델 위임 // insert into board_tb(title, content, user_id, created_at) values(?,?,?, now()); boardRepository.save(requestDTO, sessionUser.getId()); return "redirect:/"; } // http://localhost:8080?page=0 @GetMapping({"/", "/board"}) public String index(HttpServletRequest request) { List<Board> boardList = boardRepository.findAll(); request.setAttribute("boardList", boardList); return "index"; } @GetMapping("/board/saveForm") // /board/saveForm Get요청이 옴 public String saveForm() { // session 영역에 접근하기 위한 // 1. session 영역에 sessionUser 키 값에 user 객체가 있는지 체크하기 User sessionUser = (User) session.getAttribute("sessionUser"); // 2. 값이 null이면 로그인 페이지로 리다이렉션 if (sessionUser == null) { return "redirect:/loginForm"; } // 3. null이 아니면 /board/saveForm으로 이동 return "board/saveForm"; } // 상세보기시 호출 @GetMapping("/board/{id}") // 1이 프라이머리키 -> 뭐든 넣어도 실행시키려면 변수화시켜서 {} public String detail(@PathVariable int id, HttpServletRequest request) { System.out.println("id : " + id); // 1. 바로 모델 진입 -> 상세보기 데이터 가져오기 // body 데이터가 없으면 유효성 검사할 필요 없음 BoardResponse.DetailDTO reponseDTO = boardRepository.findByIdWithUser(id); //메서드 이름 변경 // user 객체를 가져와서 session 값 받기 : object라 다운 캐스팅 해야함 User sessionUser = (User) session.getAttribute("sessionUser"); //System.out.println("sessionUser: " + sessionUser); // 2. 페이지 주인 여부 체크(board의 userId와 sessionId의 값 비교) boolean pageOwner = false; if (reponseDTO.getUserId() == sessionUser.getId()) { //System.out.println("getUserId:" + reponseDTO.getUserId()); pageOwner = true; } request.setAttribute("board", reponseDTO); request.setAttribute("pageOwner", pageOwner); // 이 값을 mustache에게 줘야함! return "board/detail"; } }
notion image
Share article

vosw1