1. 화면(뷰) – 업로드 폼 & 결과 페이지

업로드 폼 main.html

<h3>single file upload</h3>
<form action="single-file" method="post" **enctype="multipart/form-data"**>
  파일: <input type="file" name="singleFile"><br>
  파일 설명: <input type="text" name="singleFileDescription"><br>
  <input type="submit" value="업로드">
</form>

<h3>multi file upload</h3>
<form action="multi-file" method="post" **enctype="multipart/form-data"**>
  파일: <input type="file" name="multiFiles" **multiple**><br>
  파일 설명: <input type="text" name="multiFileDescription"><br>
  <input type="submit" value="업로드">
</form>

결과 페이지 result.html

<h1 th:text="${message}"></h1>

<div th:if="${img != null}">
  <img th:src="${img}" width="200" height="200"/>
  <p th:text="${singleFileDescription}"></p>
</div>

<div th:if="${imgs != null}">
  <th:block th:each="img: ${imgs}">
    <img th:src="${img}" width="150" height="150">
  </th:block>
  <p th:text="${multiFileDescription}"></p>
</div>

2. 설정 application.yml

server:
  port: 8081
  tomcat:
    max-connections: 200

filepath: /Users/.../uploadFiles

spring:
  servlet:
    multipart:
      enabled: true
      max-file-size: 100MB
      max-request-size: 100MB
  web:
    resources:
      static-locations: file:///Users/.../uploadFiles/
      cache-period: 3600

3. 컨트롤러

@Controller
public class FileUploadController {

    @Value("${filepath}")
    private String filepath; // yml에서 주입 (업로드 루트 경로)

    @PostMapping("single-file")
    public String singleFile(@RequestParam MultipartFile singleFile,
                             @RequestParam String singleFileDescription,
                             RedirectAttributes rttr) {
        // 1) 원본 이름/확장자 추출
        String originFileName = singleFile.getOriginalFilename();
        String ext = originFileName.substring(originFileName.lastIndexOf("."));

        // 2) 서버 저장용 파일명 리네임(중복 방지)
        String saveName = UUID.randomUUID().toString().replace("-", "") + ext;

        // 3) 실제 저장 (로컬 디스크)
        singleFile.transferTo(new File(filepath + "/img/single/" + saveName));

        // 4) 결과 페이지에 보여줄 데이터(Flash Attribute)
        rttr.addFlashAttribute("message", originFileName + " 파일 업로드 성공!");
        rttr.addFlashAttribute("img", "/img/single/" + saveName);
        rttr.addFlashAttribute("singleFileDescription", singleFileDescription);

        return "redirect:/result";
    }

    @GetMapping("result")
    public void result(){}

    @PostMapping("multi-file")
    public String multiFileUpload(@RequestParam List<MultipartFile> multiFiles,
                                  @RequestParam String multiFileDescription,
                                  RedirectAttributes rttr) {
        List<Map<String,String>> files = new ArrayList<>();
        List<String> imgSrcs = new ArrayList<>();

        try {
            for (MultipartFile mf : multiFiles) {
                String originFileName = mf.getOriginalFilename();
                String ext = originFileName.substring(
                originFileName.lastIndexOf("."));
                String saveName = UUID.randomUUID().toString()
                .replace("-", "") + ext;

                // 저장
                mf.transferTo(new File(filepath + "/img/multi/" + saveName));

                // DB 전송용 구조(예시로만 저장)
                Map<String, String> file = new HashMap<>();
                file.put("originFileName", originFileName);
                file.put("saveName", saveName);
                file.put("filePath", "/img/multi/");
                file.put("multiFileDescription", multiFileDescription);
                files.add(file);

                // 화면 표시용 경로
                imgSrcs.add("/img/multi/" + saveName);
            }

            rttr.addFlashAttribute("message", "다중 파일 업로드 성공!");
            rttr.addFlashAttribute("imgs", imgSrcs);
            rttr.addFlashAttribute("multiFileDescription", multiFileDescription);

        } catch (IOException e) {
            // 일부 저장 성공 후 실패 대비 → 이미 저장된 파일 롤백 삭제
            for (Map<String, String> file : files) {
                new File(filepath + "/img/multi/" + file.get("saveName"))
                .delete();
            }
            rttr.addFlashAttribute("message", "다중 파일 업로드 실패!");
        }
        return "redirect:/result";
    }
}