🔍 1. Multipart란?
- Multipart는 웹 요청에서 여러 부분(part)을 포함하는 데이터 형식으로 하나의 요청에 여러 개의 데이터를 포함시킬 수 있는 방식입니다. (텍스트 데이터 (text/plain, application/json) + 파일 데이터 (image/png, application/pdf 등)
- 보통 파일 업로드와 같이 여러 가지 정보를 함께 전송할 때 사용됩니다.
- HTTP에서 multipart/form-data는 파일 업로드를 지원하는 특수한 content-type입니다.
이 방식은 폼데이터와 파일을 한 번에 서버로 보내는 데 사용됩니다.
이 방식으로 텍스트 데이터(json)와 파일 데이터를 각각 분리해서 처리할 수 있도록 해주는 것입니다!
🔍 2. MultipartFile은?
- MultipartFile은 Spring에서 제공하는 인터페이스로, 클라이언트로부터 전송된 파일을 다루기 위해 사용됩니다.
- MultipartFile 객체는 파일의 내용, 이름, 크기, MIME 타입 등을 다룰 수 있는 메서드를 제공해줍니다.
- Spring MVC에서 @RequestParam 또는 @RequestPart와 함께 사용되어 클라이언트에서 업로드한 파일을 서버에서 받을 수 있게 됩니다.
getName(): 업로드된 파일의 이름을 반환
getOriginalFilename(): 원본 파일명을 반환
getSize(): 파일의 크기를 반환 (byte 단위)
getContentType(): 파일의 MIME 타입(예: image/png)을 반환
getBytes(): 파일 내용을 바이트 배열로 반환
getInputStream(): 파일을 읽을 수 있는 InputStream을 반환
🎯 3. MultipartFile로 게시판에 첨부파일 업로드 해보기
- File 엔티티- 첨부파일 관리를 위한 엔티티와 DTO 설계(추후에 다중 첨부 파일 관리를 편하게 하려고)
- Post service 수정 - 작성 시 첨부파일 첨부 기능 추가
- Post 작성 controller 수정
(1) PostFile Entity
PostFile 엔티티는 파일의 메타데이터를 저장합니다.
데이터베이스에서 파일 정보를 저장하는 엔티티로, 파일의 이름, 경로를 저장하도록 합니다.
(게시글의 Id는 다중 파일 첨부를 위해 외래키로 가져오도록 한 것입니다!!)
@Entity(name = "filepost")
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PostFile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="post_id")
private Post post;
private String filename;
private String filepath;
}
(2) Post 작성 서비스 수정
- 파일 업로드 처리: 파일을 업로드한 후, PostFile 엔티티로 저장하는 과정
- 파일 삭제 처리: 파일이 삭제될 때, 관련된 데이터베이스의 파일 정보를 삭제하고, 서버에서 실제 파일도 제거하는 로직
(파일 삭제는 추후 구현)
@Transactional
public Long write(String nickname, PostDto.Request postDto, MultipartFile file) {
User user = userRepository.findByNickname(nickname);
postDto.setUser(user);
log.info("write post 실행");
Post post = postDto.toEntity();
postRepository.save(post);
if(file!=null && !file.isEmpty()) {
try {
//파일 저장 경로
String uploadDir = System.getProperty("user.dir") + "/src/main/resources/static/files/";
String filePath = uploadDir + file.getOriginalFilename();
File dest = new File(filePath);
file.transferTo(dest);
PostFile postFile = PostFile.builder()
.post(post)
.filename(file.getOriginalFilename())
.filepath(filePath)
.build();
postFileRepository.save(postFile);
}catch (IOException e) {
log.error("파일 저장 오류 발생", e);
throw new RuntimeException("파일 저장 실패");
}
}
return post.getId();
}
사실 파일 저장 경로를 저렇게 하드코딩하지 않는 것이 좋겠지만, 흐름을 파악하기 위해 따로 남겨두었습니다.
if문부터 참고하면, Post 작성 시 첨부파일이 첨부되지 않아도 Post 저장은 되어야 하므로
실제 파일이 업로드된 경우에만 파일 저장을 진행하도록 합니다.
File dest = new File(filePath);
file.transferTo(dest);
위 코드는 파일을 저장할 경로와 파일명을 지정한 File 객체를 생성하고 업로드된 파일을 지정한 경로로 저장하는 부분입니다.
❗그러나, file.getOriginalFilename()을 그대로 사용하면 같은 이름의 파일이 업로드될 경우 덮어쓰기 문제가 발생할 수 있습니다. -> UUID를 추가하여 파일명을 변경하는 것이 더 안전합니다.
PostFile postFile = PostFile.builder()
.post(post)
.filename(file.getOriginalFilename())
.filepath(filePath)
.build();
postFileRepository.save(postFile);
파일 정보를 저장할 PostFile 엔티티를 생성하여 파일 정보를 DB에 저장합니다.
(4) Post 작성 Controller 수정
> 이전 RestController
@PostMapping("/api/post/writePro")
public ResponseEntity<Long> write(
@RequestBody @Valid PostDto.Request postDto,
@LoginUser UserDto.Response userDto) {
Long postId = postService.write(userDto.getNickname(), postDto);
return ResponseEntity.ok(postId);
}
이전에는 Post 작성 폼에서 받은 입력 데이터를 json으로 변환하기 위해 @RequestBody로 PostDto.Request를 받도록 설계했지만,
MultipartFile 데이터를 받기 위해서는 @RequestBody가 아닌 multipart/form-data로 받아야 합니다.
> MultipartFile을 받도록 수정한 RestController
@PostMapping(value = "/api/post/writePro", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Long> write(
@RequestPart("post") @Valid PostDto.Request postDto,
@RequestPart(value = "file", required = false) MultipartFile file,
@LoginUser UserDto.Response userDto) {
Long postId = postService.write(userDto.getNickname(), postDto, file);
return ResponseEntity.ok(postId);
}
@RequestPart : multipart/form-data 요청에서 JSON 데이터(post 필드)와 파일(file 필드)을 함께 받을 수 있습니다.
(파일이 없을 수도 있으므로 required = false 설정하여 파일 첨부 없이도 글 작성 가능하게 만듦)
@RequestPart 알아보기
📌1. @RequestPart란?@RequestPart는 멀티파트 요청 (multipart request) 에서 특정 파일이나 JSON 데이터를 개별적으로 매핑할 때 사용되는 Spring MVC의 어노테이션입니다.2. @RequestParam vs @RequestBody vs @RequestPart
gaeran.tistory.com
📝 프론트엔드 요청 예시
POST /api/post/writePro
Content-Type: multipart/form-data
------WebKitFormBoundary
Content-Disposition: form-data; name="post"
Content-Type: application/json
{
"title": "게시글 제목",
"content": "게시글 내용",
"category": "공지",
"writer": "사용자1"
}
------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="example.jpg"
Content-Type: image/jpeg
(바이너리 파일 데이터)
------WebKitFormBoundary--
'Spring > 웹 게시판' 카테고리의 다른 글
[Web 게시판] 게시글 관리 설계 | 게시글 조회수 카운팅 (0) | 2025.01.30 |
---|---|
[Web 게시판] 회원 관리 설계 | User 엔티티 설계와 비즈니스 로직 (0) | 2025.01.18 |
[Web 게시판] DB 설계 및 Entity 구성 (2) | 2025.01.02 |