이미지파일을 BLOB 형태로 DB에 저장하기 && React - Spring 간 서버통신 시 BLOB 형태를 이용해 이미지파일 데이터 전송하기
이전 게시글에서 (이미지파일을 정적파일로 저장하는 방법 && 해당 정적파일을 img태그의 src속성을 이용해서 서버통신하는 방법) && (React - Spring 간 file 데이터전송방법을 알아보았습니다. 이번 게시글에서는 file 데이터를 Spring에서 받아서 정적파일로 저장하는 게 아닌 BLOB형태로 데이터를 바꿔서 DB에 저장하고 필요할 때 DB에서 해당 데이터를 꺼내서 Base64 byte 배열로 바꿔서 React에 전송하는 방법을 알아보겠습니다.
다만 해당 방법은 최근 들어 잘 사용하지 않는 방법으로 최근에는 file 서버를 따로 운영해서 해당 서버에 정적 이미지파일을 저장하고 DB에 해당 이미지파일의 경로만 문자열 형식으로 저장하는 방식을 사용하지만 로컬서버에서 테스트하거나 file 서버를 별도로 운영하지 않는 경우 BLOB 형태의 DB저장방식을 사용할 수 있습니다.
React에서 Spring 서버로 file을 전송하는 방식은 동일하므로 이는 생략하고 이번 게시글에서는 Spring에서 해당 file을 받아서 DB에 어떤 방식으로 저장하고 추후 해당 이미지파일이 필요할 때 DB에서 꺼내온 BLOB형태를 어떻게 가공해서 Spring - React 간 서버통신을 하는지 그리고 해당 Base64 byte 배열을 React가 받아서 어떻게 이미지를 화면에 출력하는지를 알아보겠습니다.
(1) Spring
@PostMapping(value = "/updateUser", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE})
@ResponseBody
@CrossOrigin("*")
public String updateUser(@RequestPart HashMap<String,Object> userData,
@RequestPart(required = false) MultipartFile file)
throws IOException, SQLException {
String userId = (String) userData.get("userId");
String userPassword = (String) userData.get("userPassword");
String userEmail = (String) userData.get("userEmail");
String userCellPhone = (String) userData.get("userCellPhone");
Idmanage idmanage = IdManagerepository.findByUSERID(userId);
idmanage.setUSERPASSWORD(userPassword);
idmanage.setEMAIL(userEmail);
idmanage.setCELLPHONE(userCellPhone);
if(file != null) {
byte[] contents = file.getBytes();
Blob blob = new SerialBlob(contents);
idmanage.setUSERFILE(blob);
}
IdManagerepository.flush();
return "Spring -----> 변경완료";
}
위의 코드는 실제 프로젝트에서 일부 발췌한 것으로 실제로 사용시 조금 수정하셔서 사용하시면 됩니다. updateUser로 요청이 들어왔을 때 consumes로 첫 번째 받은 데이터는 JSON형식임을, 두 번째 받은 데이터는 MULTIPART형식임을 지정해주고(React에서 보내는 Formdata 형식을 순서대로 작성해주시면 됩니다.) @RequestPart 어노테이션으로 첫 번째 데이터는 userData 변수에 저장하고 두 번째 데이터는 file 변수에 저장합니다. 이후 userData에서 필요한 데이터를 String 변수로 꺼낸 후 해당 변수로 findbyUSERID(JPA 문법입니다.) 해서 가져온 데이터를 update하기 위해 setter를 이용합니다. 이 때 USERFILE이라는 멤버변수는 데이터타입이 BLOB으로 정의되어있고 DB에도 BLOB으로 정의되어 있어서 이를 위해서 받아온 file 데이터를 getBytes()메소드를 이용해 byte 배열로 바꾼 후 new SerialBlob을 이용해 Blob객체로 바꾼 다음 setter를 이용해 데이터를 update하고 있습니다. 이후 update 쿼리문을 DB에 전송시키기 위해 flush를 하고 모든 과정이 종료된 후 변경완료라는 문자열을 보내고 있습니다.
추후 해당 BLOB데이터가 React에서 필요할 때 Spring - React 간 서버통신 방식에 대해 알아보겠습니다.
(1) React
//useState 선언
const [selectedUser, setSelectedUser] = useState({
userfile: null,
});
//axios로 Spring과 서버통신
const URL = "http://localhost:8088/userImage";
const postData = {
userid: userList.user_id
}
axios.post(URL, postData, null)
.then( response => {
setSelectedUser(selectedUser => ({
...selectedUser,
userfile: response.data
}));
})
.catch(error => {
console.log(error);
console.log(error.status);
})
//받아온 data 화면에 출력
return(
<img src={`data:image/jpeg;base64,${selectedUser.userfile}`} alt="Can't Loading..." />
)
React에서 이미지파일이 필요한 경우 JSON 형태의 데이터에 전송할 데이터를 담은 후 axios로 서버통신합니다. 이후 Spring에서 받아온 Base64 byte 배열을 userfile이라는 JSON 형태의 데이터 멤버변수에 set합니다. 이후 React가 관리하는 변수와 이를 변경할 setter를 useState를 이용해 선언했기 때문에 해당 setter를 사용할 경우 React가 자동 Rerendering하게 되고 이 때 받아온 데이터를 저장하고 있는 selectedUser.userfile변수를 이용해 화면에 이미지파일을 출력하게 됩니다. 이 때 주의할 점이 base64 byte 배열인 경우 src에 그냥 넣게 되면 이미지파일이 출력되지 않고 반드시 앞에 "data:image/[image파일 확장자];base64,"를 붙이고 나서 해당되는 base64 byte 배열 데이터를 넣으시면 정상적으로 출력됩니다.
(2)Spring
@PostMapping("/userImage")
@ResponseBody
public byte[] userImage(@RequestBody HashMap<String, Object> map) throws IOException, SQLException {
String userid = (String) map.get("userid");
Idmanage idmanage = IdManagerepository.findByUSERID(userid);
Blob blob = idmanage.getUSERFILE();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
InputStream in = blob.getBinaryStream();
int n = 0;
while ((n = in.read(buf)) >= 0) {
baos.write(buf, 0, n);
}
in.close();
byte[] bytes = baos.toByteArray();
byte[] encodeBase64 = Base64.getEncoder().encode(bytes);
return encodeBase64;
}
Spring에서는 userImage로 요청이 들어오면 받은 JSON형태의 데이터를 @RequestBody 어노테이션으로 map 변수에 담은 후 String데이터에 저장합니다. 이후 해당 userid로 findByUSERID를 이용해 해당되는 Idmanage 객체를 찾은 후 해당 객체의 getter를 이용해 BLOB형태로 저장되어있는 USERFILE을 받아옵니다. 이후 ByteArrayOutputStream, byte[] buf, InputStream을 이용해 blob 형태의 데이터를 ByteArrayOutputStream 객체에 write 하고 해당 객체의 toByteArray()메소드를 이용해 byte배열로 변환 후 Base64.getEncoder()의 encode()메소드로 byte 배열을 Base64 byte 배열로 변경하고 이를 return합니다.
이번 게시글에서는 앞서 살펴보았던 React - Spring 간 서버통신 시 file 데이터를 보내는 방법을 이용하여 추가적으로 Spring에서 해당 file 데이터를 BLOB 객체로 저장하는 방법 및 해당 BLOB 객체를 Base64 byte 배열로 변환한 후 이를 React로 전송하고 React에서는 이를 이용해 img파일을 화면에 출력하는 방법까지 알아보았습니다.