수정 버튼을 누르면 '수정 페이지'로 이동해야 한다.
[ updateForm (글 수정 화면) 만들기 ] - mustache 코드
{{> /layout/header}} <div class="container p-5"> <!-- 요청을 하면 localhost:8080/board/save POST로 요청됨 title=사용자입력값&content=사용자값 --> <div class="card"> <div class="card-header"><b>수정하기 화면입니다</b></div> <div class="card-body"> <form action="/board/1/update" method="post" enctype="application/x-www-form-urlencoded"> <div class="mb-3"> <input type="text" class="form-control" placeholder="Enter title" name="title" value="제목1"> </div> <div class="mb-3"> <textarea class="form-control" rows="5" name="content">내용1</textarea> </div> <button type="submit" class="btn btn-primary form-control">수정완료</button> </form> </div> </div> </div> {{> /layout/footer}}
글을 수정하려면… 기존에 글이 적혀있어야 한다 = DB 조회 필요 그래서 @GetMapping("/board/{id}/updateForm") 라고 {id}를 적은 것. {id}를 받지 않으면 조회가 안 돼 user 정보까지 필요 없고, board만 가져오면 되니까 findById 메소드를 사용하자 만약 작성자 이름 ssar 이라고까지 적혀 있으면 join을 써야 한다.
join, 서브쿼리, OrderBy = DB에 부하를 심하게 줌
메서드를 구현하다보면 핵심로직 (메서드의 주요 목적과 주요 책임)을 잊을 수 있다. 부가로직 (메서드가 가져야하는 부가적인 기능)을 구현하다 보면 그렇게 된다. 현업에 가게되면 부가로직들은 모두 리플렉션 처리되어 있을 것
[ BoardController 생성 ]
@GetMapping("/board/{id}/updateForm") public String updateForm(@PathVariable int id, HttpServletRequest request) { // 인증 체크 (인증 안되면 나가) User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) { return "redirect:/loginForm"; } // 권한 체크 (로그인한 유저의 아이디와 작성자 아이디 비교.)(권한 없으면 나가) // 조회 없이 권한 체크를 못한다. 게시글 누가 썼는지 모르잖아. 그래서 조회 먼저 // 모델 위임 (id로 board를 조회) Board board = boardRepository.findById(id); if (board.getUserId() != sessionUser.getId()) { return "error/403"; } // 3. 가방에 담기 request.setAttribute("board", board); return "board/updateForm"; }
[ 핵심 로직 ]
Board board = boardRepository.findById(id); request.setAttribute("board", board); return "board/updateForm";
[ updateForm.mustache ] - 머스태치로 데이터 뿌리기
<textarea>는 value 없으니 바로 적어줌
[ detail.mustache ] - 상세보기 화면에서 업데이트 화면으로 이동하는 버튼 수정
get요청이면 그냥 하이퍼링크 하면 되어서 a태그 사용함
수정 버튼 누르면 바로 /updateForm으로 가게끔
[ 테스트 해보기 ]
수정하기 화면이 잘 뜬다
[ 게시글 수정하기 (액션) ]
고치자
1. DTO 만들기
의문 사항... 있는거 쓰면 안되나? 똑같은데… 안돼요!!!!!!!!!!!!!! 유효성이 다를 수가 있어서 똑같아도 만드는게 좋다. 관리를 따로!
DTO는 화면마다 들고 있어야 한다
@RequestBody … 여담
파싱 전략이 json으로 바뀜
@RequestBody + (리퀘스트바디?) 오브젝트를 적으면 = json으로 받아줌
@RequestBody + String이면 = 평문 text/plain ㅇㅇ
포스트맨으로 자기가 만든 컨트롤러를 화면없이 테스트해볼 수 있다
아직 header에 content-type 이없네
포스트맨이 자동으로 x-form으로 잡아주네
평문 전략
포스트맨에서 평문을 전송했다
콘솔창에서 전송된 평문 확인
(+이 세상에 존재하지 않는 데이터 = 평문으로 받고 내가 직접 파싱)
json으로 보내니
json으로 왔다
쿼리를 보고 몇 개를 넘겨줘야 할 지 정한다 (requestDTO와 id까지 넘겨줘야)
[ 컨트롤러 ]
@PostMapping("/board/{id}/update") public String update(@PathVariable int id, BoardRequest.UpdateDTO requestDTO) { // 1. 인증 체크 User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) { return "redirect:/loginForm"; } // 2. 권한 체크 Board board = boardRepository.findById(id); if (board.getUserId() != sessionUser.getId()) { return "error/403"; } // 3. 핵심 로직 // update board_tb set title = ?, content = ? where id = ?; boardRepository.update(requestDTO, id); return "redirect:/board/"+id; //상세 보기 화면으로 돌아가기 }
return "redirect:/board/{id}";와 같이 코드를 작성하면 안됩니다. 이유는 {id}는 경로 변수(path variable)로 인식되어 처리되지 않기 때문입니다. 대신에, return "redirect:/board/" + id;와 같이 id 값을 직접 문자열로 연결하여 사용해야 한다. 이렇게 하면 id 값을 동적으로 경로에 포함시킬 수 있습니다.
[ BoardRepository ]
public class BoardRepository { private final EntityManager em; @Transactional public void update(BoardRequest.UpdateDTO requestDTO, int id) { Query query = em.createNativeQuery("update board_tb set title = ?, content = ? where id = ?"); query.setParameter(1, requestDTO.getTitle()); query.setParameter(2, requestDTO.getContent()); query.setParameter(3, id); query.executeUpdate(); }
[ 결과 확인하기 ]
수정됐다
AOP - 우리가 코드 짤 때, 핵심 로직만 짜고, 부가로직은 자동화 시킬 수 있는 것
로그 관리의 중요성!
파일에 오류가 터진 시간, 아이디 등을 로그로 남겨놔야 한다. 모든 오류는 Try-Catch로 잡아서 남겨놔야… 그래야 오류를 파악할 수 있음
Share article