v2 - 게시글 수정 (업데이트) 하기

coding S's avatar
Mar 14, 2024
v2 - 게시글 수정 (업데이트) 하기
업데이트 폼은 손 댈게 없다 @PostMapping("/board/{id}/update") public String update(@PathVariable Integer id, String title, String content, String username){ boardNativeRepository.updateById(id, title, content, username); return "redirect:/board/"+id; } 이걸 바꿀 것!!!
 

업데이트는 만들지 않는다

notion image
조회해서 영속화 시킨 다음 영속화한 객체를 변경 조회해서 상태 변경만 하면 알아서 변경함! 트랜젝션이 걸려있지 않으면 업데이트가 되지 않는다. 트랜젝션 종료 시에 알아서 업데이트 쿼리를 날리고 업데이트가 됨
삭제라는 건 내가 5번을 삭제하려고 하는데, 5번이 없으면 실제 쿼리는 오류나지 않는다. 조회도 1번을 조회했는데 없으면, 쿼리는 오류가 나지 않는다. 그저 로그가 없는 것 그러나 createNativeQuery를 사용하면 조회할 결과 값이 없을 때 noResultException 뜨는데, null을 파싱하다가 터지는 것. db가 에러나는게 아니라, 파싱하다가 에러! delete는 삭제될 데이터가 없어도 상관 없음 없는 id를 누가 삭제 요청 했다 -> 정상적이지 않음. 로그로 남겨놓고 공격 당했다 판단 -> 블랙리스트
없는 id를 수정(update)하려고 하면 -> 쿼리상에서 오류가 나지않는다.
notion image
 

delete, update 무조건 조회부터 하고 들어가야 한다!!

💡
스프링이 데이터가 없을 때, 오류는 안나도… 트랜젝션을 유지할 필요가 있을까? 트랜젝션 시간만 길어지고 좋지 않다… → 때문에 조회를 한 번 하고 들어가는 것 계좌 조회처럼… 송금할 때, 존재하는 계좌인지 조회하고, 송금이 되잖아. 그런 것! → 조회와 이체가 무조건!! 같은 트랜젝션에서 함께 실행이 되어야한다! → 그게 아니라면 유령 데이터를 가져올 수도 있다! (10초 전에 상대방 계좌가 있었는데… 없어졌어요…)
💡
write를 할 때에는 (delete, update) 무조건 조회부터 하고 들어가야 한다!! 근데 너무 많은 조회를 하고 (100번 조회를 하고) 업데이트를 해야하는거면… 이 방법을 고집하지 않아도 됨 !
 

테스트

@Test public void updateById_test() { // given int id = 1; String title = "제목수정1"; // when Board board = boardPersistRepository.findById(id); //조회된 데이터! 영속화 됨! board.setTitle(title); //실제 쿼리가 날아간 건 아니고, pc에 있는 값을 변경한 것임 //트랜젝션이 종료하면 업데이트가 날아가는데... 테스트에선 불가능하니까 em.flush 해줌 em.flush(); }
notion image
💡
조회해서 상태를 바꾸기만 하면 됨 !! 조회해서 객체의 상태를 변경해서 수정하는 걸 [ 더티체킹 ] 이라고 함 !
 

[ 이제 사용해보자! ]

BoardRequest

public class BoardRequest { @Data public static class UpdateDTO { private String title; private String content; private String username; //애를 엔티티로 바꿔서 save 하지 않을거니 toEntity 필요 없다! }
 

컨트롤러

@Transactional @PostMapping("/board/{id}/update") public String update(@PathVariable Integer id, BoardRequest.UpdateDTO requestDTO){ Board board = boardPersistRepository.findById(id); //조회해서 영속화 시킴. 객체의 상태만 바꾸면 끝! board.update(requestDTO); return "redirect:/board/"+id; }
💡
트랜젝션을 여기서 걸면 너무 길어진다. 지금은 이렇게 하고, 추후에는 서비스 레이어에서 처리하자
 

[ 만약 컨트롤러에서 @Transactional을 안 걸고 싶다면? ]

레파지토리

public class BoardPersistRepository { private final EntityManager em; @Transactional public void updateById(int id, BoardRequest.UpdateDTO requestDTO) { Board board = findById(id); board.update(requestDTO); //보드에 있는 update 메소드 } // 더티체킹

컨트롤러

@PostMapping("/board/{id}/update") public String update(@PathVariable Integer id, BoardRequest.UpdateDTO requestDTO){ boardPersistRepository.updateById(id, requestDTO); return "redirect:/board/"+id; }
💡
레파지토리에서 걸어줘라. 근데 이건… 서비스 레이어를 만들면 하등 쓸모없음
 

Board

@NoArgsConstructor @Data @Table(name = "board_tb") @Entity public class Board { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String title; private String content; private String username; @CreationTimestamp private Timestamp createdAt; public Board(String title, String content, String username) { this.title = title; this.content = content; this.username = username; } //다른 곳에서 재사용하려면 DTO 이름을 적을 수 없다! public void update(BoardRequest.UpdateDTO requestDTO) { this.title = requestDTO.getTitle(); this.content = requestDTO.getContent(); this.username = requestDTO.getUsername(); } }
board.update(requestDTO); 코드는 Board 클래스에 정의된 update 메소드를 호출하는 것. 이 메소드는 Board 클래스의 인스턴스인 board 객체의 상태를 변경한다. 여기서 전달된 requestDTO는 BoardRequest.UpdateDTO 타입의 객체로, 게시글(board)을 업데이트하기 위한 새로운 데이터(제목, 내용, 사용자 이름 등)를 담고 있다. update 메소드는 이 requestDTO에서 제공하는 정보를 사용하여 board 객체의 해당 필드(제목, 내용, 사용자 이름)를 새 값으로 설정한다. 이 과정에서 JPA의 더티 체킹(Dirty Checking) 기능이 활용된다. 더티 체킹은 트랜잭션이 종료되는 시점에 엔티티의 상태 변화를 감지하고, 변경된 엔티티에 대해 자동으로 SQL 업데이트 문을 생성하여 실행하는 JPA의 기능이다.
 

화면 확인

notion image
notion image
💡
변경 완료!
 

더티 체킹

💡
수정의 핵심 - 더티체킹 조회 된 영속화 된 객체의 값(상태)를 변경한다!! 언제 더티체킹이 되나? → 트랜젝션이 끝날 때! (그때 업데이트가 되니까) 영속화된 객체의 상태를 변경하고 트랜잭션이 종료되면 update 가 된다
 
Share article

codingb