상품을 등록하는 기능을 구현할때 여러장의 사진이 같이 저장되도록 구현해야했다.
리액트로 앞단에서 json형태의 문자열으로 서버로 post해주고 product테이블과 productImages테이블에 저장했다.

사진의 [등록하기] 버튼을 클릭하면 onChangeButton함수가 실행되고 상품을 저장하는데 필요한 정보(상품 이름, 카테고리, 한줄설명, 상세설명, 썸네일 이미지, 상세이미지들)를 updateObj라는 객체로 만들어서 묶어준다. 객체형태로 데이터를 보내게되면 서버와 클라이언트 간의 데이터 전달 구조가 명확해지고, 어떤 데이터를 주고받는지 파악하기 쉬워진다.
const onChangeButton = e => {
const updatedObj = {
productName: title,
productCategory: kind,
shortDescription: shortDesc,
detailedDescription: details,
thumbnailImage: mainImg.name, // 파일 이름 설정
imageUrl : imgList.map(img => img.originFile.name)
};
const updatedList =
kind === 'kiosk'
? [...kioskList, updatedObj]
: kind === 'cctv'
? [...cctvList, updatedObj]
: [...otherList, updatedObj];
kind === 'kiosk'
? setKioskList(updatedList)
: kind === 'cctv'
? setCctvList(updatedList)
: setOtherList(updatedList);
post(updatedObj, mainImg, imgList); // 파일 객체를 전달
};
등록할 때 선택한 카테고리가 어떤건지 검사한 후 kind에 저장, post함수의 인자로 만들었던 updateObj, mainImg, imgList를 담는다. 세개를 따로 담는 이유는 FormData에 따로 추가로 해줄것이기 때문이다.
const post = (data, file, imageFiles) => {
const formData = new FormData();
formData.append('product', JSON.stringify(data)); // JSON 데이터를 추가
formData.append('upfile', file); // 파일 추가 (업로드할 파일 객체 전달)
//formData.append('images', file);
// 상세 이미지 파일을 반복문으로 추가
imageFiles.forEach((imageFile) => {
if (imageFile.originFile) {
formData.append('images', imageFile.originFile); // 각 파일을 'images' 키로 추가
}
});
axios
.post('http://localhost:8989/product/save', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
.then(response => {
console.log('Response:', response.data);
})
.catch(error => {
if (error.response) {
console.error('Server Error:', error.response.data);
} else {
console.error('Error:', error.message);
}
});
};
post함수에서 세가지 인자를 받아서 FormData객체를 만들어서 .append로 저장할 수 있다. js가 제공하는 FormData객체는 폼데이터를 쉽게 다룰수 있게 해주기 때문에 서버에 전송할때 유용하다. 상세이미지는 여러장이 들어가기 때문에 반복문으로 하나씩 넣어준다.
axios로 비동기 post요청을 보내고나면 Controller가 요청을 보낸 url대로 매핑해 save메서드를 실행시킨다

앞단에서 상품정보를 json형태를 띈 문자열로 보냈기 때문에 @RequestParam으로 받을 수 있다. 그리고 ObjectMapper()객체를 이용해 다시 객체로 파싱한 후 썸네일 이미지도 저장한다.
썸네일 이미지는 한장이기 때문에 saveFile메서드로, 상세이미지는 여러장이기 때문에 배열형태인 saveImages메서드로 저장한다.
*이때 썸네일 이미지는 product테이블에 들어가야하고 상세보기 이미지는 productImages테이블에 들어가야한다. (erd사진 참고)

상세보기 이미지를 저장할때는 1. 이미지 아이디, 2. 경로, 3. 순서, 4. 상품아이디가 필요하다.
상품의 아이디는 DB에 상품이 저장될 때 부여받게끔 GENERATED BY DEFAULT AS IDENTITY로 생성했기 때문에 상품 저장을 먼저 하여 productId를 부여받은 뒤 상세보기 이미지를 저장해야한다.
이렇게 내가 짠 코드를 자세하게 작성해보니 필요없는 코드들도 발견할 수 있어서 좋은것 같다.