티스토리 뷰
개발 환경
Spring Boot 2.5.1
JAVA 11
먼저 성공하는 것을 알려주고 에러, 바인딩 실패하는 사례도 보여주겠습니다.
작성하는 이유
우린 데이터를 Front → Back 으로 보낼 때 json형식의 String만 보내는 것이 아닌
데이터 + 첨부파일을 함께 전송할 때가 많다.. 그것도 여러 데이터 + 여러 첨부파일..
<form> 태그를 이용하여 서버로 전송하면 좋겠지만 클라이언트(Front) 단에서는 ajax, Fetch에서 FormData 라는 객체를 생성하여 데이터를 보낸다. 그래서 서버가 그 데이터를 받을 때 여러 시도 하지 말라고 작성하게 되었다.
코드
index.html
ajax와 bootstrap은 cdn으로 넣어서 사용하였다.
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<div class="py-5 text-center">
<h2>상품 등록 폼</h2>
</div>
<form class="container mt-5">
<div class="mb-3">
<label for="itemName" class="form-label">상품명</label>
<input type="text" class="form-control" id="itemName" name="itemName">
</div>
<div class="mb-3">
<label for="price" class="form-label">가격</label>
<input type="text" class="form-control" id="price" name="price">
</div>
<div class="mb-3">
<label for="showStart" class="form-label">판매 시작일자</label>
<input type="date" class="form-control" id="showStart" name="showStart">
</div>
<div class="mb-3">
<label for="showEnd" class="form-label">판매 종료일자</label>
<input type="date" class="form-control" id="showEnd" name="showEnd">
</div>
<div class="mb-3">
<label for="file" class="form-label">파일</label>
<input type="file" class="form-control" id="file" name="file">
</div>
<button type="button" id="btnSave" class="btn btn-primary">제출</button>
</form>
</div>
</body>
</html>
Script.js
FormData 객체 생성하여 데이터 하나하나 넣기
$('#btnSave').click(function(){
save();
})
function save(){
const itemName = $('#itemName').val();
const price = $('#price').val();
const showStart = $('#showStart').val();
const showEnd = $('#showEnd').val();
const file = $('#file')[0].files[0];
let formData = new FormData();
formData.append("itemName", itemName);
formData.append("price", price);
formData.append("showStart", showStart);
formData.append("showEnd", showEnd);
formData.append("file", file);
$.ajax({
url: `/spring/upload`,
method: 'POST',
data : formData,
processData: false,
contentType: false
})
.done(function(data) {
console.log('요청 성공 - done 콜백:', data);
})
.fail(function(xhr, status, error) {
console.log('요청 실패 - fail 콜백');
console.log('Error: ' + error);
console.log('Status: ' + status);
console.dir(xhr);
})
.always(function(xhr, status) {
console.log('요청 완료 - always 콜백');
});
}
Controller
데이터가 객체에 바인딩이 되나 안되는지만 확인하기 위해 최소한만 작성
@Slf4j
@Controller
@RequestMapping("/spring")
public class SpringUploadController {
@ResponseBody
@PostMapping("/upload")
public Object save(@ModelAttribute RequestFormData formData,
@RequestParam(name = "file") MultipartFile file,
HttpServletRequest request) {
log.info("여기 : {}", formData);
log.info("여기 : {}", file.getName());
log.info("타입 : {}", request.getContentType());
return formData;
}
}
/* 출력
여기 : RequestFormData(id=null, itemName=아이폰XS, price=50000, showStart=2024-02-09, showEnd=2024-02-29)
여기 : file
타입 : multipart/form-data; boundary=----WebKitFormBoundaryN02h6LywGXTY6Nrd
*/
테스트 화면
HTML에서 필요한 데이터를 입력
제출버튼 눌렀을 시 보내지는 데이터
바인딩 된 RequestFormData 객체
실패 했을 때의 경우
실패 경우 a) ajax에서 여러개의 필드를 한객체로 모아서 formData에 그 객체를 넣었을 경우
위에 스크립트 코드와 다르게 아래와 같이 작성했다고 하자
let param = {
itemName : itemName,
price : price,
showStart : showStart,
showEnd : showEnd
}
let ajaxData = JSON.stringify(param);
let formData = new FormData();
formData.append("info", ajaxData);
formData.append("file", file);
이 때 컨트롤러는 아래와 같이 받을려고 할 것이다.
@PostMapping("/upload")
@ResponseBody
public Object save(@ModelAttribute RequestFormData formData,
@RequestParam(name = "file") MultipartFile file,
HttpServletRequest request) {
log.info("여기 : {}", formData);
log.info("여기 : {}", file.getName());
log.info("타입 : {}", request.getContentType());
return formData;
}
/* 출력
여기 : RequestFormData(id=null, itemName=null, price=null, showStart=null, showEnd=null)
여기 : file
타입 : multipart/form-data; boundary=----WebKitFormBoundaryN02h6LywGXTY6Nrd
*/
분명 데이터는 보냈는데
파일은 도착했지만 각각 데이터는 바인딩이 안된 것을 볼 수 있다. 그래서 이렇게 사용하지 말자
실패 경우 b) @RequestBody이용
아래와 같이 데이터를 보낼 것이고
let formData = new FormData();
formData.append("itemName", itemName);
formData.append("price", price);
formData.append("showStart", showStart);
formData.append("showEnd", showEnd);
formData.append("file", file);
컨트롤러에서 @RequestBody로 받아보자
@PostMapping("/upload")
@ResponseBody
public Object save(@RequestBody RequestFormData formData,
@RequestParam(name = "file") MultipartFile file,
HttpServletRequest request) {
// ~~ 이하 생략
그럼 이제 HttpMediaTypeNotSupportedException 예외가 발생하기 때문에 애초에 바인딩 되지 못합니다.
에러 내용 : Content type 'multipart/form-data;boundary=----WebKitFormBoundaryyTd4E9CuzAzIrLCp;charset=UTF-8' not supported]
이 나오게 됩니다.
감사합니다.
'백엔드 > 🌸Spring' 카테고리의 다른 글
[Spring] Spring Boot3에 Swagger 적용하기 (6) | 2024.10.17 |
---|---|
[Spring] ThreadLocal : 동시성을 해결하는 물품 보관소 (0) | 2024.05.10 |
[Spring] ArgumentResolver : @Pathvariable 요청 정보 가져오기 (0) | 2024.04.24 |
[Spring] Validation : 데이터 유효성 검사의 핵심 요소 (0) | 2024.03.27 |
[Spring] 빈이 중복 조회가 된다면? : @Autowired, @Qualifier, @Primary 의존성 주입의 다양한 접근 방법 (0) | 2023.12.19 |
- Total
- Today
- Yesterday
- 디자인패턴
- 프론트
- DBeaver
- Spring Security
- 프로세스
- 개발자
- java
- 오라클
- Front
- 데이터 베이스
- spring
- 코딩테스트
- Mac
- 자바스크립트
- 개발환경
- Fetch
- 템플릿
- JavaScript
- 개발
- 개발블로그
- Cors
- 네트워크
- git
- aws
- 비동기
- AJAX
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |