1. 개요
현재 개발 중인 서비스는 Gamification 요소를 잔뜩 넣은 어플리케이션입니다. 전반적인 요소가 픽셀 게임의 형태이기 때문에 사용자가 인증을 위해 찍은 사진도 픽셀로 변환하면 어떨까? 하는 아이디어가 나왔습니다.
처음에는 오픈 소스 라이브러리를 사용하려고 했는데요, 검색이 쉽지 않았습니다. 그러던 중 자바 표준 라이브러리를 사용해서 이미지를 픽셀로 변환할 수 있다는 걸 알게되었습니다.
오늘 포스팅에서는 사용자가 올린 사진을 픽셀로 변환하여 GCP Cloud Storage에 저장하는 과정을 정리해보도록 하겠습니다.
2. 진행 흐름
전반적인 흐름은 다음과 같습니다.
1.
사용자는 업로드할 이미지를 multipart/form-date 형태로 요청하고 서버는 이를 MultipartFile로 매핑한다
2.
서버는 요청받은 MultipartFile을 픽셀 이미지로 변환한다
3.
변환된 이미지를 GCP Cloud Storage에 저장한다
요청 DTO 클래스
public record PhotoQuestCompleteRequest(
@NotNull Long userId,
@NotNull Long questId,
@NotNull MultipartFile photo) {}
Java
복사
Controller
@PostMapping("/photo")
public ResponseEntity<Void> completePhotoQuest(
@RequestPart PhotoQuestCompleteRequest request) {
questService.completePhotoQuest(request);
return new ResponseEntity<>(HttpStatus.OK);
}
Java
복사
mutlipart/form-data를 받기 때문에 @RequestBody가 아닌 @RequestPart로 받아줍니다.
Service
private final PixelConverter pixelConverter;
private final Storage storage;
@Value("${spring.cloud.gcp.storage.bucket}")
private String bucketName;
public void completePhotoQuest(PhotoQuestCompleteRequest request) {
userPort.findById(request.userId())
.orElseThrow(() -> new UserNotFoundedException());
UserQuest userQuest = userQuestPort.findById(request.questId())
.orElseThrow(() -> new QuestNotExistedException());
String uuid = UUID.randomUUID().toString();
String type = request.photo().getContentType();
uploadPixelatedImage(request.photo(), 10, uuid, type);
userQuest.complete(uuid);
}
private void uploadPixelatedImage(MultipartFile file, int pixSize,
String uuid, String type) {
try {
BufferedImage img = pixelConverter.pixelateImage(file, pixSize);
// BufferedImage를 바이트 배열로 변환
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, "jpg", baos);
byte[] imgBytes = baos.toByteArray();
// 바이트 배열을 ByteArrayInputStream으로 변환
ByteArrayInputStream bais = new ByteArrayInputStream(imgBytes);
// 변환된 이미지 스트림을 사용하여 GCP Cloud Storage에 업로드
BlobInfo blobInfo = storage.create(
BlobInfo.newBuilder(bucketName, uuid)
.setContentType(type)
.build(),
bais // 이미지 데이터
);
} catch (IOException error) {
throw new IllegalArgumentException("Failed to upload pixelated image to GCP Cloud Storage");
}
}
Java
복사
Util 클래스
public BufferedImage pixelateImage(MultipartFile file, int pixSize) throws IOException {
// MultipartFile을 BufferedImage로 변환
BufferedImage img = ImageIO.read(new ByteArrayInputStream(file.getBytes()));
// Raster 데이터 가져오기
WritableRaster raster = img.getRaster();
// 이미지 픽셀화
for (int y = 0; y < img.getHeight(); y += pixSize) {
for (int x = 0; x < img.getWidth(); x += pixSize) {
double[] pixel = raster.getPixel(x, y, new double[3]);
for (int yd = y; (yd < y + pixSize) && (yd < img.getHeight()); yd++) {
for (int xd = x; (xd < x + pixSize) && (xd < img.getWidth()); xd++) {
raster.setPixel(xd, yd, pixel);
}
}
}
}
img.setData(raster);
return img;
}
Java
복사
Config 클래스
@Configuration
public class GoogleCloudStorageConfig {
@Bean
public Storage storage() throws IOException {
ClassPathResource resource = new ClassPathResource("gdscsunshine-88a74f12f21a.json");
GoogleCredentials credentials = GoogleCredentials.fromStream(resource.getInputStream());
String projectId = "gdscsunshine";
return StorageOptions.newBuilder()
.setProjectId(projectId)
.setCredentials(credentials)
.build()
.getService();
}
}
Java
복사
3. Cloud Storage 사용하기
1.
Google Console 이동
2.
Cloud Storage → 버킷 → 만들기 클릭
버킷 설정 정보는 다음과 같습니다.
3.
버킷 선택 후 권한 → 엑세스 권한 부여 클릭
다음과 같이 설정합니다.
버킷에 저장된 사진은 URL 형태로 노출될 수 있습니다.
하지만 중복되는 파일명을 업로드한다면 문제가 생길 수 있습니다. (파일 명 뒤에 추가적인 조치를 취해야 합니다)
4.
Key 파일 생성
서버에서 Cloud Storage에 접근할 때 사용할 Key를 만들어줍니다.
IAM 관리자 → 서비스 계정 → 서비스 계정 만들기 클릭
생성 후 서비스 계정 클릭 → 키 추가
5.
Spring Boot 설정
우선 의존성을 추가해줍니다.
build.gradle
implementation group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter', version: '1.2.5.RELEASE'
implementation group: 'org.springframework.cloud', name: 'spring-cloud-gcp-storage', version: '1.2.5.RELEASE'
Java
복사
json 파일을 resources 패키지 안에 위치하고 application.yml에 다음을 추가합니다.
spring:
cloud:
gcp:
storage:
credentials:
location:
classpath:[key 파일].json
project-id: [project-id]
bucket: [bucket name]
Java
복사