이번 블로그에서는 이미지를 저장하는 방법에 대해서 정리해보고자 한다.
API를 하나 작성하면서 이미지 저장에 대해서 알아보고자 하는데 기본적인 API 과정은 다음과 같다.
1. 클라이언트가 이미지 입력
2. 이미지 인코딩해서 전송
3. 서버에서 이미지 디코딩
4. DB에 저장 또는 파일 시스템에 저장
1. 클라이언트가 이미지 전송
테스팅을 위해 직접 프론트를 만들 수 있지만 insomnia의 multipart로 데이터를 전송받아 테스팅을 하였다.
Multipart Form을 사용하면 Key-Value 형태로 서버에 데이터 전송이 가능한데 이미지 전송 또한 가능하다.
2. 이미지 인코딩해서 전송
기본적으로 이미지는 바이너리 형태의 데이터로 저장되어 있다. 보통 네트워크를 통해 이미지 파일을 서버에 전송시 인코딩 과정을 거쳐 서버에 전송한다. Insomnia에서는 데이터를 전송할 때, 바이너리 형태의 데이터를 그대로 전송한다. 테스팅에서는 그대로 사용했지만 프론트를 만들었다면, js에서 인코딩해서 전송이 가능하다. 따라서 인코딩하는 코드를 알아보고자 한다.
자바 이미지 인코딩 코드
- 이미지는 기본적으로 비트로 이루어져있다. 아래코드는 Base64 인코딩 방식을 통해 인코딩하는 과정인데 이는 8비트 단위로 끊어서 이를 String의 형태로 바꿔주는 방식이다.
- Base64 인코딩은 내부적으로는 바이트(8비트) 단위로 데이터를 나눈다 -> 8비트를 6비트로 변환 -> 변환된 비트를 아스키코드로 변환 과정을 거친다.
try {
// 이미지 파일을 바이트 배열로 읽어옴
Path path = Paths.get(imagePath);
byte[] imageBytes = Files.readAllBytes(path);
// 이미지 바이트 배열을 Base64로 인코딩
String base64Image = Base64.getEncoder().encodeToString(imageBytes);
} catch (IOException e) {
e.printStackTrace();
}
인코딩하여 데이터를 전송하는 이유는 아래와 같다.
- 1. 네트워크 대역폭 절약 : 8비트의 이진 데이터를 6비트로 묶어 문자열로 변환하므로 약간의 용량 증가가 있지만 텍스트 형식으로 전송 가능하므로 네트워크 대역폭을 절약하고 전송속도를 향상 시킬 수 있다.
- 2. 텍스트 기반 프로토콜 호환성
- 3. 데이터 전송의 안정성 : 일반적으로 문자열 데이터 전송이 안정성을 갖는다.
3. 서버에서 이미지 받고 디코딩
우선적으로, 테스팅시에는 인코딩안한 데이터를 받았기 때문에 인코딩 안한 데이터를 받는 방식과 인코딩한 데이터를 받는 방식으로 나누려고한다. 인코딩한 데이터를 String으로 받아서 디코딩 과정을 거치고 인코딩을 안거친 데이터는 다른 방식으로 받는다.
Spring에서 Multipart-form 받기
- 1. @ModelAttribute를 사용한 컨트롤러 메서드를 통해 Multipart-form 형식과 매핑된 클래스 받는다.
- 2. MultipartFile 클래스로 인코딩 되지 않은 이미지 파일을 받는다.
자바에서 인코딩된 이미지 디코딩 코드 예시
String encodedImage = "base64_encoded_image_here";
// Base64 디코딩
byte[] decodedBytes = Base64.getDecoder().decode(encodedImage);
4. 이미지 저장
이미지 저장시에는 보통 두가지 방식을 사용한다. 그대로 DB에 바이너리 파일을 저장하거나 파일 시스템으로 저장하거나 둘 중 하나이다.
각각의 장단점이 있지만 보통은 DB에 저장하기 보다는 아마존의 S3와 같은 서비스를 통해 파일 시스템으로 이미지를 저장한다.
이 후, DB에는 이미지경로를 저장하여 데이터를 출력시에는 해당 경로를 받아오고 그 경로에서 이미지를 받아 인코딩해서 클라이언트에 전송한다.
추가적으로, 파일 저장시에는 왜 바이너리로 저장하는지 알아보자.
- 1. 데이터 보존 : 데이터 인코딩 과정에서 발생할 수 있는 변형이나 데이터 손실의 가능성을 최소화 가능하다.
- 2. 효율적인 저장 공간 활용 : 인코딩된 데이터를 저장하는 경우에는 데이터의 크기가 증가하여 공간을 더 많이 차지한다.
파일 시스템 저장 예시 코드 (로컬 환경)
- Files.copy메서드를 통해 해당 위치에 저장한다.
- getInputStream()을 통해 바이트 스트림 형태로 데이터를 읽을 수 있다.
try {
File uploadDir = new File("TestDIR");
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
String filename = "TestName";
Path filePath = Path.of(DIR, filename+".png");
Files.copy(storeTableInfoDTO.getTableImage().getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
return "File upload failed";
}
'Server Development > Spring Basic' 카테고리의 다른 글
Spring - @Transactional (0) | 2023.04.25 |
---|---|
Spring - Spring Security (0) | 2023.04.23 |
Spring - Interceptor (0) | 2023.04.06 |
Spring - Jasypt (0) | 2023.04.06 |
Spring - Multiple Requests, Threads (0) | 2023.04.05 |