진짜 개발자
본문 바로가기

FrameWork/Spring Boot

Spring Boot - 개발자를 기억하게하지 말자 (파일리스트와 데이터리스트 요청을 하나의 객체로 바인딩하기 : @ModelAttribute)

728x90
file과 data를 객체로 바인딩하기

 

Spring을 이용해 Rest API 개발중, file과 data를 같이 업로드해야 하는 상황이 있었습니다.

 

 

file과 data는 논리적으로 연관관계가 있었으며, 이를 List형태로 전송하는 경우, 각각의 인덱스 요소가 논리적으로 같은 데이터임을 기억해야하는 상황이었습니다.

이때 이들을 하나의 객체로 맵핑하여 이 조건을 개발자가 기억하지 않아도 되도록 변경하였습니다.

 

 

 

 

1. 시나리오

예를들어, 어떤 팀에 멤버를 추가하는 상황에서 멤버의 프로필 정보와, 사진을 같이 받아야 한다고 할때. 클라이언트는, 서버에게 사용자의 프로필정보(data)사진(file)을 함께 전송해야 합니다.

 

 

 

1.1 중요 사항

이때, 멤버의 사진과 프로필 정보는, 논리적으로 연관이 있는 데이터들입니다. 즉, Server에서 값을 전달받았을때,

특정 사진이 특정 프로필의 사진임을 알 수 있어야 합니다.

 

 

 

1.2 해결 방법

1) 사진을 Base64로 인코딩하여 JSON을 이용해 전달

가장 간편한 방법입니다. JSON을 이용해 @RequestBody로 전달한다면, 이미지와, 프로필 정보간의 논리적 연관관계를 구성하여 전달할 수 있습니다.

 

 

Controller

Controller는 위와 같이 @RequestBody 어노테이션을 이용해, 사용자의 요청에 담긴 데이터를, 객체로 맵핑할 수 있습니다.

 

 

문제점

  • base64로 이미지를 변환하는 경우 최대 33% 까지 파일의 크기가 증대됩니다.
  • 위의 이유로 네트워크 대역 낭비가 심합니다.
  • Spring에서는 특정 크기이상의 데이터가 Body에 담기는 경우 요청을 해석하지 못하는 경우가 있습니다.(원인 불명...)

 

 

 

2) 사진과, 프로필을 form으로 별도 전달

이번 방법은 form data을 이용해 데이터를 전송하는 방법입니다

 

 

문제점

  • 객체를 form을 이용해 데이터를 전달받고, 해당 데이터를 객체로 맵핑하기 위해서, Spring Type Conversion을 이용해야 합니다.
  • 멤버 리스트를 전달 받는 경우 데이터의 논리적 연관을 파악하기가 어렵습니다.

 

 

 

데이터의 논리적 연관을 파악하기 어려움 : 개발자에게 기억을 강요

위 형식으로 데이터를 받는 경우, 멤버 리스트 데이터를 받을 때 더 큰 문제가 발생합니다.

 

리스트로 전달받는다면 위와 같은 형식으로 데이터를 전달 받을 수 있습니다. 각각 배열의 같은 인덱스 요소가 같은 멤버의 데이터입니다.

 

문제점은 바로 두 데이터간의 논리적 연관관계를 개발자가 인지하고 있어야 한다는 점입니다. 즉, "같은 인덱스의 요소는 같은 멤버의 데이터야"를 가정한 채로, 프로그래밍을 해야합니다.

 

 

 

 

3) formdata를 하나의 객체로 전달

이번에는 @ModelAttribute 를 이용해 form data를 객체로 바로 맵핑하는 방법입니다. 이방식으로 처리하면, 데이터 간의 논리적 연관관계를 표현하며, base64와 같이 인코딩을 하지 않기 때문에 파일 사이즈가 증대되는 현상도 발생하지 않습니다.

 

아래에서 더 자세히 알아보겠습니다.

 

 

 

 

2. 구현

앞서 살펴본 구현 방법들중 3) formdata를 하나의 객체로 전달을 이용. 하여 구현을 할 것입니다.

 

2.1 한 멤버의 정보를 전송

MemberProfile

 

RegisterMemberRequest

 

TestController

 

 

테스트

PostMan 을 이용해 테스트를 진행합니다.

 

POST 메소드를 선택하고 url을 입력합니다.

 

Body를 클릭하고, form-data를 선택합니다.

그 후, key의 type을 File로 변환합니다.

그 후, RegisterMemberRequest클래스의 MultipartFile에 해당하는 변수의 이름을 적습니다. 마지막으로 value에 이미지를 업로드합니다.

 



이번엔 MemberProfile에 맵핑될 데이터를 파라미터에 설정해야합니다. key에는 RegisterMemberRequest클래스의 MemberProfile타입의 변수의 이름 . MemberProfile 클래스의 필드 이름 형식으로 입력합니다.

 


 

 

 

 

2.2 여러 멤버의 정보를 전송

이번엔 여러 멤버의 데이터를 전송해보겠습니다.

 

RegisterMemberRequestForm

우선 List<RegisterMemberRequest>를 멤버변수로 가지는 클래스를 생성합니다.

 

TestController

Contoller를 위와같이 변경합니다.

 

 

테스트

List에 데이터를 바로 집어 넣는다는 생각으로 formdata의 키값을 입력하시면 됩니다.

 

RegisterMemberRequestForm 클래스의 memberRequestList 변수는 list형태입니다. 따라서, memberRequestList[0], memberRequestList[1] ... 의 형식으로 키값을 지정합니다.

 

 

 

마무리

앞서 알아본 방식들중 3번째 방식을 이용한다면, 개발자에게 특정 조건을 기억하도록 강요하지 않습니다. 개발자는 항상 코드를 통해 어플리케이션을 표현해야합니다.