S3를 사용하여 이미지를 업로드하는 코드는 이전 게시물에 올려뒀습니다! 먼저 아래 글을 읽고 오는 것을 추천합니다.
2024.06.13 - [서버 공부] - [스프링] S3를 이용한 이미지 업로드
[스프링] S3를 이용한 이미지 업로드
사진 업로드를 할 수 있는 외주 작업을 맡게 되어 S3를 이용한 이미지 업로드 로직을 작성했습니다.더보기요구사항 - 게시물에 이미지는 필수값이 아니다.- 하나의 게시물에 올릴 수 있는 파일
hy5sun.tistory.com
보통 한 페이지에 하나의 API를 요청하는 게 좋지만, 그럴 경우 form-data로 List<MultipartFile>과 JSON 데이터를 한 번에 받아야 합니다.
플러터 같은 경우, form-data에 두 가지 타입의 데이터를 받는 게 어렵다고 하여 이와 같이 코드를 작성했습니다!
1. S3Controller 코드 작성 : 이미지 올리기 API 생성
@RestController
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/images")
public class S3Controller {
private final S3Service s3Service;
@PostMapping()
@ResponseStatus(HttpStatus.CREATED)
public CustomResponse uploadImages(@RequestPart(value = "files", required = false) List<MultipartFile> files, @Login Member member) {
FileDto image = s3Service.uploadFiles(files);
return CustomResponse.response(HttpStatus.CREATED, "이미지를 정상적으로 올렸습니다.", image);
}
}
파일은 @RequestPart로 받아왔습니다. @RequestPart는 HTTP Request Body에 multipart/form-data가 포함되어 있는 경우 사용합니다. @RequestPart는 JSON이나 XML과 같은 데이터를 받을 수 있다는 특징이 있습니다.
@RequestParam을 사용해도 괜찮습니다! 다만 @RequestParam은 multipartFile이 아닌 경우 등록된 Converter를 사용하고, @RequestPart는 Content-Type 헤더를 고려한 HttpMessageConverters에 의존한다는 차이점이 있습니다.
이미지 업로드 API에 대한 Response는 다음과 같습니다.
{
"statusCode": 201,
"message": "이미지를 정상적으로 올렸습니다.",
"data": {
"imageUrl": []
}
}
여기서 받은 imageUrl을 글 작성 API 요청할 때 서버로 전달하면 됩니다!
2. Image Entity 생성
@Table(name="image")
@Entity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Image extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
@Column(nullable = false)
private String imageUrl;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "board_id", nullable = false)
@OnDelete(action = OnDeleteAction.CASCADE)
private Board board;
@Builder
public Image(String imageUrl, Board board) {
this.imageUrl = imageUrl;
this.board = board;
}
}
저는 Image Entity를 따로 만들어서 imageUrl로 받은 데이터를 저장하도록 했습니다.
필드 값으로 이미지 url 과 board_id를 생성하여 추후에 게시물을 조회할 때, boardId를 통해 이미지 url 값을 가져오도록 했습니다.
4. CreateBoardRequest & BoardService & BoardController 작성 : 글 작성 API 생성
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class CreateBoardRequest {
@NotBlank(message = "제목을 입력하지 않았습니다.")
private String title;
@NotBlank(message = "내용을 입력하지 않았습니다.")
private String content;
@NotNull(message = "PET HELP 여부를 선택하지 않았습니다.")
private Boolean isPetHelp;
@NotNull(message = "이미지 URL을 입력하지 않았습니다.")
private List<String> imageUrl;
}
@Service
@RequiredArgsConstructor
@Slf4j
public class BoardService {
private final BoardRepository boardRepository;
private final ImageRepository imageRepository;
@Transactional
public DetailBoardResponse createBoard(Member member, CreateBoardRequest req) {
List<Image> images = makeImage(req.getImageUrl());
Board board = Board.builder()
.title(req.getTitle())
.content(req.getContent())
.isPetHelp(req.getIsPetHelp())
.images(images)
.member(member)
.build();
boardRepository.save(board);
// Image 엔티티 속 Board 저장
images.forEach(image -> {
image.setBoard(board);
});
imageRepository.saveAll(images);
return DetailBoardResponse.toDto(board);
}
// image builder
private List<Image> makeImage(List<String> urls) {
return urls.stream()
.map(url -> Image.builder().imageUrl(url).build())
.collect(Collectors.toList());
}
}
@RestController
@RequestMapping("/boards")
@RequiredArgsConstructor
@Slf4j
public class BoardController {
private final BoardService boardService;
@PostMapping()
@ResponseStatus(HttpStatus.CREATED)
public CustomResponse create(@Validated @RequestBody CreateBoardRequest req, @Login Member member) throws IOException {
DetailBoardResponse board = boardService.createBoard(member, req);
return CustomResponse.response(HttpStatus.CREATED, "게시물을 정상적으로 작성했습니다.", board);
}
}
클라이언트는 글을 작성할 때, 이미지 업로드 API 요청 후 글 작성 API를 요청하면 됩니다!
감사합니다.
Reference
'서버 공부' 카테고리의 다른 글
| [스프링] 이미지와 함께 게시물 작성하기 (0) | 2024.06.25 |
|---|---|
| [스프링] S3를 이용한 이미지 업로드 (0) | 2024.06.13 |
| [스프링] org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request 해결 방법 (0) | 2024.06.13 |
| [스프링] @NoArgsConstructor & @AllArgsConstructor & @RequiredArgsConstructor (3) | 2024.06.02 |
| Access Token & Refresh Token (0) | 2024.05.30 |