• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

image 전송과 함께 데이터는 json으로 보내고 싶은 경우

21.09.12 07:49 작성 조회수 6.8k

2

restAPI에서 image 와 함께 데이터를 같이 보낼때,

보내려는 데이터가 많이 복잡할 경우에는 json으로 보내는 것이 좋을 것 같아 json으로 보내는 방법을 시도해봤는데요

 

@RequestPart DTO dto 이런식으로 dto를 직접 넣었을때는
기본적으로 requestData가 formdata 형식이라 그런지 리졸버가 아예 처리를 하지 못하고, request를 받지 않는 것 같습니다.

 

그래서 아래와 같이 json의 경우 string으로 받아 테스트 해보았는데요

    @PostMapping
// @PostMapping(consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<ResponseOKDto<PostResponseDto>> post(
@RequestPart("data") String data, @RequestPart("file") MultipartFile file,
@ApiIgnore HttpSession session) throws IOException {

Account account = getSessionCheckedAccount(session);

if(!file.isEmpty()){
String path = session.getServletContext().getRealPath("/") + "images/" + file.getOriginalFilename();
file.transferTo(new File(path));
}
String o = objectMapper.readValue(data, String.class);
// objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
PostRequestDto postRequestDto = objectMapper.readValue(data, PostRequestDto.class);
Long postId = postService.post(postRequestDto.getTitle(), postRequestDto.getDesc(),
postRequestDto.getPrice(), postRequestDto.getCategoryTag(), account);
return new ResponseEntity<>(new ResponseOKDto<>(
new PostResponseDto(postId)), HttpStatus.OK);
}

이와 같이 포스트맨으로 data를 보냈을 때, 스트링으로 받은 data를 오브젝트 매퍼를 이용해서 바로 DTO로 변환해보니 jackson에서 변환할수 없다는 익셉션이 떠

(Unexpected character ('\' (code 92)): was expecting double-quote to start field name)

디버깅을 해보니 object mapper를 이용해 String으로 한번 변환하고 다시 dto로 변환해야만 잘 동작 했습니다.

아래와 같이 데이터를 ""로 감싸주지 않은 경우에는 해당 에러가 출력되었고요

no String-argument constructor/factory method to deserialize from String value ('{"title":"test","desc":"test","price":10,"categoryTag":"Digital"}')
 at [Source: (String)""{\"title\":\"test\",\"desc\":\"test\",\"price\":10,\"categoryTag\":\"Digital\"}""; line: 1, column: 1]

제가 했던 접근 방식은 문제점이 일단 2개가 보이는데

- 스트링으로 한번 더 변환을 해줘야해 복잡하다

- 들어온 data 값에 대해 @Validated 를 적용하지 못한다.

 

Q1. 다른 방식으로 data를 json형식으로 받아오고 dto에 대한 validtaion을 적용할 수 있는 방법이 있을까요?

 

Q2. 필드가 아주 많을 경우에 form으로 하나씩 받는건 너무 코드가 지저분 해지는 것 같은데, 벨리데이션과 함께 적용하려면 이렇게 해야하는 걸까요?

 

Q3. objectMapper로 두번 변환 대신에 doubleQuetoALLOW 옵션을 주었는데도 에러가 떴는데, 더블쿼터 에러를 해결할 수 있는 방법이 있을까요?

 

답변주시면 감사드리겠습니다.

항상 좋은 강의해주셔서 감사합니다

답변 3

·

답변을 작성해보세요.

7

안녕하세요. 승현님

이런 문제는 보통 이미지와 JSON데이터를 함께 보내지 않습니다.

API를 전송할 때는 보통 content/type을 application/json으로 전송하는데 이것은 multipart/formdata 방식이 아니기 때문입니다.

이런 문제는 보통 다음과 같이 해결합니다.

1. 분리 요청

이미지만 AJAX등을 통해서 별도 요청으로 먼저 서버에 전송합니다.

서버는 이미지를 저장하고, 저장된 이미지의 id(또는 파일명)를 클라이언트에 전송합니다.

클라이언트는 이미지의 id와 전송할 데이터를 application/json으로 전송합니다.

2. base64 인코딩

이미지를 base64로 인코딩해서 application/json으로 전송할 데이터와 base64로 인코딩된 이미지를 함께 전송합니다.

감사합니다.

 

관련해서 이미지 ajax 전송, 이미지 base64 인코딩 전송등으로 검색해보시면 도움이 되실거에요.

감사합니다.

1

유승현님의 프로필

유승현

질문자

2021.09.12

또한 validation 부분은 dto로 변환할때 할수 있으면 좋을 것 같은데,
이게 불가능할 경우 entity단에서 field에 validation을 적용하는 방법을 사용할 순 있을것 같습니다,

0

유승현님의 프로필

유승현

질문자

2021.09.12

data 입력 값을 아래와 같이 했을 경우 object mapper를 한번만 사용해도 되는것은 확인했습니다.

 

@PostMapping
// @PostMapping(consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<ResponseOKDto<PostResponseDto>> post(
@RequestPart("data") String data, @RequestPart("file") MultipartFile file,
@ApiIgnore HttpSession session) throws IOException {

Account account = getSessionCheckedAccount(session);

if(!file.isEmpty()){
String path = session.getServletContext().getRealPath("/") + "images/" + file.getOriginalFilename();
file.transferTo(new File(path));
}
// String o = objectMapper.readValue(data, String.class);
// objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
PostRequestDto postRequestDto = objectMapper.readValue(data, PostRequestDto.class);
Long postId = postService.post(postRequestDto.getTitle(), postRequestDto.getDesc(),
postRequestDto.getPrice(), postRequestDto.getCategoryTag(), account);
return new ResponseEntity<>(new ResponseOKDto<>(
new PostResponseDto(postId)), HttpStatus.OK);
}