웹 개발을 하다 보면 파일을 업로드해야 하는 경우가 많습니다.
프로필 이미지, 첨부 파일, 문서 관리 등 파일 업로드 기능은 정말 자주 사용되죠.
이번 글에서는 Spring Boot에서 파일을 업로드하고, 로컬 저장소에 저장하는 방법을 설명해보겠습니다.
1. 파일 업로드 기능을 위한 환경 설정
Spring Boot에서는 기본적으로 multipart/form-data 형식의 파일 업로드를 지원합니다.
파일을 저장할 디렉토리를 application.properties에서 지정해줍니다.
application.properties 설정
# applicaiton.properties
# 파일 업로드 디렉토리 설정
upload.local.dir=<저장할 경로> ex) C:/Users/test/Documents/uploads
이 설정을 통해 업로드된 파일이 저장될 위치를 지정할 수 있습니다.
2. MultipartFile이란
Spring Boot에서 HTTP 요청을 통해 파일을 업로드할 때 사용하는 인터페이스입니다.
즉, 클라이언트가 파일을 업로드하면 MultipartFile 객체로 받아서 처리할 수 있습니다.
MultipartFile을 사용하면 다음과 같은 기능을 수행할 수 있습니다:
✔ 업로드된 파일의 이름 가져오기 (getOriginalFilename())
✔ 파일 크기 확인 (getSize())
✔ 파일의 MIME 타입 확인 (getContentType())
✔ 바이트 배열로 변환 (getBytes())
✔ 특정 경로에 저장 (transferTo())
3. 파일 업로드 기능 구현하기
이제 파일을 실제로 저장할 기능을 구현해보겠습니다.
파일 업로드 기능을 구현할 때는 서비스(Service) 계층과 컨트롤러(Controller) 계층을 나누어 관리하는 것이 좋습니다.
- 서비스 계층(LocalStorageService): 파일을 저장하는 로직을 처리
- 컨트롤러 계층(FileUploadController): 클라이언트에서 API 요청을 받을 엔드포인트 제공
LocalStorageService.java (파일 저장 로직)
@Slf4j
@Service
public class LocalStorageService {
// 로컬 업로드 경로
@Value("${upload.local.dir}")
private String uploadDir;
// 저장된 파일의 전체 경로 반환
public String getFullPath(String fileName) {
return uploadDir + File.separator + fileName;
}
// 단일 파일 저장 후 FileUpload DTO 반환
public FileUpload storeFile(MultipartFile file) {
String originalName = file.getOriginalFilename();
String storedName = generateStoredFileName(originalName);
String fullPath = getFullPath(storedName);
File dest = new File(fullPath);
if (!dest.getParentFile().exists()) {
boolean created = dest.getParentFile().mkdirs();
if (!created) {
log.error("Failed to create directory: {}", dest.getParentFile().getAbsolutePath());
throw new RuntimeException("Directory creation failed");
}
}
try {
file.transferTo(dest);
} catch (IOException e) {
throw new RuntimeException(e);
}
log.info("File stored: {}", fullPath);
return new FileUpload(originalName, storedName);
}
// 여러 파일 저장 후 결과 리스트 반환 (storeFile 재사용)
public List<FileUpload> storeFiles(MultipartFile[] files) {
List<FileUpload> responses = new ArrayList<>();
for (MultipartFile file : files) {
try {
FileUpload uploadResult = storeFile(file);
responses.add(uploadResult);
} catch (Exception e) {
log.error("Failed to store file {}: {}", file.getOriginalFilename(), e.getMessage());
}
}
return responses;
}
// UUID를 이용해 고유 파일명 생성 (UUID_원본파일명)
private String generateStoredFileName(String originalFileName) {
return UUID.randomUUID().toString() + "_" + originalFileName;
}
}
이제 파일을 저장하는 기능이 완성되었습니다.
FileUploadController.java (파일 업로드 API 제공)
이제 실제로 파일을 업로드할 수 있는 API 엔드포인트를 만들어 보겠습니다.
이 컨트롤러는 클라이언트가 파일을 업로드할 때 호출하는 API를 제공합니다.
@RestController
@RequestMapping("/api/files")
@RequiredArgsConstructor
@Slf4j
public class FIleUploadController {
private final LocalStorageService localStorageService;
// 단일 파일 업로드
@PostMapping("/upload")
public ResponseEntity<FileUpload> uploadFile(@RequestParam("file") MultipartFile file) {
try {
FileUpload fileUpload = localStorageService.storeFile(file);
return ResponseEntity.ok(fileUpload);
} catch (Exception e) {
log.error("File upload failed: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
// 여러 파일 업로드
@PostMapping("/upload/multiple")
public ResponseEntity<List<FileUpload>> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) {
List<FileUpload> responses = localStorageService.storeFiles(files);
return ResponseEntity.ok(responses);
}
// 이미지 보기
@GetMapping("/images/{filename}")
public Resource viewImage(@PathVariable String filename) throws MalformedURLException {
return new UrlResource("file:" + localStorageService.getFullPath(filename));
}
// 파일 다운로드
@GetMapping("/download/{filename}")
public ResponseEntity<Resource> downloadFile(@PathVariable String filename) throws MalformedURLException {
String fullPath = localStorageService.getFullPath(filename);
UrlResource resource = new UrlResource("file:" + fullPath);
String encodedFileName = UriUtils.encode(filename, StandardCharsets.UTF_8);
String contentDisposition = "attachment; filename=\"" + encodedFileName + "\"";
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition)
.body(resource);
}
}
FileUpload.java (파일 업로드 결과를 담는 DTO)
이제 파일 업로드 결과를 담을 DTO(Data Transfer Object)를 만들어 줍니다.
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FileUpload {
// 원본 파일명
private String originalFileName;
// 저장된 파일명 (UUID 포함)
private String storedFileName;
}
4. 파일 업로드 테스트하기
이제 Postman 또는 프론트엔드를 통해 업로드 API를 테스트해볼 수 있습니다.
단일 파일 업로드

업로드 경로 확인
여러 파일 업로드
업로드 경로 확인
5. 이미지 보기, 다운로드 테스트
이미지 보기
파일 다운로드
6. 마무리하며
이제 Spring Boot에서 파일을 업로드하고 로컬에 저장하는 기능을 구현할 수 있습니다!
이 기능은 간단한 이미지 저장, 문서 관리, 파일 처리 등 여러 곳에서 활용될 수 있습니다.
+아래 글에서 vue로 파일 업로드, 다운로드하는 방법을 확인할 수 있습니다.
2025.02.17 - [Vue]이미지 업로드 & 다운로드 구현하기
[Vue]이미지 업로드 & 다운로드 구현하기
Vue 3를 활용하여 이미지 파일을 업로드하고 다운로드하는 기능을 구현하는 방법을 소개합니다.Spring Boot를 사용한 서버 구축아래 포스트에서 확인하시면 됩니다.2025.02.14 - [Spring]파일 업로드 하
danghunri.tistory.com