Fetch API 데이터 전송 (기본)
클라이언트(html, js)
- index.js
<!DOCTYPE html>
<html lang=ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-compatible" content="IE=dege">
<title>My first Web Page</title>
</head>
<body>
<h1>fetch API 비동기 처리</h1>
<form>
<label for="id">아이디 :</label>
<input type="text" id="id"> <br>
<label for="pw">비밀번호 :</label>
<input type="password" id="pw"><br>
<!-- <input type="file" id="file"> -->
<button type="submit" id="submit">전송</button>
</form>
<script src="/HTML_CSS_JS/index.js"></script>
</body>
</html>
- index.js
const addIndexEvent = () => {
const buttonEvent = document.getElementById("submit");
buttonEvent.addEventListener("click", submitServer);
};
const submitServer = async (event) => {
event.preventDefault();
const COMMON_URL = 'http://localhost:8080';
const param = {
'id': document.getElementById('id').value,
'pw': document.getElementById('pw').value
};
const option = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(param)
};
try {
const res = await fetch(`${COMMON_URL}/save`, option);
if (!res.ok) {
throw new Error('Network response was not ok');
}
const data = await res.json();
console.log(data);
} catch (error) {
console.error('Error:', error);
}
};
addIndexEvent();
async와 await를 사용하는 주된 이유는 비동기 코드를 작성할 때, 보다 간결하고 직관적으로 코드를 작성하고 관리할 수 있기 때문입니다.
서버(SpringBoot)
@Getter
public class SaveDTO {
private String id;
private String pw;
}
@Slf4j
@RestController
public class FetchApiTest {
@PostMapping("/save")
public SaveDTO save(@RequestBody SaveDTO saveDTO){
log.info("name={}, age={}", saveDTO.getId(), saveDTO.getPw());
return saveDTO;
}
}
Fetch API 데이터 전송 + File
JSON 형식으로 같이 보내기가 어렵다. MultipartFile 형태는 클라이언트에서 요청보낸 JSON 타입을 역직렬화(deserialize) 할 수 없기 때문이다. 파일형태의 요청은 json이 아닌 multipart/form-data 형식으로 보내야 한다.
서버 측 코드에서 MultipartFile를 사용하여 JSON 데이터를 직접 받으려고 시도: JSON으로 파일 데이터를 받으려고 하면 발생하는 오류입니다. MultipartFile는 일반적으로 multipart/form-data 형식으로 전송된 파일 데이터를 처리하기 위해 사용됩니다. JSON을 사용하여 파일 데이터를 직접 전송하는 것은 지원되지 않습니다.
- 클라이언트
<!DOCTYPE html>
<html lang=ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-compatible" content="IE=dege">
<title>My first Web Page</title>
</head>
<body>
<h1>fetch API 비동기 처리</h1>
<form>
<label for="id">아이디 :</label>
<input type="text" id="id"> <br>
<label for="pw">비밀번호 :</label>
<input type="password" id="pw"><br>
<input type="file" id="file">
<button type="submit" id="submit">전송</button>
</form>
<script src="/HTML_CSS_JS/index.js"></script>
</body>
</html>
const addIndexEvent = () => {
const buttonEvent = document.getElementById("submit");
buttonEvent.addEventListener("click", submitServer);
};
const submitServer = async (event) => {
event.preventDefault();
const COMMON_URL = 'http://localhost:8080';
const formData = new FormData();
formData.append('id', document.getElementById('id').value);
formData.append('pw', document.getElementById('pw').value);
formData.append('image', document.getElementById('file').files[0]);
const option = {
method: 'POST',
headers: {
},
body: formData
};
try {
const res = await fetch(`${COMMON_URL}/save`, option);
if (!res.ok) {
throw new Error('Network response was not ok');
}
const data = await res.json();
console.log(data);
} catch (error) {
console.error('Error:', error);
}
};
addIndexEvent();
- 서버
@Getter
public class SaveDTO {
private String id;
private String pw;
private String fileName;
private String contentType;
public SaveDTO(String id, String pw, MultipartFile image) {
this.id = id;
this.pw = pw;
this.fileName = image.getOriginalFilename();
this.contentType = image.getContentType();
}
}
@Slf4j
@RestController
public class FetchApiTest {
@PostMapping("/save")
public SaveDTO save(
@RequestParam String id,
@RequestParam String pw,
@RequestParam MultipartFile image
){
log.info("name={}, age={}, image={}", id, pw, image.getOriginalFilename());
return new SaveDTO(id, pw, image);
}
}
❗️formData에 파일을 포함하여 서버로 전송하게 될때는 Content-Type을 정의하면 안됩니다.
**formData**에 파일을 포함하여 서버로 전송할 때 **Content-Type**을 수동으로 설정하지 않는 이유는 브라우저가 자동으로 필요한 정보를 추가하기 때문입니다. 이 자동화된 과정은 Content-Type 헤더에 multipart/form-data 뿐만 아니라, 필요한 boundary 값을 포함합니다. 이 boundary 값은 각 파트를 구분하는 데 사용되며, 데이터 스트림 내의 각 부분을 분리하는 역할을 합니다.
**Content-Type**을 수동으로 설정할 때 **multipart/form-data**를 포함하지만 적절한 boundary 값을 포함하지 않는 경우, 요청이 제대로 작동하지 않을 수 있습니다. 이는 boundary 값이 각 파트를 구분하는 데 필수적이기 때문입니다.
**boundary**는 멀티파트 메시지에서 각 파트를 분리하는 데 사용되는 구분자입니다. 이 구분자는 메시지의 본문에 포함되는 각 파트의 시작과 끝을 명시합니다. 서버가 요청을 받았을 때, 이 **boundary**를 사용하여 본문의 각 파트를 올바르게 인식하고 파싱합니다.
멀티파트 데이터 전송에서는 **boundary**를 사용하여 메시지의 각 파트를 구분합니다. 이 **boundary**는 Content-Type 헤더 내의 multipart/form-data 타입과 함께 사용되어, 데이터 스트림 내에서 각 파트의 시작과 끝을 명확하게 식별할 수 있도록 합니다.
JSON 형식은 **boundary**가 필요 없는 데이터 형식입니다. boundary는 멀티파트 메시지(multipart/form-data)에서 사용되며, 주로 파일 업로드와 같이 복수의 데이터 스트림이나 대량의 바이너리 데이터를 전송할 때 필요합니다. 각 데이터 스트림을 구분하고, 데이터의 시작과 끝을 명확하게 나타내는 데 사용됩니다.
'SpringBoot' 카테고리의 다른 글
[Spring Boot] @Configuration vs @Component (0) | 2024.04.30 |
---|---|
[Spring Boot] Validation Annotation (DTO) (0) | 2024.04.29 |
[Spring Boot] CORS 설정하기 (0) | 2024.04.28 |