클라우드 S3가이드 - 100-hours-a-week/16-Hot6-wiki GitHub Wiki

S3 연동 요청 및 제공 가이드

데이터베이스에 UUID로 저장된 S3 이미지 파일 조회 흐름.

기본 흐름

  1. 이미지를 업로드할 때:

    • UUID를 생성 (예: abc123-uuid.jpg)
    • S3에 abc123-uuid.jpg 이름으로 업로드
    • 이 UUID를 DB에 저장 (컬럼에 저장)
  2. 이미지를 조회할 때:

    • DB에서 UUID를 조회 (예: 게시글 id로 이미지 UUID를 가져옴)
    • S3 URL에 UUID를 붙여 최종 이미지 URL 생성하고 조회 가능.

예시 코드 흐름 (Java 기준)

1. 업로드 시

UUID uuid = UUID.randomUUID();
String filename = uuid.toString() + ".jpg";
amazonS3.putObject(bucketName, filename, file);
saveToDatabase(uuid.toString()); // DB에 저장

2. 조회 시

String uuid = getImageUuidFromDB(postId); // DB에서 가져오기
String imageUrl = "https://" + onthe-top-s3-example + ".s3.amazonaws.com/" + uuid + ".jpg"; // onthe-top-s3exampe = 저희의 임시 bucket이름입니다.

> CloudFront를 쓴다면:
```java
String imageUrl = "https://dev.onthe-top.com/" + uuid + ".jpg";

정리

항목 설명
저장 UUID를 S3 object key로 사용
DB 저장 UUID만 저장 (image_uuid 컬럼 등)
조회 UUID 기반으로 S3 URL 생성

🧩 React-Spring Boot 연동 예시

React(예시)

1. 업로드 예시

const formData = new FormData();
formData.append("file", fileInput.files[0]);

fetch("http://localhost:8080/s3/upload", {
  method: "POST",
  body: formData,
})
  .then((res) => res.text())
  .then((msg) => alert(msg));

Spring(예시)

1. dependency 의존성 추가

// AWS S3(gradle)
dependencies {
    implementation 'com.amazonaws:aws-java-sdk-s3:1.12.260'
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

2. application.properties 설정

버킷이름과 잘 복사해온 액세스와 시크릿키, 그리고 지역을 넣어주세요. 시크릿키와 액세스키를 노출시키면 안됩니다.

#AWS S3 관련 설정
cloud.aws.s3.bucketName=BUCKET_NAME(onthe-top)
cloud.aws.credentials.accessKey=ACCESS_KEY(디코에...)
cloud.aws.credentials.secretKey=SECRET_KEY(디코에...)
cloud.aws.region.static=ap-northeast-2
cloud.aws.stack.auto=false

3. S3Config.java

역할: AWS S3 클라이언트 설정을 위한 Bean 등록

package com.example.demo.config;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class S3Config {

    @Value("${aws.accessKeyId}")
    private String awsAccessKeyId;

    @Value("${aws.secretKey}")
    private String awsSecretKey;

    @Value("${aws.region}")
    private String awsRegion;

    @Bean
    public AmazonS3 amazonS3() {
        BasicAWSCredentials awsCredentials = new BasicAWSCredentials(awsAccessKeyId, awsSecretKey);
        return AmazonS3ClientBuilder.standard()
                .withRegion(awsRegion)
                .withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
                .build();
    }
}

4. S3Service.java

비즈니스 로직(파일 업로드/다운로드) 처리

package com.example.demo.service;

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;

import java.net.URL;
import java.util.Date;
import java.util.UUID;



@Service
public class S3Service {

    private final AmazonS3 amazonS3;
    
    @Value("${aws.s3.bucket}")
    private String bucketName;

    public S3Service(AmazonS3 amazonS3) {
        this.amazonS3 = amazonS3;
    }

    // Upload file to S3 bucket

    public String uploadFile(File file) {
        try {
            String originalName = file.getName();
            String extension = "";

            int dotIndex = originalName.lastIndexOf(".");
            if (dotIndex != -1) {
                extension = originalName.substring(dotIndex); // 확장자 포함 (.jpg 등)
            }

            String uniqueFileName = "assets/images/" + UUID.randomUUID().toString() + extension;

            amazonS3.putObject(new PutObjectRequest(bucketName, uniqueFileName, file));

            return "File uploaded successfully: " + uniqueFileName;
        } catch (Exception e) {
            e.printStackTrace();
            return "Error uploading file";
        }
    }
}

5. S3Controller.java

HTTP 요청 (REST API) 처리

package com.example.demo.controller;

import com.example.demo.service.S3Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;

@RestController
@RequestMapping("/s3")
public class S3Controller {

    @Autowired
    private S3Service s3Service;

    // Upload a file to S3
    @PostMapping("/upload")
    public String uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
        File convFile = new File(file.getOriginalFilename());
        file.transferTo(convFile); // Convert MultipartFile to File
        return s3Service.uploadFile(convFile);
    }
}

간이 테스트

curl -F "[email protected]" http://localhost:8080/s3/upload