쌓고 쌓다

[스프링 부트] 게시글 수정시 첨부파일 수정 방법 - 22 본문

프로그래밍/spring

[스프링 부트] 게시글 수정시 첨부파일 수정 방법 - 22

승민아 2023. 8. 11. 14:10

먼저 게시글 수정 클릭시 첨부파일 수정 내용에 관한 처리를 어떻게했는지 이야기를 하자면

  1. 게시글 수정 html에서 해당 게시글의 파일 정보들을 가져온다.
  2. 파일 정보들을 첨부 파일 목록에 출력(load)한다.
  3. 첨부 파일 목록에 기존 파일 출력(load) 옆에 삭제버튼에 클릭 이벤트로 아래의 태그를 생성한다.
    • <input type="hidden" name="deleteFilesId" value="해당 파일 PK">
    • 이 태그를 통해서 서버 POST 요청에 deleteFilesId를 바인딩하여 해당 PK를 삭제하는 과정을 거칠 것이다.
  4. 추가로 첨부 파일에 넣는 파일들을 서버에서 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이 터진다...

 

 

 

구현 결과

Comments