티스토리 뷰

스프링 로고

 

 

 

개발 환경

 

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]

이 나오게 됩니다.

 

 

감사합니다.

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
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
글 보관함