AJAX : 스프링 셋팅하기

Feb 22, 2024
AJAX : 스프링 셋팅하기

1. 초기 셋팅하기

  • 연결 끊고 내 GitHub에 Repository 추가해서 연결하기
notion image
 

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}}
notion image
 
  • 바뀔 데이터들은 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로 바인딩하면 됨
notion image
 

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만 주면 글자를 뿌릴 수 없음, 정확한 분기 처리를 위한 정확한 데이터가 필요함
notion image
실패해도 무조건 파싱해야함
파싱하고 body만 돌려주면 됨
컨트롤러 입장에서는 body에 상태코드가 없으면 400인지 알 수가 없음
body에 상태코드, 메세지, 응답되는 데이터(null일 수 있음)가 담겨있음
→ 성공하면 데이터를 입히고 실패하면 데이터를 파싱해서 alert창 띄우기
→ 성공 여부를 알 수 있는 상태 코드, 응답 데이터, 메세지(실패했을 때 중요)가 필요함
notion image
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); } }
notion image
notion image
Share article

vosw1