본문 바로가기
컴공/스프링 부트 (Spring boot)

스프링부트(Spring Boot) 이미지 포함한 투표 생성하기(프론트와 협업)

by 만슨 2023. 12. 5.

투표를 만들 때 이미지 포함한 투표를 어떻게 받아올지 고민을 많이 하였다. 

 

처음 이미지 없이 투표 내용만 받아 올 때는 @RequestBody 어노테이션을 이용하여 JSON 형태로 받아와서 구현을 하였는데 이미지파일을 함께 받기 위해서 @RequestPart어노테이션으로 받아오게 수정을 하였다

 

 어떻게 받아오게 됐는지 한 번 살펴보자

 

 

 

 

처음에는 PollRequest 디티오와 Multipart타입인 mediaData를 @RequestPart를 이용하여 받아오려고 하였다

 

 

PollRequest 구조

 

 

그러나 프론트에서 계속 오류가 뜨고

투표 오류 응답: 415 {"timestamp":"2023-12-02T17:08:37.831+00:00","status":415,"error":"Unsupported Media Type","path":"/polls/upload"} 

 

 서버에선

2023-12-03 14:12:30.974 WARN 10 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported]

 

위와같은 오류가 계속떳다.. 프론트에서

 

const pollRequest = {
          user: nickname,
          title: titleInput,
          question: description,
          category: selectedCategory,
          choiceDtos: options.map((option, index) => ({
            text: option.trim(),
          })),
        };

        formData.append(
          'pollRequest',
          [JSON.stringify(pollRequest)],
          {
            type: 'application/json',
          }
        );


if (selectedMedia) {
          const localUri = selectedMedia;
          const filename = localUri.split('/').pop();
          const match = /\.(\w+)$/.exec(filename ?? '');
          const type = match
            ? `image/${match[1]}`
            : 'image';

          const response = await fetch(localUri);
          const blob = await response.blob();

          formData.append('mediaData', {
            uri: localUri,
            name: filename,
            type: type, // Use the actual Content-Type from the result
            blob: blob,
          });
        }
      }
      console.log(
        'formData 자 과연 어떻게 보일까?',
        formData
      );
      const response = await fetch(
        'https://port-0-capstone-backend-1d6du62aloxt3u8i.sel5.cloudtype.app/polls/upload',
        {
          method: 'POST',
          headers: {
            'Content-Type': 'multipart/form-data',
            'AUTH-TOKEN': jwtToken,
          },
          body: formData,
        }
      );

(프론트엔드 요청 코드)

 

 위와 같이  'Content-Type' 도  'multipart/form-data' 로 설정 해주었고 url에도 문제가 없었는데 서버로 값 자체가 넘어오지 않았다..

 

그래서 열심히 찾아본 결과 이미지 파일과 Dto를 함께 받는 글들을 보았고 

 

차이점을 본 결과 구조는 똑같지만 Dto 에 choice필드를 list형식으로 받아오는 것이 눈에 띄었다

 

그래서 choice를 따로 받아오기로 결정!

 

요청을 두개로 나누고 poll을 먼저 받은 후 choice를 받기로 하였다

(choice를 poll에 매핑하기위해 pollId가 필요해서)

 

 

Dto로 받지 않고 필요한 필드를 @PathVariable을 통해 받아오고(단순 문자열로 받아와짐)

 

  

 JSON 문자열과 Java 객체 사이의 변환을 해주는  ObjectMapper를 통해 JSON 문자열(user)을 PollRequest 타입의 Java 객체로 변환해주었다. 

그 후 서비스에서 투표 정보를 db에 저장 해주었다

 

 

선택지는 위에서 만든 투표에서 pollId를 받아와 투표를 찾고 choiceRepository를 이용하여 저장 하였다.

 

선택지를 저장하는 과정에서

 

엔터티(Poll)의 컬렉션 속성(choices)에 대해 "all-delete-orphan" 옵션이 지정되었고, 그 컬렉션이 해당 엔터티에서 더 이상 참조되지 않는데도 불구하고 Hibernate에서 해당 컬렉션을 처리하려고 시도한다는 문제 발생!

 

따라서 위처럼 기존의 컬렉션을 비워주고 다시 선택지를 투표에 넣어주었다!

 

 

참고 : Spring Controller에서 MultipartFile, Dto를 함께 요청하기 (velog.io)