쌓고 쌓다
[스프링 부트] 게시글 수정시 첨부파일 수정 방법 - 22 본문
먼저 게시글 수정 클릭시 첨부파일 수정 내용에 관한 처리를 어떻게했는지 이야기를 하자면
- 게시글 수정 html에서 해당 게시글의 파일 정보들을 가져온다.
- 파일 정보들을 첨부 파일 목록에 출력(load)한다.
- 첨부 파일 목록에 기존 파일 출력(load) 옆에 삭제버튼에 클릭 이벤트로 아래의 태그를 생성한다.
- <input type="hidden" name="deleteFilesId" value="해당 파일 PK">
- 이 태그를 통해서 서버 POST 요청에 deleteFilesId를 바인딩하여 해당 PK를 삭제하는 과정을 거칠 것이다.
- 추가로 첨부 파일에 넣는 파일들을 서버에서 List<MultipartFile>로 처리한다.
1. 해당 게시글의 파일 정보 API
@RestController
public class UploadFileController {
...
@GetMapping("/files/{posterId}")
public List<UploadFile> findFiles(@PathVariable("posterId") Long pno) throws IOException {
List<UploadFile> uploadFiles = uploadFileService.findByPno(pno);
return uploadFiles;
}
}
"/files/게시글PK"로 GET 요청이 오면 해당 게시글의 파일 정보들을 응답한다.
2. 게시글의 파일 정보 load
2.1 HTML
먼저 html에 작성한 태그들을 아래와 같다. file-list에 첨부파일명과 삭제 버튼을 넣을 것이다.
<input type="file" name="files" multiple="multiple" onchange="updateFiles(this.files)">
<div id="file-list">
</div>
파일 업로드 및 목록 수정에 관한 updateFiles 함수는 자세히 포스팅한적있다.
https://non-stop.tistory.com/540
2.2 파일 정보 요청 및 HTML에 뿌리기
$.ajax({
type: "GET",
url: `/files/[[${poster.id}]]`,
success: function (files) {
loadFiles(files);
}
})
ajax로 기존 파일 정보를 받아와 loadFiles 메서드로 화면에 뿌려주는 함수를 수행한다.
3. loadFiles - 기존 파일 뿌려주기
function loadFiles(files) {
const fileList = document.getElementById('file-list');
for(let i=0; i<files.length; i++) {
const item = document.createElement('div');
const fileName = document.createTextNode(files[i].uploadFileName);
const deleteButton = document.createElement('button');
deleteButton.addEventListener('click', (event) => {
item.remove();
event.preventDefault();
const deleteItem = document.createElement('input');
deleteItem.setAttribute("name", "deleteFilesId");
deleteItem.setAttribute("value", files[i].id);
deleteItem.setAttribute("type", "hidden");
fileList.appendChild(deleteItem);
});
deleteButton.innerText="X";
item.appendChild(fileName);
item.appendChild(deleteButton);
fileList.appendChild(item);
}
}
files 정보로 file-list에 파일명과 삭제 버튼을 생성하여 뿌려준다.
중요한 로직은 삭제버튼 클릭시 아래의 태그를 생성하여 보관한다.
- <input type="hidden" name="deleteFilesId" value="해당 파일 PK">
이 태그를 통해 submit하면 deleteFilesId로 서버에서 바인딩하여 처리할 예정이다.
3. 삭제 파일 및 추가 업로드 파일 처리
@Slf4j
@Controller
public class PosterController {
@PostMapping("/poster/edit")
public String edit(@RequestParam(value="id") Long id,
@RequestParam(required = false) List<MultipartFile> files,
@RequestParam(required = false) List<Long> deleteFilesId,
@Valid Poster poster, Errors errors) throws IOException {
if(errors.hasErrors()) {
return "posters/editPosterForm";
}
posterService.editPoster(id, poster, files, deleteFilesId);
return "redirect:/posters";
}
}
<input> 태그의 name 속성이 "deleteFilesId"로 동일했다.
동일한 이름으로 전달되는 값은 배열 형태로 서버로 전달된다.
삭제 처리된 파일을 PK 배열이 넘어오므로 이제 이것으로 처리만해주면 된다.
+ 동일한 input 태그의 name 속성값은 배열로 넘어온다.
MultiValueMap<String, Long>으로 받으려했으나 배열로 넘어오는걸 모르고 고생했다.
또한 @RequireParam의 name 속성을 주지않아 의도하지 않은
폼 데이터 값들도 들어가므로 name 속성으로 꼭 바인딩을 확실히 시키자.
+ 업로드 파일 처리 서비스의 삭제 메서드 주의
public void deleteByIds(List<Long> deleteFileIds) {
List<UploadFile> deleteFiles = uploadFileRepository.findAllById(deleteFileIds);
for (UploadFile deleteFile : deleteFiles) {
deleteUploadFile(deleteFile); // deleteUploadFile? 파일 삭제와 DB삭제 메서드
}
}
삭제한 파일이 존재하지 않아
List<Long> list = null; 이고 for each문을 돌릴때 꼭 널 체크를 해주자.
안해주면 NullPointerException이 터진다...
구현 결과
'프로그래밍 > spring' 카테고리의 다른 글
HTTP 요청 메시지 바디에 TEXT, JSON 받기 (@RequestBody) (0) | 2023.08.12 |
---|---|
@ModelAttribute ? (0) | 2023.08.12 |
@RequestParam + required / defaultValue / Map (0) | 2023.08.10 |
[스프링 부트] 첨부파일(파일 업로드) 목록 수정 - 21 (0) | 2023.08.09 |
Spring MVC 사용 방식 (0) | 2023.08.08 |