From 198bfe4d69fef642afaba38cf642294d5d10e965 Mon Sep 17 00:00:00 2001 From: Baguette-bbang Date: Sun, 6 Jul 2025 05:31:57 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B5=AC=EA=B8=80=20=EB=93=9C=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=20API=20=ED=86=B5=ED=95=A9=20=EB=B0=8F=20?= =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=EC=84=A4=EC=A0=95=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 구글 드라이브 파일 업로드 시스템 구현 - 향수 생성 시 자동 파일 업로드 및 URL 업데이트 - iOS 다운로드용 URL 형태로 변경 (/uc?export=download&id=) - 배포 파이프라인에 구글 서비스 계정 키 설정 추가 - application-prod.yml에 구글 드라이브 설정 추가 - gitignore에 서비스 계정 키 파일 추가 --- .github/workflows/dev_deploy.yml | 5 + .gitignore | 6 +- build.gradle | 5 + .../file/controller/FileUploadController.java | 95 ++++++ .../domain/file/dto/FileUploadResponse.java | 28 ++ .../file/service/GoogleDriveService.java | 312 ++++++++++++++++++ .../perfume/service/PerfumeGptService.java | 30 +- .../perfume/service/PerfumeService.java | 60 +--- .../umc/global/config/GoogleDriveConfig.java | 44 +++ src/main/resources/application.yml | 7 + 10 files changed, 548 insertions(+), 44 deletions(-) create mode 100644 src/main/java/com/umc/domain/file/controller/FileUploadController.java create mode 100644 src/main/java/com/umc/domain/file/dto/FileUploadResponse.java create mode 100644 src/main/java/com/umc/domain/file/service/GoogleDriveService.java create mode 100644 src/main/java/com/umc/global/config/GoogleDriveConfig.java diff --git a/.github/workflows/dev_deploy.yml b/.github/workflows/dev_deploy.yml index 335fec7..412b8ba 100644 --- a/.github/workflows/dev_deploy.yml +++ b/.github/workflows/dev_deploy.yml @@ -27,6 +27,11 @@ jobs: echo "${{ secrets.APPLICATION_PROD_YML }}" > ./src/main/resources/application-prod.yml shell: bash + - name: Generate Google Service Account Key + run: | + echo "${{ secrets.GOOGLE_SERVICE_ACCOUNT_KEY }}" > ./src/main/resources/jovial-monument-432709-s0-5455a7338d58.json + shell: bash + - name: Grant permission to gradlew run: chmod +x gradlew diff --git a/.gitignore b/.gitignore index 625111a..a0cf3f2 100644 --- a/.gitignore +++ b/.gitignore @@ -85,4 +85,8 @@ node_modules/ ### Application specific ### application-dev.yml application-prod.yml -application-local.yml \ No newline at end of file +application-local.yml + +### Google Service Account Keys ### +jovial-monument-432709-s0-5455a7338d58.json +*service-account*.json \ No newline at end of file diff --git a/build.gradle b/build.gradle index 8431070..0c6711c 100644 --- a/build.gradle +++ b/build.gradle @@ -65,6 +65,11 @@ dependencies { // Jackson JSR310 모듈 (LocalDateTime 직렬화 지원) implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' + // Google Drive API 의존성 + implementation 'com.google.apis:google-api-services-drive:v3-rev20220815-2.0.0' + implementation 'com.google.auth:google-auth-library-oauth2-http:1.19.0' + implementation 'com.google.http-client:google-http-client-jackson2:1.43.3' + } tasks.named('test') { diff --git a/src/main/java/com/umc/domain/file/controller/FileUploadController.java b/src/main/java/com/umc/domain/file/controller/FileUploadController.java new file mode 100644 index 0000000..fc7de60 --- /dev/null +++ b/src/main/java/com/umc/domain/file/controller/FileUploadController.java @@ -0,0 +1,95 @@ +package com.umc.domain.file.controller; + +import com.umc.domain.file.dto.FileUploadResponse; +import com.umc.domain.file.service.GoogleDriveService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/files") +@RequiredArgsConstructor +@Slf4j +public class FileUploadController { + + private final GoogleDriveService googleDriveService; + + /** + * 파일 업로드 + */ + @PostMapping("/upload/{recordId}") + public ResponseEntity uploadFile( + @PathVariable String recordId, + @RequestParam("file") MultipartFile file) { + + try { + if (file.isEmpty()) { + return ResponseEntity.badRequest().body("파일이 비어있습니다."); + } + + FileUploadResponse response = googleDriveService.uploadFile(file, recordId); + + return ResponseEntity.ok(Map.of( + "success", true, + "data", response + )); + + } catch (IOException e) { + log.error("파일 업로드 실패", e); + return ResponseEntity.internalServerError().body(Map.of( + "success", false, + "error", "파일 업로드에 실패했습니다: " + e.getMessage() + )); + } + } + + /** + * 레코드의 모든 파일 조회 + */ + @GetMapping("/record/{recordId}") + public ResponseEntity getRecordFiles(@PathVariable String recordId) { + try { + Map> files = googleDriveService.getRecordFiles(recordId); + + return ResponseEntity.ok(Map.of( + "success", true, + "data", files + )); + + } catch (IOException e) { + log.error("파일 조회 실패", e); + return ResponseEntity.internalServerError().body(Map.of( + "success", false, + "error", "파일 조회에 실패했습니다: " + e.getMessage() + )); + } + } + + /** + * 파일 삭제 + */ + @DeleteMapping("/{fileId}") + public ResponseEntity deleteFile(@PathVariable String fileId) { + try { + googleDriveService.deleteFile(fileId); + + return ResponseEntity.ok(Map.of( + "success", true, + "message", "파일이 성공적으로 삭제되었습니다." + )); + + } catch (IOException e) { + log.error("파일 삭제 실패", e); + return ResponseEntity.internalServerError().body(Map.of( + "success", false, + "error", "파일 삭제에 실패했습니다: " + e.getMessage() + )); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/umc/domain/file/dto/FileUploadResponse.java b/src/main/java/com/umc/domain/file/dto/FileUploadResponse.java new file mode 100644 index 0000000..e780c36 --- /dev/null +++ b/src/main/java/com/umc/domain/file/dto/FileUploadResponse.java @@ -0,0 +1,28 @@ +package com.umc.domain.file.dto; + +import lombok.Data; + +@Data +public class FileUploadResponse { + private String fileId; + private String fileName; + private String publicUrl; + private String directUrl; + private String recordId; + private String fileType; + private Long fileSize; + private String mimeType; + + public FileUploadResponse(String fileId, String fileName, String publicUrl, + String directUrl, String recordId, String fileType, + Long fileSize, String mimeType) { + this.fileId = fileId; + this.fileName = fileName; + this.publicUrl = publicUrl; + this.directUrl = directUrl; + this.recordId = recordId; + this.fileType = fileType; + this.fileSize = fileSize; + this.mimeType = mimeType; + } +} \ No newline at end of file diff --git a/src/main/java/com/umc/domain/file/service/GoogleDriveService.java b/src/main/java/com/umc/domain/file/service/GoogleDriveService.java new file mode 100644 index 0000000..df77ce0 --- /dev/null +++ b/src/main/java/com/umc/domain/file/service/GoogleDriveService.java @@ -0,0 +1,312 @@ +package com.umc.domain.file.service; + +import com.umc.domain.file.dto.FileUploadResponse; +import com.google.api.client.http.FileContent; +import com.google.api.services.drive.Drive; +import com.google.api.services.drive.model.File; +import com.google.api.services.drive.model.FileList; +import com.google.api.services.drive.model.Permission; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.HashMap; + +@Service +@RequiredArgsConstructor +@Slf4j +public class GoogleDriveService { + + private final Drive driveService; + + @Value("${google.drive.parent-folder-id}") + private String parentFolderId; + + private static final Map MIME_TYPES = Map.ofEntries( + // 오디오 파일 + Map.entry("mp3", "audio/mpeg"), + Map.entry("wav", "audio/wav"), + Map.entry("m4a", "audio/mp4"), + Map.entry("aac", "audio/aac"), + Map.entry("ogg", "audio/ogg"), + Map.entry("flac", "audio/flac"), + // 비디오 파일 + Map.entry("mp4", "video/mp4"), + Map.entry("avi", "video/x-msvideo"), + Map.entry("mov", "video/quicktime"), + Map.entry("wmv", "video/x-ms-wmv"), + Map.entry("flv", "video/x-flv"), + Map.entry("webm", "video/webm"), + // 이미지 파일 + Map.entry("jpg", "image/jpeg"), + Map.entry("jpeg", "image/jpeg"), + Map.entry("png", "image/png"), + Map.entry("gif", "image/gif"), + Map.entry("webp", "image/webp"), + Map.entry("bmp", "image/bmp"), + Map.entry("svg", "image/svg+xml") + ); + + /** + * 파일 업로드 메인 메서드 + */ + public FileUploadResponse uploadFile(MultipartFile multipartFile, String recordId) throws IOException { + // 임시 파일 생성 + Path tempFile = createTempFile(multipartFile); + + try { + // 파일 타입 결정 + String fileType = determineFileType(multipartFile.getContentType()); + + // 폴더 생성 또는 찾기 + String folderId = findOrCreateFolder(recordId, fileType); + + // 파일 업로드 + String fileId = uploadToGoogleDrive(tempFile, multipartFile.getOriginalFilename(), + multipartFile.getContentType(), folderId); + + // 공개 권한 설정 + makeFilePublic(fileId); + + // URL 생성 - iOS에서 직접 다운로드 가능한 URL 사용 + String publicUrl = "https://drive.google.com/uc?export=download&id=" + fileId; + String directUrl = "https://drive.google.com/uc?export=download&id=" + fileId; + + return new FileUploadResponse( + fileId, + multipartFile.getOriginalFilename(), + publicUrl, + directUrl, + recordId, + fileType, + multipartFile.getSize(), + multipartFile.getContentType() + ); + + } finally { + // 임시 파일 삭제 + Files.deleteIfExists(tempFile); + } + } + + /** + * 폴더 찾기 또는 생성 + */ + private String findOrCreateFolder(String recordId, String fileType) throws IOException { + String folderName = recordId + "_" + fileType; + + try { + // 기존 폴더 검색 + String query; + if ("root".equals(parentFolderId)) { + query = String.format("name='%s' and mimeType='application/vnd.google-apps.folder' and 'root' in parents", folderName); + } else { + query = String.format("name='%s' and mimeType='application/vnd.google-apps.folder' and '%s' in parents", + folderName, parentFolderId); + } + + FileList result = driveService.files().list() + .setQ(query) + .setFields("files(id, name)") + .execute(); + + List files = result.getFiles(); + if (files != null && !files.isEmpty()) { + log.info("기존 폴더 찾음: {} (ID: {})", folderName, files.get(0).getId()); + return files.get(0).getId(); + } + + // 폴더 생성 + return createFolder(folderName); + + } catch (Exception e) { + log.error("폴더 찾기/생성 실패: {}", e.getMessage()); + throw new IOException("폴더 생성에 실패했습니다: " + e.getMessage(), e); + } + } + + /** + * 폴더 생성 + */ + private String createFolder(String folderName) throws IOException { + try { + File fileMetadata = new File(); + fileMetadata.setName(folderName); + fileMetadata.setMimeType("application/vnd.google-apps.folder"); + + // 부모 폴더 설정 + if ("root".equals(parentFolderId)) { + fileMetadata.setParents(Collections.singletonList("root")); + } else { + fileMetadata.setParents(Collections.singletonList(parentFolderId)); + } + + File file = driveService.files().create(fileMetadata) + .setFields("id") + .execute(); + + log.info("폴더 생성 완료: {} (ID: {})", folderName, file.getId()); + return file.getId(); + + } catch (Exception e) { + log.error("폴더 생성 실패: {}, 오류: {}", folderName, e.getMessage()); + throw new IOException("폴더 생성에 실패했습니다: " + e.getMessage(), e); + } + } + + /** + * 구글 드라이브에 파일 업로드 + */ + private String uploadToGoogleDrive(Path filePath, String fileName, String mimeType, String folderId) throws IOException { + File fileMetadata = new File(); + fileMetadata.setName(fileName); + fileMetadata.setParents(Collections.singletonList(folderId)); + + java.io.File uploadFile = filePath.toFile(); + FileContent mediaContent = new FileContent(mimeType, uploadFile); + + File file = driveService.files().create(fileMetadata, mediaContent) + .setFields("id") + .execute(); + + log.info("파일 업로드 완료: {} (ID: {})", fileName, file.getId()); + return file.getId(); + } + + /** + * 파일을 공개적으로 접근 가능하게 설정 + */ + private void makeFilePublic(String fileId) throws IOException { + Permission permission = new Permission(); + permission.setType("anyone"); + permission.setRole("reader"); + + driveService.permissions().create(fileId, permission).execute(); + log.info("파일 공개 권한 설정 완료: {}", fileId); + } + + /** + * 임시 파일 생성 + */ + private Path createTempFile(MultipartFile multipartFile) throws IOException { + String originalFilename = multipartFile.getOriginalFilename(); + String extension = originalFilename != null ? + originalFilename.substring(originalFilename.lastIndexOf('.')) : ".tmp"; + + Path tempFile = Files.createTempFile("upload_", extension); + multipartFile.transferTo(tempFile.toFile()); + + return tempFile; + } + + /** + * 파일 타입 결정 + */ + private String determineFileType(String mimeType) { + if (mimeType != null && mimeType.startsWith("audio/")) { + return "audio"; + } else if (mimeType != null && mimeType.startsWith("video/")) { + return "video"; + } else if (mimeType != null && mimeType.startsWith("image/")) { + return "image"; + } + return "unknown"; + } + + /** + * 레코드의 모든 파일 조회 + */ + public Map> getRecordFiles(String recordId) throws IOException { + Map> result = new HashMap<>(); + result.put("audio", new ArrayList<>()); + result.put("video", new ArrayList<>()); + result.put("image", new ArrayList<>()); + + // 오디오 폴더 파일 조회 + String audioFolderId = findFolder(recordId, "audio"); + if (audioFolderId != null) { + result.put("audio", getFilesInFolder(audioFolderId, recordId, "audio")); + } + + // 비디오 폴더 파일 조회 + String videoFolderId = findFolder(recordId, "video"); + if (videoFolderId != null) { + result.put("video", getFilesInFolder(videoFolderId, recordId, "video")); + } + + // 이미지 폴더 파일 조회 + String imageFolderId = findFolder(recordId, "image"); + if (imageFolderId != null) { + result.put("image", getFilesInFolder(imageFolderId, recordId, "image")); + } + + return result; + } + + /** + * 폴더 찾기 + */ + private String findFolder(String recordId, String fileType) throws IOException { + String folderName = recordId + "_" + fileType; + String query = String.format("name='%s' and mimeType='application/vnd.google-apps.folder'", folderName); + + FileList result = driveService.files().list() + .setQ(query) + .setFields("files(id, name)") + .execute(); + + List files = result.getFiles(); + return (files != null && !files.isEmpty()) ? files.get(0).getId() : null; + } + + /** + * 폴더 내 파일 목록 조회 + */ + private List getFilesInFolder(String folderId, String recordId, String fileType) throws IOException { + String query = String.format("'%s' in parents", folderId); + + FileList result = driveService.files().list() + .setQ(query) + .setFields("files(id, name, mimeType, size, createdTime)") + .execute(); + + List files = new ArrayList<>(); + if (result.getFiles() != null) { + for (File file : result.getFiles()) { + String publicUrl = "https://drive.google.com/uc?export=download&id=" + file.getId(); + String directUrl = "https://drive.google.com/uc?export=download&id=" + file.getId(); + + files.add(new FileUploadResponse( + file.getId(), + file.getName(), + publicUrl, + directUrl, + recordId, + fileType, + file.getSize(), + file.getMimeType() + )); + } + } + + return files; + } + + /** + * 파일 삭제 + */ + public void deleteFile(String fileId) throws IOException { + driveService.files().delete(fileId).execute(); + log.info("파일 삭제 완료: {}", fileId); + } +} \ No newline at end of file diff --git a/src/main/java/com/umc/domain/perfume/service/PerfumeGptService.java b/src/main/java/com/umc/domain/perfume/service/PerfumeGptService.java index cbdcf8e..beba748 100644 --- a/src/main/java/com/umc/domain/perfume/service/PerfumeGptService.java +++ b/src/main/java/com/umc/domain/perfume/service/PerfumeGptService.java @@ -4,6 +4,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.umc.domain.perfume.entity.SourceType; import com.umc.domain.perfume.entity.Perfume; +import com.umc.domain.file.dto.FileUploadResponse; +import com.umc.domain.file.service.GoogleDriveService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -15,6 +17,7 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -26,6 +29,7 @@ public class PerfumeGptService { private final RestTemplate restTemplate; private final ObjectMapper objectMapper; + private final GoogleDriveService googleDriveService; @Value("${openai.api.key}") private String apiKey; @@ -41,7 +45,31 @@ public class PerfumeGptService { */ public Perfume generatePerfume(SourceType sourceType, String url, MultipartFile file) { String description = generatePerfumeDescription(sourceType, file); - return createPerfumeEntity(sourceType, url, description); + Perfume perfume = createPerfumeEntity(sourceType, url, description); + + return perfume; + } + + /** + * 향수 생성 후 파일 업로드 및 URL 업데이트 + */ + public void uploadFileAndUpdateUrl(Perfume perfume, MultipartFile file) throws IOException { + try { + log.info("파일 업로드 시작 - 향수 ID: {}, 파일명: {}", perfume.getId(), file.getOriginalFilename()); + + String recordId = perfume.getId().toString(); + FileUploadResponse fileResponse = googleDriveService.uploadFile(file, recordId); + + // 향수 엔티티의 URL을 구글 드라이브 URL로 업데이트 + String oldUrl = perfume.getUrl(); + perfume.setUrl(fileResponse.getPublicUrl()); + + log.info("파일 업로드 완료: {} - URL 변경: {} -> {}", + fileResponse.getFileName(), oldUrl, fileResponse.getPublicUrl()); + } catch (Exception e) { + log.error("파일 업로드 실패 - 향수 ID: {}, 오류: {}", perfume.getId(), e.getMessage(), e); + throw e; // 상위로 예외 전파 + } } /** diff --git a/src/main/java/com/umc/domain/perfume/service/PerfumeService.java b/src/main/java/com/umc/domain/perfume/service/PerfumeService.java index 307995c..ac54ed3 100644 --- a/src/main/java/com/umc/domain/perfume/service/PerfumeService.java +++ b/src/main/java/com/umc/domain/perfume/service/PerfumeService.java @@ -40,18 +40,28 @@ public PerfumeResponseDto createPerfume(SourceType sourceType, MultipartFile fil // 1. 파일 유효성 검증 validateFile(file, sourceType); - // 2. 가상 URL 생성 (실제 파일 저장 없음) - String virtualFileUrl = generateVirtualFileUrl(file); + // 2. GPT를 통한 향수 정보 생성 (임시 URL 사용) + String tempUrl = "/temp/" + UUID.randomUUID().toString(); + Perfume perfume = perfumeGptService.generatePerfume(sourceType, tempUrl, file); - // 3. GPT를 통한 향수 정보 생성 (파일 직접 전달) - Perfume perfume = perfumeGptService.generatePerfume(sourceType, virtualFileUrl, file); - - // 4. 검증된 사용자 정보 설정 + // 3. 검증된 사용자 정보 설정 perfume.setUser(existingUser); - // 5. 데이터베이스에 저장 + // 4. 데이터베이스에 저장 (ID 생성을 위해) Perfume savedPerfume = perfumeRepository.save(perfume); + // 5. 파일을 구글 드라이브에 업로드하고 URL 업데이트 + try { + perfumeGptService.uploadFileAndUpdateUrl(savedPerfume, file); + log.info("파일 업로드 및 URL 업데이트 완료 - 향수 ID: {}", savedPerfume.getId()); + } catch (Exception e) { + log.error("파일 업로드 실패 - 향수 ID: {}, 오류: {}", savedPerfume.getId(), e.getMessage()); + // 파일 업로드 실패해도 향수는 생성됨 (임시 URL 유지) + } + + // 6. 업데이트된 URL로 데이터베이스 저장 + savedPerfume = perfumeRepository.save(savedPerfume); + log.info("향수 생성 완료 - ID: {}, 사용자: {}, 타입: {}", savedPerfume.getId(), existingUser.getNickname(), sourceType); @@ -101,41 +111,7 @@ private void validateFile(MultipartFile file, SourceType sourceType) { contentType); } - /** - * 가상 파일 URL 생성 (실제 파일 저장 없음) - */ - private String generateVirtualFileUrl(MultipartFile file) { - try { - // 파일명 생성 (UUID + 원본 확장자) - String originalFilename = file.getOriginalFilename(); - String extension = ""; - - if (originalFilename != null && originalFilename.contains(".")) { - extension = originalFilename.substring(originalFilename.lastIndexOf(".")); - } else { - // 파일 형식에 따른 기본 확장자 설정 - String contentType = file.getContentType(); - if (contentType != null) { - if (contentType.startsWith("audio/")) { - extension = ".mp3"; - } else if (contentType.startsWith("image/")) { - extension = ".jpg"; - } - } else { - extension = ".tmp"; - } - } - - String filename = UUID.randomUUID().toString() + extension; - - // 가상 URL 반환 (실제 파일 저장 없음) - return "/virtual/" + filename; - - } catch (Exception e) { - log.warn("파일 URL 생성 중 오류 발생: {}", e.getMessage()); - return "/virtual/" + UUID.randomUUID().toString() + ".tmp"; - } - } + /** * 향수 조회 diff --git a/src/main/java/com/umc/global/config/GoogleDriveConfig.java b/src/main/java/com/umc/global/config/GoogleDriveConfig.java new file mode 100644 index 0000000..a818420 --- /dev/null +++ b/src/main/java/com/umc/global/config/GoogleDriveConfig.java @@ -0,0 +1,44 @@ +package com.umc.global.config; + +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.gson.GsonFactory; +import com.google.api.services.drive.Drive; +import com.google.api.services.drive.DriveScopes; +import com.google.auth.http.HttpCredentialsAdapter; +import com.google.auth.oauth2.GoogleCredentials; +import com.google.auth.oauth2.ServiceAccountCredentials; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Collections; + +@Configuration +public class GoogleDriveConfig { + + @Value("${google.drive.service-account-key-path}") + private Resource serviceAccountKeyPath; + + @Value("${google.drive.application-name}") + private String applicationName; + + private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); + + @Bean + public Drive driveService() throws IOException, GeneralSecurityException { + final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport(); + + GoogleCredentials credentials = ServiceAccountCredentials + .fromStream(serviceAccountKeyPath.getInputStream()) + .createScoped(Collections.singleton(DriveScopes.DRIVE_FILE)); + + return new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, new HttpCredentialsAdapter(credentials)) + .setApplicationName(applicationName) + .build(); + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 36d35c8..d0171cb 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -31,3 +31,10 @@ server: port: 8080 jwt: secret: my-super-secret-jwt-key-that-is-very-long-256bit + +# Google Drive 설정 +google: + drive: + service-account-key-path: classpath:jovial-monument-432709-s0-5455a7338d58.json + parent-folder-id: root + application-name: UMC-Hackathon-Spring