1. 초기 셋팅하기
- 연결 끊고 내 GitHub에 Repository 추가해서 연결하기
2. AJAX 문법
- $.ajax( url {, settings } ) : setting안에 url을 집어넣을 수 있음
- $.ajax( {settings} )
- url : string
- setting: js object
$.ajax({ accepts: { mycustomtype: 'application/x-some-custom-type' }, // Instructions for how to deserialize a `mycustomtype` converters: { 'text mycustomtype': function(result) { // Do Stuff return newresult; } }, // Expect a `mycustomtype` back from server dataType: 'mycustomtype' });
- async : 디폴트가 true 건드릴 일 없음
- accepts : 건드릴 일이 없음, 적지도 않을 것임, 대부분 json임
- contentType : default: application/x-www-form-urlencoded; charset=UTF-8
- context :
- data : javascript object 형태, x-www-form-urlencoded
a=bc&d=e%2Cf
a=bc: "a"라는 이름의 값이 "bc"
d=e%2Cf`: "d"라는 이름의 값이 "e,f"로 URL 인코딩된 값
쉼표(,)는 URL에서 특수 문자이므로 %2C로 인코딩
a%5B%5D=1&a%5B%5D=2
"a"라는 이름의 값이 배열로 표현, 값은 각각 1과 2
%5B%5D는 "[]"를 URL 인코딩한 것
"a[]"라는 이름의 배열로 해석
- RFC : HTTP프로토콜들이 ?된 문서
- url을 safe하게 작성해야 함
주소에 board?, board=, board&는 안됨
퍼센트-인코딩을 한 후의 예약어 문자
! | # | $ | & | ' | ( | ) | * | + | , | / | : | ; | = | ? | @ | [ | ] |
%21 | %23 | %24 | %26 | %27 | %28 | %29 | %2A | %2B | %2C | %2F | %3A | %3B | %3D | %3F | %40 | %5B | %5D |
3. 더미 데이터를 가져와서 뿌릴 것
- 가방에 담는 것이 아니라 디자인만 줄 것
- 순수하게 INDEX 페이지로 이동
4. index.mustache에서 게시글 하나 빼고 다 삭제하기
{{> 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> <tr> <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> </tbody> </table> </div> {{> layout/footer}}
- 바뀔 데이터들은 json으로 다운 받아 사용할 것
- render함수 만들기
{{> 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 render(){ return `<tr> <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}}
- 처음에 이 틀만 주고 브라우저가 ajax 통신해서 다운받아 쓰게 됨
- 템플릿 엔진이 없어도 됨
- 순수한 html문서에 js로 바인딩하면 됨
5. ajax로 삭제하기 위해 id 추가하기
{{> 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 render(){ 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}}
5. 통신 코드 넣기
- 이 페이지가 로드되고 나서 바로 다운받아야 함
- 버튼 안눌러도 데이터가 나와야 함
- function안에 넣을 필요없음
- 넣어서 호출해도됨
- 다운 받으려면 json을 받을 수 있는 api가 서버에 없어서 만들어야 함
- 다 리턴되는게 템플릿 엔진, 파일들 = 컨트롤러
- 데이터를 리턴하는 것을 api컨트롤러라고 함 → 만들기
6. BoardApiController만들기
- board : 보드 줘라는 주소
- boards : 복수는 보드들 줘
- boards/1 : 보드들 중에 1번 줘 해서 보통 복수형을 씀
package shop.mtcoding.blog.board; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RequiredArgsConstructor @RestController // 데이러틀 리턴 public class BoardApiController { private final BoardRepository boardRepository; // DI // 주소 만들기 @GetMapping("api/board") // 보드 줘라는 주소, 복수는 보드들 줘, 보드들 중에 1번 줘해서 복수형을 씀 public void findAll(){ } }
7. BoardApiController에서 api/boards 주소 만들기
- 데이터만 주는 것, 얘만 응답하면 안됨
- 항상 상태 코드랑 메세지를 같이 줘야 함
바로 boardList를 주면 받는 쪽에서 if해서 분기 처리할 때 복잡해짐
그래서 상태 코드만 보면 됨
- 성공했을 때의 상태 코드, 메세지는 중요하지 않음
실패했을 때의 상태 코드와 메세지가 중요함
- 결과가 null이라고 무조건 오류라고 할 수 없음
- sucess : 상태 코드 → body말고 header에도 들어옴
그래도 body에 담아줘야 하는 이유 : 헤더에만 주면 메세지를 못 봄
→ body에 담아서 줘야 무슨 에러인지 알 수 있음
ex) 휴대폰 애도 동일하게 레이어가 있음
휴대폰도 레파지토리가 있음
레파지토리는 api서버에 연결됨
휴대폰 자체 내장된 데이터테이스가 있을 수 있음
내부에 sql write가 있음(메모장, 달력 등)
응답을 받을 때 http 프로토콜 요청이니까 header와 body를 받음
레파지토리의 책임 : 데이터를 잘 받아서 자기 오브젝트로 파싱하는 것
→ header에 상태코드 400이 들어오면? 실패 → body를 파싱할 필요가 없음
→ 통신 요청하고 나서 400확인하고 폰에 에러났다고 돌려줄 때 400만 주면 메세지를 못 봄
그래서 body에 담아서 줘야 무슨 에러인지 알 수 있음
→ 그냥 400만 주면 글자를 뿌릴 수 없음, 정확한 분기 처리를 위한 정확한 데이터가 필요함
실패해도 무조건 파싱해야함
파싱하고 body만 돌려주면 됨
컨트롤러 입장에서는 body에 상태코드가 없으면 400인지 알 수가 없음
body에 상태코드, 메세지, 응답되는 데이터(null일 수 있음)가 담겨있음
→ 성공하면 데이터를 입히고 실패하면 데이터를 파싱해서 alert창 띄우기
→ 성공 여부를 알 수 있는 상태 코드, 응답 데이터, 메세지(실패했을 때 중요)가 필요함
package shop.mtcoding.blog.board; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RequiredArgsConstructor @RestController // 데이러틀 리턴 public class BoardApiController { private final BoardRepository boardRepository; // DI // 주소 만들기 @GetMapping("api/boards") // 보드 줘라는 주소, 복수는 보드들 줘, 보드들 중에 1번 줘해서 복수형을 씀 public void findAll(){ List<Board> boardList = boardRepository.selectAll(); // 상태코드랑 메세지랑 같이 줘야함 } }
8. 응답의 공통 DTO - ApiUtil만들기
- 상태 코드 : 200, 400, 404, 405
- 메세지 : 성공, 실패시 -> 정확한 메세지
- 데이터 타입을 알 수 없으니 제네릭 사용
- new를 못하면 제네릭을 못 씀 이때 오브젝트를 사용
- new 할 수 있으면 제네릭을 사용
package shop.mtcoding.blog.board; import lombok.AllArgsConstructor; import lombok.Data; @AllArgsConstructor @Data public class ApiUtil<T> { private Integer status; // 상태 코드 : 200, 400, 404, 405 private String msg; // 메세지 : 성공, 실패시 -> 정확한 메세지 private T body; // 데이터 타입을 알 수 없으니 제네릭 사용 }
- 생성자 만들기
package shop.mtcoding.blog.board; import lombok.Data; @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 = body; } }
9. json으로 상태코드, 메세지, 바디 전송하기
- 오브젝트 걸고 리턴하면 스프링이 자동으로 json으로 변환해줌
@RestController @responseBody가 붙었을때
- 굳이 내가 오브젝트 맵퍼로 해서 변환해줄 필요가 없음
- 내가 response를 제어하면 스프링이 강제로 바꿔버림
- 무조건 apiUtil로 리턴하면 됨
- 오브젝트일때 MessageConverter라는 클래스가 오브젝트를 응답할 때 자동 발동함
- MessageConverter(추상 클래스) : 뭐가 올지 모르니까 추상화 되어 있음
- 첫번째 방법
package shop.mtcoding.blog.board; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RequiredArgsConstructor @RestController // 데이러틀 리턴 public class BoardApiController { private final BoardRepository boardRepository; // DI // 주소 만들기 @GetMapping("api/boards") // 보드 줘라는 주소, 복수는 보드들 줘, 보드들 중에 1번 줘해서 복수형을 씀 public ApiUtil<?> findAll() { // ApiUtil<List<Board>>도 가능 List<Board> boardList = boardRepository.selectAll(); // 상태코드랑 메세지랑 같이 줘야함 return new ApiUtil<>(200, "성공", boardList); } }
- 두번째 방법
package shop.mtcoding.blog.board; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RequiredArgsConstructor @RestController // 데이러틀 리턴 public class BoardApiController { private final BoardRepository boardRepository; // DI // 주소 만들기 @GetMapping("api/boards") // 보드 줘라는 주소, 복수는 보드들 줘, 보드들 중에 1번 줘해서 복수형을 씀 public ApiUtil<List<Board>> findAll() { List<Board> boardList = boardRepository.selectAll(); // 상태코드랑 메세지랑 같이 줘야함 return new ApiUtil<>(boardList); } }
Share article