⌨️ 리뷰 작성 구현
multipart 파일을 <form> 태그가 아닌 Axios를 통해 전달하며, 미리 보기 방식을 Vue.js에 맞게 바꾸면서 많은 어려움이 있었다.
1. 리뷰 작성
<template>
<Header />
<hr class="header_hr">
<div class="all">
<div class="title">
<h1>리뷰 등록</h1><br>
</div>
<div class="wrapper">
<div class="left">
<section v-if=walkingPath>
<div class="walking-path">
<img v-if="walkingPath.photosList == 0" src="/images/noimage.png" alt="">
<img v-if="walkingPath.photosList > 0"
:src="'http://localhost:8089/ex_images/' + walkingPath.photosList[0].imgName" alt="" />
<span>{{ walkingPath.title }}</span>
<section v-if="walkingPath.mapList > 0">
<span>{{ walkingPath.mapList[0].distance }}</span>
</section>
<span>{{ walkingPath.addr }}</span>
</div>
</section>
</div>
<div class="review_form">
<div class="form">
<form @submit.prevent="postReview">
<section class="write">
<span>별점</span> <br>
<span value="5">★★★★★</span><br>
<span>본문</span> <br>
<textarea name="content" v-model="content" rows="10" cols="60" wrap="soft"
style="resize: none; padding: 10px" required></textarea><br>
<span>사진(5개 제한)</span><br>
<section class="img-area">
<div id="review_image_container" class="image_container">
</div>
<div class="filebox">
<label for="file">+</label>
<input id="file" type="file" @change="readInputFile" multiple />
</div>
</section>
</section>
<section class="submit">
<input type="submit" value="저장" class="btn-review">
<button class="btn-ref"><router-link
:to="`/walking-path/${props.id}`">뒤로가기</router-link></button>
</section>
</form>
</div>
</div>
</div>
</div>
<Footer />
</template>
리뷰 작성의 <template> 부분
1.1. request Axios 만들기
function postReview() {
const formData = new FormData();
const reviewsRequestDTO = {
content: content.value
}
const json = JSON.stringify(reviewsRequestDTO);
const blob = new Blob([json], { type: 'application/json' });
formData.append('reviewsRequestDTO', blob);
if (files.value && files.value.length > 0) {
files.value.forEach((file) => {
formData.append('files', file);
});
} else {
formData.append('files', new Blob(), '');
}
// formData의 내용을 로그로 출력
for (let pair of formData.entries()) {
console.log(pair[0] + ', ' + pair[1]);
}
axios.post(`http://localhost:8089/${props.id}/reviews`, formData, {
headers: {
Authorization: JSON.parse(localStorage.getItem("token")).authorization,
'Content-Type': 'multipart/form-data', // 수정된 부분
},
}).then(response => {
if (response.status == 201) {
router.push('/walking-path/' + props.id)
}
})
// .then(() => close(undefined))
.catch(error => console.log(error))
}
</script>
리퀘스트를 하는 function 이다. 이 코드를 짜면 발생한 이슈들에 대해 이야기하며 정리해 보자.
ISSUE
- 서버에서 multipart/octet-stream으로 데이터를 받은 경우
- file이 null이 들어가는 경우
크게 위 두 가지 경우로 압축할 수 있을 것 같다.
1. octet-stream의 경우
-> 나의 문제는 첫 번째로는 application/json 파트와 files 파트를 나눠서 보내줄 때 발생했다.
처음의 코드는
<script>
const requestReviewsDTO = { content: content. value}
...
formData.append('requestReviewsDTO',application/json)
...
</script>
식으로 formData 객체에 append 해주었다.
이렇게 하니 문제없이 전송이 되었는데 이는 사실 files가 제대로 전송되지 않아 가능했던 것이다.
아래 작성할 미리 보기 구현하는 코드에서 실수가 있어 files가 제대로 전달되지 않아
파트가 application/json과 files " " 로 전달되었기에 문제가 없었다.
-> files를 제대로 전송하니 생기는 오류
files가 제대로 입력받아지니 octet-stream이 뜨기 시작했다.
이에 나는 json 객체를 files와 같은 blob 객체로 만들고 타입을 json으로 지정해 주는 것으로 해결했다.
1.2. 미리 보기 구현하기
const readInputFile = (e) => {// 미리보기 기능구현
const review = document.getElementById('review_image_container')
review.innerHTML = '';
var file = e.target.files;
//e.target.files;
var fileArr = Array.from(file);
files.value = fileArr;
console.log(fileArr)
fileArr.forEach(function (f) {
if (!f.type.match("image/.*")) {
alert("이미지 확장자만 업로드 가능합니다.");
return;
};
var reader = new FileReader();
reader.onload = function (e) {
const img = document.createElement('img');
img.style.width = "100px";
img.src = e.target.result;
review.appendChild(img);
};
reader.readAsDataURL(f);
})
}
미리 보기에서도 주소API와 마찬가지로 미리보기 업로드한 files을 바인딩하는 과정에서 실수가 있어서 데이터가 제대로 넘어가지 않았던 것이다.
해당 files을 잘 바인딩하니 문제없이 서버로 데이터가 넘어갔다.
2. 피드백
- 백엔드만 구현할 때는 몰랐던 multipart의 문제점을 알게 됐다.
- 협업을 위해서는 다른 사이드의 기초 지식도 있으면 좋겠다는 생각을 한다.