물론이죠! 아래는 **스프링 부트(Spring Boot)**에서의 **파일 업로드**, **멀티 파일 업로드**, **파일 다운로드**를 다루는 실무 중심의 설명 글입니다. --- # ✅ 스프링 부트에서 파일 업로드 및 다운로드 구현하기 현대 웹 애플리케이션에서는 사용자가 이미지를 업로드하거나 문서를 다운로드하는 기능이 필수입니다. 스프링 부트는 이러한 기능을 손쉽게 구현할 수 있도록 다양한 API를 제공합니다. 이 글에서는 다음을 다룹니다: - 단일 파일 업로드 - 다중(멀티) 파일 업로드 - 파일 다운로드 - 실무에서 주의할 점 --- ## 📁 1. 의존성 설정 (`build.gradle` 또는 `pom.xml`) 스프링 부트에서는 기본적으로 `spring-boot-starter-web`만으로도 파일 업로드/다운로드가 가능합니다. **Gradle 예시:** ```groovy dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' } ``` --- ## 📤 2. 단일 파일 업로드 ### ✅ 컨트롤러 예시 ```java @RestController @RequestMapping("/files") public class FileUploadController { private final Path uploadDir = Paths.get("uploads"); public FileUploadController() throws IOException { Files.createDirectories(uploadDir); } @PostMapping("/upload") public ResponseEntity uploadFile(@RequestParam("file") MultipartFile file) throws IOException { if (file.isEmpty()) return ResponseEntity.badRequest().body("파일이 비어 있습니다"); Path targetPath = uploadDir.resolve(file.getOriginalFilename()); Files.copy(file.getInputStream(), targetPath, StandardCopyOption.REPLACE_EXISTING); return ResponseEntity.ok("업로드 성공: " + file.getOriginalFilename()); } } ``` ### 🔍 설명 - `@RequestParam("file")`: HTML ``의 name과 일치해야 합니다. - `MultipartFile`: 업로드된 파일을 표현하는 객체입니다. - `Files.copy(...)`: 서버의 특정 디렉토리에 저장. --- ## 📤 3. 멀티 파일 업로드 ### ✅ 컨트롤러 예시 ```java @PostMapping("/upload-multiple") public ResponseEntity uploadMultiple(@RequestParam("files") List files) throws IOException { if (files.isEmpty()) return ResponseEntity.badRequest().body("업로드된 파일이 없습니다."); for (MultipartFile file : files) { if (!file.isEmpty()) { Path targetPath = uploadDir.resolve(file.getOriginalFilename()); Files.copy(file.getInputStream(), targetPath, StandardCopyOption.REPLACE_EXISTING); } } return ResponseEntity.ok("멀티 파일 업로드 성공"); } ``` ### 🔍 HTML에서 보내는 방법 ```html
``` --- ## 📥 4. 파일 다운로드 ### ✅ 컨트롤러 예시 ```java @GetMapping("/download/{filename}") public ResponseEntity downloadFile(@PathVariable String filename) throws IOException { Path filePath = uploadDir.resolve(filename); if (!Files.exists(filePath)) { return ResponseEntity.notFound().build(); } Resource resource = new UrlResource(filePath.toUri()); return ResponseEntity.ok() .contentType(MediaType.APPLICATION_OCTET_STREAM) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"") .body(resource); } ``` ### 🔍 주요 포인트 - `UrlResource`: 파일을 다운로드 가능한 형태로 변환 - `Content-Disposition`: 파일이 브라우저에 표시되지 않고 다운로드되도록 설정 --- ## ⚠️ 실무에서 주의할 점 ### 1. 파일 이름 중복 처리 - `UUID`를 붙이거나 시간 기반으로 처리해야 파일 덮어쓰기 방지 가능 ```java String safeName = UUID.randomUUID() + "_" + file.getOriginalFilename(); ``` ### 2. 저장 경로 보안 - 사용자 입력을 통한 경로 조작 공격 방지 (`../` 등) - 반드시 경로를 정규화 (`normalize()`)하고 업로드 디렉토리 밖으로 벗어나지 않도록 체크 ```java Path normalized = uploadDir.resolve(file.getOriginalFilename()).normalize(); if (!normalized.startsWith(uploadDir)) { throw new SecurityException("잘못된 경로입니다"); } ``` ### 3. 용량 제한 설정 **application.properties** ```properties spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=20MB ``` --- ## ✅ 마무리 | 기능 | 구현 여부 | |-----------------|-----------| | 단일 파일 업로드 | ✅ | | 멀티 파일 업로드 | ✅ | | 파일 다운로드 | ✅ | 스프링 부트에서는 `MultipartFile`, `Resource`, `ResponseEntity` 등의 기본 컴포넌트를 통해 파일 업로드/다운로드 기능을 쉽게 구현할 수 있습니다. 다만 실무에서는 보안, 파일 크기 제한, 파일명 중복 등의 다양한 상황을 고려해야 합니다. --- 필요하다면 `AWS S3`, `FTP`, 혹은 DB BLOB 저장 방식도 활용 가능하며, 이 글은 **로컬 저장소 기반**의 기본 구현을 중심으로 설명했습니다. 더 확장하고 싶은 부분이 있으면 언제든지 말해주세요! --- 좋습니다! 이번엔 **파일을 데이터베이스에 BLOB(Binary Large Object)** 형태로 저장하고, 다시 꺼내서 다운로드하는 방식을 다룬 글을 정리해드릴게요. --- # 🗃️ 스프링 부트에서 파일을 DB(BLOB)로 업로드 및 다운로드하기 웹 애플리케이션에서 파일을 **데이터베이스(BLOB 컬럼)**에 직접 저장하는 방식은 파일 보안이나 백업, 이식성 측면에서 유리할 수 있습니다. 이 글에서는 다음을 다룹니다: - DB에 파일 업로드 (BLOB 저장) - DB에서 파일 다운로드 - 실제 구현 예시 (Spring Boot + JPA + H2/MySQL) --- ## 📦 1. 테이블 설계 (DDL 예시) ### ✅ MySQL 예시 ```sql CREATE TABLE file_data ( id BIGINT AUTO_INCREMENT PRIMARY KEY, file_name VARCHAR(255), content_type VARCHAR(100), data LONGBLOB ); ``` > `data` 컬럼이 실제 파일의 바이트 데이터를 저장하는 BLOB 필드입니다. --- ## 🧱 2. 엔터티 클래스 정의 ```java import jakarta.persistence.*; @Entity @Table(name = "file_data") public class FileData { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String fileName; private String contentType; @Lob @Column(columnDefinition = "LONGBLOB") private byte[] data; // 생성자, Getter/Setter 생략 } ``` - `@Lob` 어노테이션은 `byte[]` 데이터를 BLOB으로 매핑합니다. - `contentType`은 다운로드할 때 MIME 타입을 알려주기 위해 사용합니다. --- ## 📂 3. 리포지토리 인터페이스 ```java import org.springframework.data.jpa.repository.JpaRepository; public interface FileDataRepository extends JpaRepository { } ``` --- ## 📤 4. 파일 업로드 (DB 저장) ```java @RestController @RequestMapping("/files") public class FileDataController { private final FileDataRepository fileDataRepository; public FileDataController(FileDataRepository repo) { this.fileDataRepository = repo; } @PostMapping("/upload") public ResponseEntity uploadFile(@RequestParam("file") MultipartFile file) throws IOException { if (file.isEmpty()) return ResponseEntity.badRequest().body("빈 파일입니다"); FileData fileData = new FileData(); fileData.setFileName(file.getOriginalFilename()); fileData.setContentType(file.getContentType()); fileData.setData(file.getBytes()); fileDataRepository.save(fileData); return ResponseEntity.ok("파일 업로드 성공: ID = " + fileData.getId()); } } ``` --- ## 📥 5. 파일 다운로드 (DB에서 BLOB 조회) ```java @GetMapping("/download/{id}") public ResponseEntity downloadFile(@PathVariable Long id) { return fileDataRepository.findById(id) .map(file -> ResponseEntity.ok() .contentType(MediaType.parseMediaType(file.getContentType())) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFileName() + "\"") .body(file.getData())) .orElse(ResponseEntity.notFound().build()); } ``` --- ## 🧪 6. Postman 또는 HTML 테스트 예시 ### ✅ HTML 업로드 폼 ```html
``` ### ✅ 다운로드 링크 예시 ```html 파일 다운로드 ``` --- ## ⚠️ 실무에서 주의할 점 ### 1. 성능 문제 - BLOB 방식은 대용량 파일 처리 시 성능 저하가 발생할 수 있음 → **작은 이미지나 문서 위주에 적합** ### 2. DB 백업 크기 증가 - 파일이 많아지면 DB 백업/복원 시간이 길어질 수 있음 ### 3. 파일 크기 제한 설정 ```properties spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=20MB ``` ### 4. 파일 다운로드 속도 - 서버가 파일을 전부 로드해야 하므로, 큰 파일은 **스트리밍 방식 처리**를 고려할 것 --- ## ✅ 정리 | 기능 | 구현 방식 | |------------------|-----------| | 파일 업로드 | `MultipartFile` → BLOB 저장 | | 파일 다운로드 | `byte[]` → HTTP 응답 | | 확장성/보안 | 파일 시스템보다 안전하나 대용량엔 부적합 가능 | --- ## 🔚 마무리 파일을 **데이터베이스 BLOB에 저장하는 방식**은 외부 파일 시스템 없이 관리가 간편하고, 트랜잭션 처리와 보안이 뛰어납니다. 단, 대용량 파일이 많을 경우에는 성능과 저장소 이슈를 고려하여 **S3, 파일 시스템 저장 방식**과 함께 혼합하여 쓰는 것이 좋습니다. --- 필요하다면 **썸네일 저장, 이미지 미리보기 API, 스트리밍 다운로드**까지 확장도 가능합니다. 원하시면 다음 단계도 이어서 도와드릴게요!