fetch 함수를 이용하면 새로고침 없이 api를 호출하여 http 요청을 보내고 응답을 받아올 수 있다.
이번 포스팅은 스프링으로 개발된 백엔드 코드의 api를 호출하여 json 객체를 fetch 함수를 이용하여 응답을 받아보는 걸 배운 과정을 기록할 것이다.
과정 속에서 내가 발생시킨 어이없는 실수, 멍청한 실수들을 기록해두어, 다음엔 실수하지 않도록 하기 위함이다.
꼭 백엔드 환경이 스프링+자바가 아니더라도 상관없다.
참고로 fetch 공식 문서는 아래 링크에서 확인가능하다.
https://ko.javascript.info/fetch
fetch 함수 - GET
아래와 같은 응답을 보내주는 api가 있다.
@GetMapping("/rooms")
public ResponseEntity<List<RoomDto>> showRooms() {
final List<RoomDto> roomDtos = springChessService.showRooms();
return ResponseEntity.ok(roomDtos);
}
http status는 200, 현재 존재하는 체스 방 정보들을 json 객체로 보내주는 api이다.
이 경우, js에선 아래와 같이 작성하면 된다.
const getRoomList = async () => {
let rooms = await fetch('/rooms');
rooms = await rooms.json();
return rooms;
}
fetch 함수에 url만 지정해줄 경우, 디폴트로 GET 메서드로 진행되어 url로부터 컨텐츠를 받아온다.
fetch는 비동기 처리 함수이기 때문에 async 함수에서만 사용할 수 있다.
fetch함수가 작동될 때까지 await해주고, 정상적으로 응답이 왔다면 응답 본문을 json 파싱하는 과정 또한 await을 걸어주자.
이렇게 하면 현재 존재하는 체스 방 정보들이 rooms에 성공적으로 json객체 꼴로 담기게 된다.
@GetMapping("/{id}")
public ResponseEntity<ChessMap> start(@PathVariable long id) {
return ResponseEntity.ok(springChessService.initializeGame(id));
}
참고로 위와 같이 PathVariable이 존재할 경우, fetch에선 아래와 같이 받아주면 된다.
let chessMap = await fetch('/' + roomId);
chessMap = await chessMap.json();
showChessMap(chessMap.chessMap);
chessMap을 json 객체로 받아와서, chessMap 객체의 chessMap 속성을 보여주는 것이다. (프로퍼티명을 헷갈리게 짜놨다...)
fetch 함수 - POST
이제 GET이 아닌 형태를 살펴보자.
@PostMapping("/delete/{id}")
public ResponseEntity<Boolean> deleteGame(@PathVariable long id,
@RequestBody RoomDeleteRequestDto roomDeleteRequestDto) {
return ResponseEntity.ok(springChessService.deleteChessRoom(roomDeleteRequestDto, id));
}
위 메서드는 @RequestBody로 요청을 받으면, 그 값을 이용해 체스방을 삭제하고 결과를 응답으로 보내주는 메서드이다.
참고로 요청을 받은 RoomDeleteRequestDto에는 체스방을 삭제하기 위한 인증정보인 비밀번호가 들어가있다.
위 응답을 요청하기 위한 js코드는 아래와 같다.
const deleteRoom = async (e) => {
// 비밀번호를 입력받는다.
const password = window.prompt('비밀번호를 입력하세요');
// 비밀번호 검증
if (password.length < 1 || password.length > 15) {
alert('비밀번호를 1자 이상 15자 이하로 입력해주세요');
return;
}
// fetch 함수
const bodyValue = {
password: password
};
await fetch('/delete/' + e.target.id, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
'Accept': 'application/json'
},
body: JSON.stringify(bodyValue)
});
// 현재 리소스를 다시 불러오도록 (새로고침과 유사한 기능)
window.location.reload();
}
삭제 버튼을 눌러야 작동되는 함수기 때문에 event를 인자로 받는다.
fetch 함수를 보면 url외에 추가적으로 정보가 더 있는 것을 확인할 수 있다.
method는 POST임을 명시해주었고, headers에 json 객체라는 것을 알려주었다.
그리고 RequestBody의 요청으로 보내줄 정보를 body에 심어주는 것인데, bodyValue꼴로 보내면 된다고 명시해주는 것이다.
참고로 /delete + e.target.id로만 보내게 되면 '/delete/{id}' 꼴이 아닌 '/delete{id}'꼴이 되는 것에 유의하자.
실수하기 좋은 부분이다.
자바스크립트는 따로 디버깅하기 힘들어서 400 에러든 404에러든 무언가를 만나게 될 것이다.
마지막으로 아래 메서드를 보자.
@PostMapping("/start")
public ResponseEntity<?> createGame(@RequestBody RoomRequestDto roomRequestDto) {
final long roomId = springChessService.makeChessRoom(roomRequestDto);
return ResponseEntity.created(URI.create("/" + roomId)).body(roomId);
}
체스룸을 새롭게 생성하여 body에 roomId를 응답으로 보내주는 메서드이다.
참고로 created는 http status 201이다.
위 응답을 처리하는 메서드는 아래와 같다.
const makeRoomByRequest = async () => {
// .value를 안해주면 인식하지 못함!
const roomName = document.getElementById('roomName').value;
const roomPassword = document.getElementById('roomPassword').value;
// ...
const bodyValue = {
name: roomName,
password: roomPassword
};
roomId = await fetch('/start', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
'Accept': 'application/json'
},
body: JSON.stringify(bodyValue)
});
roomId = await roomId.json();
// ...
}
주의해야 될 점이, input 값으로 roomName과 roomPassword를 입력받아 요청에 보내주려할 때,
.value를 해주지 않으면 인식하지 못한다! input은 태그라는 점에 유의하자. (이거 때문에 1시간 삽질했다...)
나머지는 위와 동일하다.
자바스크립트에선 디버깅이 상당히 어렵기 때문에 DOM에 대한 공부를 정말 꼼꼼히 해두는 것이 정신건강에 좋다는 것을 느꼈다....
fetch 함수 개념 및 응용을 아주아주 간단히 알아보면서, 삽질 내용도 간단하게 적어보았다.
내 전체 코드는 아래 링크에서 확인가능하다.
https://github.com/kth990303/jwp-chess/tree/step2
+) 아직 많이 배우는 중이라 지적해줄 부분이나 틀린 부분이 있다면 피드백해주세요! 환영합니다~
js, css에 익숙하지 않아 fetch함수에 적응하는 데에 힘들었지만, 내 손으로 직접 자바스크립트 코드도 짜다보니 더욱 보람차고 내 손으로 만들었다는 성취감이 느껴져서 행복하다.
이런 게 개발의 재미이지 않나 싶다 ㅎㅎ