궤도

[Spring] 파일 업로드시 dto도 같이 받고 싶으면 어떻게 할까? / 부제 : @PutMapping에 파일 업로드 해도 되나? 본문

💻 현생/📋 스터디

[Spring] 파일 업로드시 dto도 같이 받고 싶으면 어떻게 할까? / 부제 : @PutMapping에 파일 업로드 해도 되나?

영이오 2021. 7. 31. 14:36

면적면적 개발 도중...궁금한게 생겼다.

 

이건 북적북적의 책 정보 수정 화면인데, 커버이미지를 수정할 수도 있다.

 

https://gaemi606.tistory.com/m/entry/Spring-Boot-multipartform-data-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-React-Axios-REST-API

 

Spring Boot | multipart/form-data 파일 업로드 ( + React , Axios, REST API, multiple files)

클라이언트(React) 측에서 파일과 함께 JSON데이터를 전송해보기. 찾아보면 파일 하나만 전송하는 경우 예제는 많은데 JSON데이터와 함께 보내는 건 잘 없었다.. 거기다가 나는 모델 안에 오브젝트

gaemi606.tistory.com

이런저런 예제를 찾아보다 알게된 사실은

 

1. 파일업로드시엔 @RequestPart로 한다.

2. 이건 form-data니까 dto를 보낼때도 기존의 @RequestBody가 아니라 @RequestPart를 붙여야한다.

 

뭐 그래...post 때는 커버이미지가 필수일테니 이럼 되겠거니 하겠는데...

난 지금 수정을 하려는 것이다. 그니까 기존 이미지가 수정될수도 있고 안될 수도 있는데

 

VIVA 때도 프로필 수정이 이런 로직이었던 것 같아서 코드를 찾아봤다.

// Router
router.post("/", upload.single('profile_img'), (req, res) => {
  try {
    res.send({ //파일 정보 넘김
      message: "upload success",
      status: 'success',
      data: {
        file: req.file
      }
    });
  } catch (err) { //무언가 문제가 생김
    res.send({
      message: "ERROR",
      status: 'fail'
    })
  }
});

// Update userInfo
//localhost:3001/api/user/profile/samdol
router.put('/:stu_id', function (req, res, next) {
  const id = req.params.stu_id;
  let body = req.body;

  models.student.update({
    stu_nick: body.stu_nick,
    stu_grade: body.stu_grade,
    stu_photo: body.stu_photo
  }, {
    where: { stu_id: id }
  })
    .then(num => {
      if (num == 1) {
        res.send({
          message: "UserInfo was updated successfully.",
          status: 'success'
        });
      } else {
        res.send({
          message: "Data was not found or req.body is empty!",
          status: 'fail'
        });
      }
    })
    .catch(err => {
      res.send({
        message: "Error updating UserInfo",
        status: 'fail'
      });
      console.log(err);
    });
});

그랬다...당시 아무것도 모르던 나는 이미지 업로드와 프로필 수정을 따로 했었다.

 

그렇게 혼란속 구글링을 이어나가다가...@RequestPart의 required를 false로 할 수 있다는 것을 알아냈다.

그럼 뭐 null인 경우를 핸들링해주면 이미지를 수정하지 않아도 될 것 같은데 갑자기 이런 생각이 든 것이다.

 

사진 업로드는 엄밀히 따지면 POST인데 PUT에 해도 되는건가..? 근데 POST와 PUT의 차이점이 정확히 뭐지?

 

막연히 POST는 추가, PUT은 수정이라고 써오긴 했지만 갑자기 그 둘의 차이가 이거말고 뭔데?라고 생각하니 말이 안나왔다.

 

https://velog.io/@53_eddy_jo/RESTful%ED%95%9C-%EC%84%B8%EA%B3%84%EC%97%90%EC%84%9C%EC%9D%98-POST%EC%99%80-PUT%EC%9D%98-%EC%B0%A8%EC%9D%B4-%EA%B1%B0%EA%B8%B0%EC%97%90-FETCH%EA%B9%8C%EC%A7%80

 

RESTful한 세계에서의 POST와 PUT의 차이, 거기에 PATCH까지

HttpResponseHTTP/1.1 200 OK{ “id”: 1, “name”: “뽀로로”, “grade”: 1}HttpResponseHTTP/1.1 200 OK{ “id”: 1 “name”: “뽀로로”, “grade”: 2}HTTP/1.1 200 OK{ “id”

velog.io

그래서 난 파일 업로드에 PutMapping을 쓸 수 있단 걸까?

 

https://stackoverflow.com/questions/66439660/spring-boot-rest-api-multiple-requestpart-not-working-with-feign-client

 

Spring Boot Rest Api - Multiple @RequestPart not working with Feign client

I have a Spring Boot 2.4.3 with @RestController public class DemoFeignController { @PutMapping(value = "/document", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public String u...

stackoverflow.com

쓸 수 있을 것 같기도 하고...그래서 일단 하나하나 해보기로 했다.

 

    @PutMapping(value = "/info/{bookId}")
    public void bookshelfInfo(@AuthenticationPrincipal User user,
                                           @PathVariable Long bookId,
                                           @RequestPart(required = false) MultipartFile thumbnail,
                                           @Valid @RequestPart BookshelfInfoUpdateReqDto bookshelfInfoUpdateReqDto) {
        log.info("[Request] Update book info " + bookId);
        log.info("file name = "+thumbnail.getOriginalFilename());
        log.info("dto info = "+bookshelfInfoUpdateReqDto.getTitle());
    }

대충 이렇게 짜고

 

날렸는데

 

오류가 떴다!

 

이런저런 삽질을 해본 뒤 고쳤다.

여기 dto를 넘기는 부분을 application/json으로 바꾸고

 

된다!

 

용량이 맞는 사진이 삼돌이 사진밖에 없어서 어쩌다보니 이걸로 테스트했다. 견상권 침해로 고소당해도 할 말은 없는..

 

근데 이걸 뺴니까 nullPointerException이 발생한다. required=false를 했는데 왜지?

 

    @PutMapping(value = "/info/{bookId}")
    public void bookshelfInfo(@AuthenticationPrincipal User user,
                                           @PathVariable Long bookId,
                                           @RequestPart(required = false) MultipartFile thumbnail,
                                           @Valid @RequestPart BookshelfInfoUpdateReqDto bookshelfInfoUpdateReqDto) {
        log.info("[Request] Update book info " + bookId);
        if(thumbnail!=null)
            log.info("file name = "+thumbnail.getOriginalFilename());
        log.info("dto info = "+bookshelfInfoUpdateReqDto.getTitle());
    }

아주 기초적인 실수였다.

 

어쨌든 결론은 PutMapping이어도 required=false 해놓고 넘기면 괜찮다는 것이다.

다만 지금은 아주 기초적인 것만 한거라 s3 버킷에 업로드 해보면서 문제가 생기진 않는지 봐야겠다.

 

s3 통해서 파일 업로드 한 글

Comments