반응형

Image.class
- Image 도메인을 만든다.
- postImageUrl: 사진을 전송받아서 그 사진을 서버의 특정 폴더에 저장 -Db에 그 저장된 경로를 insert
- 한명의 유저는 여러 이미지를 업로드를 할 수 있고, 하나의 이미지는 하나의 유저만이 만들 수 있으므로 @ManyToOne
- user는 db에서 User객체가 아닌, FK로 저장되기 때문에 userId라는 이름으로 컬럼명을 설정해준다.

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
public class Image {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String caption;
    private String postImageUrl;

    @JoinColumn(name = "userId") // DB컬럼명 설정
    @ManyToOne
    private User user;

    //이미지 좋아요 정보-> 추후추가

    //댓글-> 추후추가

    private LocalDateTime createDate;

    @PrePersist
    public void createDate(){
        this.createDate = LocalDateTime.now();
    }

}

 

ImageRepository

public interface ImageRepository extends JpaRepository<Image, Integer> {
}

 

ImageController

- imageUploadDto와 유저정보인 principalDetails를 받도록 한다.
- 업로드가 완료되면 유저의 프로필 화면으로 이동하도록 설정한다.
- MultipartFile은 @Notblank 어노테이션이 지원되지 않으므로 이미지 첨부 유효성 검사는 if문으로 처리해준다.

@RequiredArgsConstructor
@Controller
public class ImageController {

    private final ImageService imageService;

   ...
   
    @PostMapping("/image")
    public String imageUpload(ImageUploadDto imageUploadDto, 
    @AuthenticationPrincipal PrincipalDetails principalDetails){

        if(imageUploadDto.getFile().isEmpty()){
            throw new CustomValidationException("이미지가 첨부되지 않았습니다.", null);
        }
        imageService.upload(imageUploadDto, principalDetails);

        return "redirect:/user/"+principalDetails.getUser().getId();
    }


}

 

ControllerExceptionHandler

- 업로드 이미지가 없을때 CustomValidationException을 throw하고 그것을 ControllerExceptionHandler의 validationException메소드가 낚아챈다.
- 이 경우 errorMap 매개변수를null로 전달하기 때문에 errorMap이 null일 경우, null이 아닐 경우를 분기해야한다.

@RestController
@ControllerAdvice //모든 Exception들을 낚아챔
public class ControllerExceptionHandler {

   
    @ExceptionHandler(CustomValidationException.class) //CustomValidationException 발동하는 모든 Exception을 이 함수가 가로챔
    public String validationException(CustomValidationException e){
        if(e.getErrorMap() ==null){
            return Script.back(e.getMessage());
        }else{
            return Script.back(e.getErrorMap().toString());
        }
    }

....
}

 

CustomValidationException

public class CustomValidationException extends RuntimeException{

    //객체를 구분할때
    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;
    }
}

 

ImageUploadDto

- 파일을 받을거기 때문에 요청을 위한 dto(ImageUploadDto)를 만들어준다.
- ImageUploadDto는 file 과 caption을 받을 수 있도록 한다.
- toEntity 메소드를 통해 Image객체로 형변환할 수 있도록 한다. 

@Data
public class ImageUploadDto {

    //MultipartFile 에는 @Notblank가 지원안된다.
    private MultipartFile file;
    private String caption;

    public Image toEntity(String postImagUrl, User user){
        return Image.builder()
                .caption(caption)
                .postImageUrl(postImagUrl)
                .user(user)
                .build();
    }
}

 

ImageService

- upload메소드에는 imageUploadDto와 principalDetails를 받는다.
- imageFilePath : 실제 저장될 경로
- uploadFolder: 이미지가 저장될 로컬경로다, @Value 어노테이션으로 해당 값을 가져올 수 있다. 
- imageUploadDto의 toEntity() 메소드를 통해 db에 저장할Image객체로 변환한다.
  이때 매개변수로 uuid가 포함된 이미지명과 현재 로그인한 유저의 정보를 전달한다. 

@RequiredArgsConstructor
@Service
public class ImageService {

    @Value("${file.path}") //application.properties에서 가져옴
    private String uploadFolder;

    private final ImageRepository imageRepository;

    @Transactional
    public void upload(ImageUploadDto imageUploadDto, PrincipalDetails principalDetails){

        UUID uuid = UUID.randomUUID();
        String imageFilename = uuid + "_"+ imageUploadDto.getFile().getOriginalFilename(); //db에 저장되는 파일명

        Path imageFilePath = Paths.get(uploadFolder+imageFilename);

        // 통신, I/O -> 예외가 발생할 수 있다.
        try {
            Files.write(imageFilePath, imageUploadDto.getFile().getBytes()); //(실제 저장될 경로, 실제 이미지 파일)
        } catch (Exception e){
            e.printStackTrace();
        }

        //Image 테이블에 저장
        Image image = imageUploadDto.toEntity(imageFilename, principalDetails.getUser());
        imageRepository.save(image);


    }
}

 

upload.html
- 파일은 전송하는것이므로 enctype 로 multipart/form-data 로 설정해준다.

<!--사진업로드 Form-->
<form class="upload-form" th:action="@{/image}" method="post" enctype="multipart/form-data">
    <input type="file" name="file"  onchange="imageChoose(this)"/>
    <div class="upload-img">
        <img src="/images/person.jpeg" alt="" id="imageUploadPreview" />
    </div>

    <!--사진설명 + 업로드버튼-->
    <div class="upload-form-detail">
        <input type="text" placeholder="사진설명" name="caption">
        <button class="cta blue">업로드</button>
    </div>
    <!--사진설명end-->

</form>

+ Recent posts