[필수용어 실무 7편] 파일 업로드/다운로드 완전 정복 – JSP ㆍServletㆍSpring 실무 패턴 25선

파일 업로드와 다운로드

파일 업로드와 다운로드 기능모든 웹 서비스에 반드시 필요한 핵심 기능입니다.
게시판, 회원 프로필, 보고서 제출, 이미지 관리 등 거의 모든 시스템이 이 기능을 사용합니다.

하지만 초보자에게 파일 업로드 기능은 난이도가 높습니다. 저장 경로, 용량 제한, 보안 정책, MIME 타입, 파일명 충돌 처리 등 신경 쓸 게 많기 때문입니다. 저 또한 엄청 고생했던 기억이 있습니다. 하나 해결하면 또 다른 하나가 문제가 되는 그런 상황들 말이죠.

이 글은 입문자도 바로 이해할 수 있도록 JSP·Servlet → Spring MVC → 실무 패턴 → 보안까지 전체 흐름을 한 번에 정리한 완성 가이드입니다. 많은 분들께 도움이 되길 기대합니다.

오늘은 파일 업로드 기능을 25개의 필수 체크리스트로 완전히 끝내 보겠습니다.

업로드 다운로드

1. 단일 파일 업로드(기본)

핵심 개념: form 태그의 enctype="multipart/form-data" 필수

<form action="/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="file"/>
    <button>업로드</button>
</form>

Tip) enctype 빠지면 절대 안 됩니다.(입문자 최다 실수 1위).


2. Servlet 기반 처리 (Commons FileUpload)

List<FileItem> items = new ServletFileUpload(factory).parseRequest(request);
for(FileItem item : items){
    if(!item.isFormField()){
        item.write(new File("C:/upload/" + item.getName()));
    }
}

Tip) JSP 단독으로 하지 말고 반드시 Servlet으로 분리해 줍니다.


3. Spring MultipartFile (실무 표준)

@PostMapping("/upload")
public String upload(MultipartFile file) throws Exception {
    file.transferTo(new File("C:/upload/" + file.getOriginalFilename()));
    return "ok";
}

Tip) Spring은 MultipartResolver 자동 설정 덕분에 가장 사용률 높음.


4. 다중 파일 업로드 (List<MultipartFile>)

@PostMapping("/multi")
public void upload(List&lt;MultipartFile> files) throws Exception {
    for(MultipartFile f : files){
        f.transferTo(new File("C:/upload/" + f.getOriginalFilename()));
    }
}

Tip) 다중 업로드는 반드시 input 태그에 multiple 속성 필요.


5. 용량 제한 설정

spring:
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 30MB

Tip) max-request-size는 파일 여러 개 업로드 시 전체 용량 제한.


6. 경로 관리(절대경로 vs 상대경로)

방식장점단점
절대경로(C:/upload)설정 쉬움서버 이동 시 수정 필요
상대경로(/resources/upload)이식성 좋음초기 설정 필요

Tip) Docker·배포 고려하면 절대경로 사용 X.


7. 확장자 검사 (보안 필수)

String ext = FilenameUtils.getExtension(file.getOriginalFilename());
if(!List.of("png","jpg","pdf").contains(ext)) throw new RuntimeException("허용X");

Tip) .exe .bat .js .sh 반드시 차단.


8. MIME 타입 검사

if(!file.getContentType().startsWith("image")) throw new Exception("이미지만 가능");

Tip) contentType 위변조 가능 → 확장자 + MIME 두 개 모두 검사.


9. UUID 기반 저장명 생성(충돌 방지)

String saveName = UUID.randomUUID() + "." + ext;

Tip) 저장 파일명은 UUID, DB에는 원본명/저장명 둘 다 저장.


10. 날짜별 폴더 생성(대규모 시스템 표준)

String folder = LocalDate.now().toString().replace("-", "/");  // 2025/02/07
Files.createDirectories(Path.of(uploadPath, folder));

Tip) S3·NAS 쓰면 날짜 디렉토리 구조 필수.


11. 파일 메타데이터 DB 저장

DB에 저장하는 정보

컬럼설명
original_name원본 파일명
save_nameUUID 저장명
size파일 크기
mime파일 타입
path실제 저장 경로
reg_date업로드일

Tip) 파일 다운로드는 DB save_name 기반으로 제공.


12. 게시판 파일 첨부 패턴

  1. 글 등록 → 파일 업로드
  2. DB에 게시글 ID + 파일 정보 연결
  3. 글 조회 시 files(1:N) 조회
  4. 다운로드 시 파일 단위 제공

Tip) 입문자가 가장 많이 하는 실수는 “글 수정 → 파일 덮어쓰기” 로직을 잘못 구성하는 부분입니다


13. 파일 다운로드(기본)

Path path = Paths.get(uploadPath + filename);
Resource res = new InputStreamResource(Files.newInputStream(path));
return ResponseEntity.ok()
      .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + originalName)
      .body(res);

Tip) 다운로드 파일명은 반드시 originalName 사용.


14. 파일명 한글 깨짐 문제 해결

String encoded = URLEncoder.encode(originalName, "UTF-8").replaceAll("\\+", "%20");

Tip) Window <-> Linux 간 이동할 때 자주 발생.


15. XSS 대비 파일명 필터링

공격 예시

image.png&lt;script>alert(1)&lt;/script>

대응

originalName = originalName.replaceAll("[&lt;>]", "");

16. 이미지 썸네일 자동 생성

Thumbnailator 사용 예시

Thumbnails.of(file).size(200, 200).toFile(thumbnailPath);

Tip) 관리자 페이지에 썸네일 리스트 빠르게 불러올 때 필수.


17. AWS S3 업로드

PutObjectRequest request = PutObjectRequest.builder()
        .bucket("my-bucket")
        .key("upload/" + saveName)
        .build();
s3.putObject(request, RequestBody.fromBytes(file.getBytes()));

Tip) 실제 기업에서 가장 많이 쓰는 저장 방식.


18. NAS 연동

NAS(사내 파일 서버) 경로

/mnt/shared/upload/

Tip) 금융권·공공기관에서 가장 흔함.


19. 파일 삭제(단건)

Files.deleteIfExists(Path.of(path, saveName));

Tip) DB 레코드 삭제 + 실제 파일 삭제 둘 다 해야 함.


20. 파일 일괄 삭제

게시글 삭제 시 첨부파일 전체 삭제 필요

for(FileDTO f : files){
    Files.deleteIfExists(Path.of(f.getPath(), f.getSaveName()));
}

21. 실패 시 롤백 처리

  1. 파일 저장
  2. DB 저장
  3. 중간 실패 시 저장된 파일 삭제
try {
   file.transferTo(...)
   dao.insert(...)
} catch(Exception e){
   Files.delete(...)
}

22. Drag & Drop 업로드

프론트 예제

dropZone.addEventListener("drop", e=>{
    e.preventDefault();
    const files = e.dataTransfer.files;
});

Tip) 에디터 이미지 업로드에 가장 흔하게 사용.


23. AJAX 업로드 (FormData)

const fd = new FormData();
fd.append("file", file);

fetch("/upload", { method: "POST", body: fd });

Tip) 페이지 리로드 없이 업로드 가능 → 최근 UI 표준.


24. 다운로드 권한 체크

예) 로그인한 사용자만 다운로드

if(user == null) throw new UnauthorizedException();

Tip) 중요한 문서 다운로드에는 필수.


25. 실행가능 파일(.exe) 업로드 차단

웹쉘 공격 예방

String ext = ext.toLowerCase();
if(ext.equals("exe") || ext.equals("sh") || ext.equals("js")) throw new Exception();

Tip) 가장 중요한 보안 정책.


✔ 공식 문서(가장 신뢰도 높음)

  1. Servlet 3.0 File Upload 공식 스펙 (Oracle)
    https://docs.oracle.com/javaee/6/tutorial/doc/glraq.html
  2. Apache Commons FileUpload 공식 문서
    https://commons.apache.org/proper/commons-fileupload/using.html
  3. AWS S3 Upload 공식 개발자 가이드
    https://docs.aws.amazon.com/AmazonS3/latest/userguide/upload-objects.html
  4. Thumbnailator – GitHub 공식 문서
    https://github.com/coobird/thumbnailator

✔ 실무·보안 자료(기업·기관 레퍼런스)

  1. OWASP File Upload Security Guide
    업로드 취약점, MIME 위조, 파일 확장자 검사 등 업계 표준 보안 가이드
    https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload
  2. Naver D2 – 이미지 업로드/처리 기술 블로그
    https://d2.naver.com/search?keyword=upload
  3. Google Web Fundamentals – 사용자 파일 처리
    https://developers.google.com/web/fundamentals/media/capturing-images

✔ 추가 참고 자료(확장 지식)

  1. MDN – FormData / 파일 업로드 & AJAX
    https://developer.mozilla.org/en-US/docs/Web/API/FormData
  2. StackOverflow – 파일 업로드 실무 패턴 Q&A
    https://stackoverflow.com/questions/tagged/file-upload

마무리하며

이 25가지는 실제 회사에서 100% 사용되는 실무 구성입니다.
이걸 기반으로 게시판, 에디터, 관리자 도구 등 모든 시스템의 파일 업로드 기능을 만들 수 있습니다.

다음 편 예고

다음 글에서는 웹 개발에서 절대 피해갈 수 없는 인증(Authentication)과 보안(Security) 파트를 다룹니다. 입문자들이 가장 많이 막히는 구간이기도 하고, 실무에서는 한 줄의 설정 실수로도 보안 사고가 발생할 수 있어 반드시 정확히 이해해야 하는 주제입니다.

특히 다음 편은 단순 용어 설명을 넘어,
✔ 로그인 흐름이 어떻게 작동하는지
✔ 세션(Session)과 쿠키(Cookie)가 정확히 무엇을 하는지
✔ JWT 토큰이 왜 등장했고 어떻게 구성되는지
✔ CSRF, XSS 같은 보안 공격을 실제로 어떤 방식으로 막는지

등을 실제 업무 기준으로 하나씩 풀어낼 예정입니다.

[필수용어 실무] 이전 편을 보지 못했다면? 필수용어 실무 시리즈 바로가기

[필수용어 실무 1편] 비전공자가 실무에서 바로 활용 가능한 데이터 연동 & 서버 흐름 25선
[필수용어 실무 2편] 실무에서 마주치는 예외 처리 & 트러블슈팅 핵심 25선
[필수용어 실무 3편] JSP & MVC 패턴 실무 흐름 완전정복 25선
[필수용어 실무 4편] JSP 데이터 처리 & EL/JSTL 실무활용 25선
[필수용어 실무 5편] JSP + AJAX 비동기 데이터 통신 완전정복 25선
[필수용어 실무 6편] JSP + REST API 연동 실전 25선

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤