FullStack

Spring Boot와 JavaScript로 파일 및 메타데이터 함께 업로드하는 방법

개발자-제이 2025. 2. 27. 14:46

 

1. 프론트엔드 - 파일 및 메타데이터 업로드

프론트엔드에서 파일과 추가 정보(메타데이터)를 함께 업로드하려면 FormData를 활용해야 한다.

HTML + JavaScript 코드 (Vanilla JS + Axios)

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>파일 및 데이터 업로드</title>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
    <h2>파일 및 메타데이터 업로드 테스트</h2>

    <input type="file" id="fileInput" /><br><br>
    <input type="text" id="fileName" placeholder="파일 이름 입력" /><br><br>
    <input type="text" id="fileDescription" placeholder="파일 설명 입력" /><br><br>

    <button onclick="uploadFile()">파일 업로드</button>

    <p id="result"></p>

    <script>
        function uploadFile() {
            const fileInput = document.getElementById("fileInput");
            const file = fileInput.files[0];

            if (!file) {
                alert("파일을 선택하세요.");
                return;
            }

            const fileName = document.getElementById("fileName").value;
            const fileDescription = document.getElementById("fileDescription").value;

            const formData = new FormData();
            formData.append("file", file);
            formData.append("fileName", fileName);
            formData.append("fileDescription", fileDescription);

            axios.post("http://localhost:8080/rest/v1/file/upload", formData, {
                headers: { "Content-Type": "multipart/form-data" }
            })
            .then(response => {
                document.getElementById("result").innerText = "업로드 성공: " + response.data;
            })
            .catch(error => {
                document.getElementById("result").innerText = "업로드 실패: " + error.response.data;
            });
        }
    </script>
</body>
</html>

 

2. Spring Boot 파일 업로드 컨트롤러

Spring Boot에서 파일과 추가 데이터를 받는 방법은 크게 두 가지가 있다.

 

3. @ModelAttribute를 사용한 DTO 방식

이 방법은 DTO를 활용해 한 번에 데이터를 받을 수 있는 방식이다.

FileDto.java (DTO 객체)

import lombok.Data;
import org.springframework.web.multipart.MultipartFile;

@Data
public class FileDto {
    private MultipartFile file;
    private String fileName;
    private String fileDescription;
}

FileController.java

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.io.IOException;

@Slf4j
@RestController
@RequestMapping("/rest/v1/file")
public class FileController {

    private static final String UPLOAD_DIR = "C:/uploads/";

    @PostMapping("/upload")
    public ResponseEntity<String> uploadFile(@ModelAttribute FileDto fileDto) {
        if (fileDto.getFile().isEmpty()) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("파일이 없습니다.");
        }

        try {
            String filePath = UPLOAD_DIR + fileDto.getFile().getOriginalFilename();
            fileDto.getFile().transferTo(new File(filePath));

            log.info("파일 업로드 성공: {}", filePath);
            log.info("파일 이름: {}", fileDto.getFileName());
            log.info("파일 설명: {}", fileDto.getFileDescription());

            return ResponseEntity.ok("파일 업로드 성공: " + fileDto.getFile().getOriginalFilename());
        } catch (IOException e) {
            log.error("파일 저장 실패", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("파일 업로드 실패");
        }
    }
}

 

4. @RequestParam을 사용한 방식

이 방법은 파일과 데이터를 개별적으로 받는 방식이다.

@PostMapping("/upload")
public ResponseEntity<String> uploadFile(
        @RequestParam("file") MultipartFile file,
        @RequestParam("fileName") String fileName,
        @RequestParam("fileDescription") String fileDescription) {

    if (file.isEmpty()) {
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("파일이 없습니다.");
    }

    try {
        String filePath = "C:/uploads/" + file.getOriginalFilename();
        file.transferTo(new File(filePath));

        log.info("파일 업로드 성공: {}", filePath);
        log.info("파일 이름: {}", fileName);
        log.info("파일 설명: {}", fileDescription);

        return ResponseEntity.ok("파일 업로드 성공: " + file.getOriginalFilename());
    } catch (IOException e) {
        log.error("파일 저장 실패", e);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("파일 업로드 실패");
    }
}

 

5. @ModelAttribute vs @RequestParam 비교

방식 장점 단점
@RequestParam 간단한 구현이 가능 필드가 많아지면 관리가 어려움
@ModelAttribute DTO를 활용해 깔끔한 코드 유지 가능 multipart/form-data 형식이 아닐 경우 오류 발생

 

6. 정리

  • Vue에서 FormData를 사용해 파일과 추가 정보를 함께 전송
  • Spring Boot에서는 @RequestParam 또는 @ModelAttribute를 사용해 처리 가능
  • DTO를 활용하면 코드 유지보수가 쉬워짐
반응형