Git Clone

참고 사이트
게시글 목록보기, 수정화면 이동
index 수정
{{> layout/header}} <div class="container p-5"> <table class="table table-striped"> <thead> <tr> <th>번호</th> <th>제목</th> <th>내용</th> <th>작성자</th> <th></th> </tr> </thead> <tbody> </tbody> </table> </div> <script> function getBoard(){ return` <tr id="board-5"> <td>5</td> <td>제목5</td> <td>내용5</td> <td>홍길동</td> <td> <div class="d-flex"> <form action="#"> <button class="btn btn-danger">삭제</button> </form> <form action="/board/1/updateForm" method="get"> <button class="btn btn-warning">수정</button> </form> </div> </td> </tr> `; } </script> {{> layout/footer}}
BoardApiController 생성
api에 주소에는 보통 복수형을 쓴다.

@RequiredArgsConstructor @RestController public class BoardApiController { private final BoardRepository boardRepository; @GetMapping("/api/boards") public void findAll(){ List<Board> boardList = boardRepository.selectAll(); } }
ApiUtil 생성
- Api에서 Header에 상태코드가 있지만 Body에도 상태코드를 담아서 보내줘야 한다.
- Body를 파싱해서 Client에게 error message를 보내줘야 하기 때문
- Header의 상태코드에 상관없이 Body를 파싱한다.
@Data public class ApiUtil<T> { private Integer status; // 200, 400, 404, 405 private String msg; // 성공, 실패 시 -> 정확한 메시지 private T body; public ApiUtil(T body) { this.status = 200; this.msg = "성공"; this.body = body; } public ApiUtil(Integer status, String msg) { this.status = status; this.msg = msg; this.body = null; } }
BoardApiController 수정
@RequiredArgsConstructor @RestController public class BoardApiController { private final BoardRepository boardRepository; @GetMapping("/api/boards") public ApiUtil<?> findAll() { List<Board> boardList = boardRepository.selectAll(); return new ApiUtil<>(boardList); } }
실행 해보기

MessageConverter가 발동하여 JSON으로 바뀌는 것을 확인
jQuery, FontAwesome 추가
header.mustache head에 추가
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
jQuery 코드 작성

index.mustache
$.ajax({ url: "/api/boards", type: "get" }).done((res) => { console.log("통신 성공"); console.log(res); }).fail((res) => { console.log("통신 실패") // console.log(res); alert(res.responseJSON.msg); // location.href = "/loginForm"; });

getboard → render 함수 수정

한줄이 board


function render(board) { return ` <tr id="board-${board.id}"> <td>${board.id}</td> <td>${board.title}</td> <td>${board.content}</td> <td>${board.author}</td> <td> <div class="d-flex"> <button onclick="del(${board.id})" class="btn btn-danger">삭제</button> <form action="/board/${board.id}/updateForm" method="get"> <button class="btn btn-warning">수정</button> </form> </div> </td> </tr> `; }


let boardList = res.body; boardList.forEach((board)=>{ $("#board-box").append(render(board)); });
게시글 삭제
BoardApiController

@DeleteMapping("/api/boards/{id}") public ApiUtil<?> deleteById(@PathVariable Integer id){ boardRepository.deleteById(id); return new ApiUtil<>(null); }
BoardRepository - deleteById 만들기

@Transactional public void deleteById(Integer id) { Query query = em.createNativeQuery("delete from board_tb where id = ?"); query.setParameter(1, id); query.executeUpdate(); }
ajax 코드 작성

function del(boardId) { $.ajax({ url: `api/boards/${boardId}`, type: "delete" }).done((res) => { $(`#board-${boardId}`).remove(); }).fail((res) => { alert(res.response.JSON.msg); location.reload(); // F5 }); }
BoardApiController 수정

게시글 쓰기
JSON은 form태그 사용 불가능
saveForm.mustache 수정

버튼의 type을 button으로 하거나, form태그를 없애준다.
<div class="container p-5"> <div class="card"> <div class="card-header"><b>익명 글쓰기 화면입니다</b></div> <div class="card-body"> <form> <div class="mb-3"> <input type="text" class="form-control" placeholder="Enter author" id="author"> </div> <div class="mb-3"> <input type="text" class="form-control" placeholder="Enter title" id="title"> </div> <div class="mb-3"> <textarea class="form-control" rows="5" id="content"></textarea> </div> <button onclick="btnWrite()" type="button" class="btn btn-primary form-control">글쓰기완료</button> </form> </div> </div> </div>
btnWrite 함수 작동되는지 확인


BoardApiController

BoardRequest 생성

public class BoardRequest { @Data public static class WriteDTO{ private String title; private String content; private String author; } }
PostMapping 수정

insert 수정

테스트 코드 수정

POSTMAN으로 요청해보기


ajax 작성

<script> function btnWrite(){ // JavaScript Object let board = { title: $("#title").val(), content: $("#content").val(), author: $("#author").val() }; console.log(board); // JavaScript Object -> JSON으로 바꾸기 // let boardJson = JSON.stringify(board); // console.log(boardJson); $.ajax({ url: "/api/boards", type: "POST" , data: JSON.stringify(board), contentType: "application/json; charset=utf-8" }).done((res)=>{ location.href = "/"; }).fail((res)=>{ alert(res.responseJSON.msg); }); } </script>
게시글 수정
updateForm.mustache 수정
<div class="container p-5"> <div class="card"> <div class="card-header"><b>익명 글수정 화면입니다</b></div> <div class="card-body"> <form> <div class="mb-3"> <input type="text" class="form-control" placeholder="Enter author" id="author"> </div> <div class="mb-3"> <input type="text" class="form-control" placeholder="Enter title" id="title"> </div> <div class="mb-3"> <textarea class="form-control" rows="5" id="content"></textarea> </div> <button onclick="btnUpdate()" type="button" class="btn btn-primary form-control">글수정완료</button> </form> </div> </div> </div>
btnUpdate() 함수 작동 확인
<script> function btnUpdate(){ alert("클릭되나"); } </script>

BoardApiController

BoardRepository update 틀 만들기

UpdateDTO와 WriteDTO가 같으므로 WriteDTO 이름 변경


DTO의 구성이 같더라도 새로운 DTO를 하나 더 만드는 것이 좋다.
변경될 수도 있기 때문에(수정은 제목과 내용만 하는 것으로) → 사용자 관점
update 완성

BoardApiController

@PutMapping("/api/boards/{id}") public ApiUtil<?> update(@RequestBody BoardRequest.WriteAndUpdateDTO requestDTO, @PathVariable Integer id){ boardRepository.update(requestDTO, id); return new ApiUtil<>(null); }
postman 요청


ajax 코드 수정
<script> function btnUpdate(){ // JavaScript Object let board = { title: $("#title").val(), content: $("#content").val(), author: $("#author").val() }; // console.log(board); // JavaScript Object -> JSON으로 바꾸기 // let boardJson = JSON.stringify(board); // console.log(boardJson); $.ajax({ url: `/api/boards/{{id}}`, type: "PUT" , data: JSON.stringify(board), contentType: "application/json; charset=utf-8" }).done((res)=>{ location.href = "/"; }).fail((res)=>{ alert(res.responseJSON.msg); }); } </script>
BoardApiController update 수정
id를 가져오기 위해 setAttribute 해야함

@PutMapping("/api/boards/{id}") public ApiUtil<?> update(@RequestBody BoardRequest.WriteAndUpdateDTO requestDTO, @PathVariable Integer id, HttpServletRequest request){ boardRepository.update(requestDTO, id); request.setAttribute("id",id); return new ApiUtil<>(null); }

@GetMapping("/board/{id}/updateForm") public String updateForm(@PathVariable int id, HttpServletRequest request) { Board board = boardRepository.selectOne(id); request.setAttribute("board",board); return "board/updateForm"; }
UpdateForm 수정

<div class="container p-5"> <div class="card"> <div class="card-header"><b>익명 글수정 화면입니다</b></div> <div class="card-body"> <form> <div class="mb-3"> <input type="text" class="form-control" placeholder="Enter author" id="author" value="{{board.author}}"> </div> <div class="mb-3"> <input type="text" class="form-control" placeholder="Enter title" id="title" value="{{board.author}}"> </div> <div class="mb-3"> <textarea class="form-control" rows="5" id="content">{{board.author}}</textarea> </div> <button onclick="btnUpdate()" type="button" class="btn btn-primary form-control">글수정완료</button> </form> </div> </div> </div>
Share article