Image를 서버에 업로드하고, DB에 INSERT하는 로직까지 끝이났다.
당연히 어떠한 서비스를 만들었으면 '유효성 검사(Validation)'를 해주어야 한다.
가장 쉬운 프론트엔드 단에서의 '유효성 검사'를 해주자
첫번째로 Image파일을 설명하는 caption부분을 '비울 수 없게' 설정 해주겠다.
upload.jsp
<!--사진설명 + 업로드버튼-->
<div class="upload-form-detail">
<input type="text" placeholder="사진설명" name="caption" required="required"/>
<button class="cta blue">업로드</button>
</div>
<!--사진설명end-->
그리고 두번째로 'Image파일'없이 업로드하는것은 허용하지 않고 싶다
하지만 Image의 타입인 MultipartFile 타입은 @NotBlank같은 Validation을 지원하지 않는다.
따라서, Image upload에 대한 '유효성 검사'는 if문을 사용해서 '수동'으로 만들어 주어야 한다.
ImageController로 가보자
package com.cos.photogramstart.web;
import com.cos.photogramstart.config.PrincipalDetails;
import com.cos.photogramstart.handler.ex.CustomValidationException;
import com.cos.photogramstart.service.ImageService;
import com.cos.photogramstart.web.dto.Image.ImageUploadDto;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@RequiredArgsConstructor
@Controller
public class ImageController {
private final ImageService imageService;
@GetMapping({"/", "/image/story"})
public String story() { //기본페이지
return "image/story";
}
@GetMapping("/image/popular")
public String popular() { //인기페이지
return "image/popular";
}
@GetMapping("/image/upload")
public String upload() { //사진 등록을 위한 페이지
return "image/upload";
}
@PostMapping("/image")
public String imageUpload(ImageUploadDto imageUploadDto,
@AuthenticationPrincipal PrincipalDetails principalDetails) {
//서비스 호출
imageService.사진업로드(imageUploadDto, principalDetails);
if (imageUploadDto.getFile().isEmpty()) { // imageUploadDto에서 File이 없으면
throw new CustomValidationException("이미지가 첨부되지 않았습니다.", null);
}
return "redirect:/user/"+principalDetails.getUser().getId();
}
}
-> 이 상태로 Image없이 업로드를 시도해보자
package com.cos.photogramstart.web.dto;
import com.cos.photogramstart.domain.User;
import com.cos.photogramstart.domain.image.Image;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
@Data
public class ImageUploadDto { //file과 caption을 받을것이다.
private MultipartFile file;
private String caption;
public Image toEntity(String postImageUrl, User user) {
return Image.builder()
.caption(caption)
.postImageUrl(postImageUrl)
.user(user)
.build();
}
}
-> HttpStatus 상태코드 500이 응답되면서, message도 함께 응답되었다.
그런데 CustomValidationException을 걸어주면, 이러한 HttpStatus 코드가 응답되어선 안되는데???
CustomValidationException이 제대로 동작했는지 확인해보자.
package com.cos.photogramstart.handler;
import java.util.Map;
public class CustomValidationException extends RuntimeException {
//객체를 구분하기 위해 '시리얼넘버'를 넣어주는것
//JVM을 위해 걸어주는것이다
private static final long serialVersionUID = 1L;
private Map<String, String> errorMap;
public CustomValidationException(String message, Map<String, String> errorMap) {
super(message);
this.errorMap = errorMap;
}
public Map<String, String> getErrorMap() {
return errorMap;
}
}
-> validationException에서 분기하여 errorMap도 같이 응답할것인지, message만 응답할 것인지를 if문으로 만들어주자.
package com.cos.photogramstart.handler;
import com.cos.photogramstart.handler.ex.CustomApiException;
import com.cos.photogramstart.handler.ex.CustomValidationApiException;
import com.cos.photogramstart.handler.ex.CustomValidationException;
import com.cos.photogramstart.util.Script;
import com.cos.photogramstart.web.dto.CMRespDTO;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
@RestController
@ControllerAdvice //모든 Exception을 다 낚아챈다
public class ControllerExceptionHandler {
//JavaScript로 응답하는 Handler
@ExceptionHandler(CustomValidationException.class)
public String validationException(CustomValidationException e) {
//CMRespDto, Script 비교
//1. 클라이언트에게 응답할때는 Script 좋음
//2. Ajax통신을 하거나 Android 통신을 하게되면 CMRespDto가 좋다
//즉, 개발자를 위한 응답에는 CMRespDto, 클라이언트를 위해서는 Script가 좋다
if (e.getErrorMap() == null) {
return Script.back(e.getMessage());
} else {
return Script.back(e.getErrorMap().toString());
}
//자바스크립트로 짜는 부분까지 2가지 방향으로 갔을때 사용자에게 어떤것이 좋을지 판단해보라고 나눈것
}
//CMRespDto 오브젝트를 응답하는 핸들러
@ExceptionHandler(CustomValidationApiException.class)
public ResponseEntity<?> validationApiException(CustomValidationApiException e) {
return new ResponseEntity<>(
new CMRespDTO<>(-1, e.getMessage(), e.getErrorMap()),
HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(CustomApiException.class)
public ResponseEntity<?> apiException(CustomApiException e) {
return new ResponseEntity<>(
new CMRespDTO<>(-1, e.getMessage(),null),
HttpStatus.BAD_REQUEST);
}
// <?>를 사용하면 제네릭 타입이 결정이 된다
// BAD_REQUEST는 400번대 오류이다 -> 너가 요청을 잘못했다
}
-> 정상적으로 ValidationException이 동작했지만, 클라이언트가 알 필요없는 경로까지 응답된다. 따라서 return Script.back(e.toString());이 아닌 getMessage를 응답해주도록 하자.
if (e.getErrorMap() == null) {
return Script.back(e.getMessage());
} else {
return Script.back(e.getMessage());
}
'메타코딩 SNS프로젝트' 카테고리의 다른 글
31. 프로필 페이지 - Image 프로필페이지에 View 렌더링하기 (0) | 2022.06.24 |
---|---|
30. 프로필 페이지 - Image 양방향 매핑 이해하기 (0) | 2022.06.21 |
28. 프로필 페이지 - Image 파일경로를 DB에 INSERT하기 (0) | 2022.06.21 |
27. 프로필 페이지 - Image 업로드 경로를 프로젝트 외부에 두는 이유 (0) | 2022.06.20 |
26. 프로필 페이지 - Image를 서버에 업로드하기(UUID활용) (0) | 2022.06.20 |