이 포스트를 작성하게 된 계기는 개인적으로 진행하고 있는 게시판 프로젝트에서 게시글에 아무런 파일을 첨부하지 않고 전송해도 계속 뭔가가 전송되는 것을 발견했기 때문이다.
위처럼 게시글 작성 시 전송된 파일의 이름과 크기를 로깅하도록 했을 때 크기 0의 빈 파일이 전송되는 것을 볼 수 있다. transferTo 메서드로 실제 파일로 옮겨봐도 역시 0바이트 짜리 파일이 생성될 뿐이었다. 왜 이런 것일까?
MDN 문서를 참조하면 다음과 같은 내용을 볼 수 있다.
- If multiple files are selected, the string represents the first selected file. JavaScript can access the other files through the input's files property.
- If no file is yet selected, the string is "" (empty).
- The string is prefixed with C:\fakepath\, to prevent malicious software from guessing the user's file structure.
즉 생각했던 것과 달리 파일을 선택하지 않고 폼 데이터를 전송해도 해당 태그의 이름으로 빈 문자열이 전송된다는 것이다. 그렇기 때문에 서버측에서는 실제 바이트 데이터는 없는 크기 0의 파일(로 취급되는 문자열)을 수신하기 때문에 위처럼 MultipartFile 객체를 받았다고 생각하게 되는 것이다.
보안을 위해 클라이언트 측 스토리지를 반영하지 않고 그냥 파일 이름과 fakepath란 경로를 합하여 전송한다는 것도 알 수 있었다.
그럼 어떻게 이 빈 파일을 처리하지 않도록 할 수 있을까? 가끔 파일의 크기가 0인지 확인하여 처리하는 경우가 있지만 이미 MultipartFile 클래스에는 이런 경우를 대비하여 isEmpty라는 메서드가 있다.
Return whether the uploaded file is empty, that is, either no file has been chosen in the multipart form or the chosen file has no content.
메서드 설명에서 볼 수 있듯이 파일이 비어있을 경우, 즉 빈 파일이거나 클라이언트 측에서 아무런 파일을 전송하지 않은 경우를 확인할 수 있다. 이를 바탕으로 다음과 같이 코드를 수정할 수 있다.
@Override
public void store(List<MultipartFile> files) {
for (MultipartFile file : files) {
if(!file.isEmpty()) store(file);
}
}
파일을 실제로 업로드하기 전 isEmpty 메서드로 적절한 파일인지 확인한다.
어쨌든 중요한 점은 아무런 파일을 선택하지 않아도 빈 문자열이 전달되기 때문에 파일을 업로드할 때는 항상 isEmpty 메서드로 검증 후 처리하도록 하자.
나와 동일한 고민을 한 사람이 스택오버플로우에도 있었는데 검색 키워드(multipartfile, no file 등)를 못 찾아서 한참을 헤매다가 해결할 수 있었다.
웹의 기초적인 지식이라고 할 수 있는 부분인데 한참동안 헤맸기 때문에 부끄럽다.