DB를 거치지 않고 이미지를 S3에 저장하는 법이 있다고?

Halo·2025년 6월 19일
0

JAVA/Spring

목록 보기
13/15
post-thumbnail

👀 과정

0. Presigned URL이란?

PreSigned Url은 말 그대로 미리 서명한 Url이라는 뜻이다. S3의 소유자가 미리 특정 권한(파일 업로드, 파일 다운로드 등)에 대해 서명을 해준 뒤 사용자에게 해당 Url을 제공해준다. 사용자는 서명된 권한을 사용할 수 있다. 소유자가 여러 정보에 대해 미리 서명을 해두고, 사용자가 추후에 전달받아 사용한다는 점에서 수표와 비슷하다.

PreSigned Url을 사용하게 된다면, 클라이언트가 서버에 거치지 않고 파일을 저장소에 직접 업로드할 수 있어, 서버의 자원을 절약할 수 있다.

사용자는 이미지를 S3에 올리기 위해 S3의 Presigned URL이 필요하다.

1. 클라이언트가 서버에 Presigend URL 요청

let presigned_url = await fetch("/item/presigned-url?filename=" + name);

2. 서버는 S3에 Presigned URL 요청

public String createPresignedUrl(String path) {
    var putObjectRequest = PutObjectRequest.builder()
           .bucket(bucket)  // 올릴 버킷명
           .key(path)       // 경로
           .build();
    var preSignRequest = PutObjectPresignRequest.builder()
           .signatureDuration(Duration.ofMinutes(3)) //url 유효기간
           .putObjectRequest(putObjectRequest)
           .build();
    return s3Presigner.presignPutObject(preSignRequest).url().toString(); //presigned url Return
 }

3. S3는 서버에 Presigned URL 제공
S3에서 주는데 http:~&a:b 이런식으로 이미지경로 + 쿼리스트링 형태로 제공해준다.

4. 서버는 클라이언트에 Presigned URL 제공

let presigned_url = await fetch("/item/presigned-url?filename=" + name);

1번에한 Get요청의 return 값(presigned_url)으로 받는다.

5. 클라이언트는 S3 Presigned URL에 Put 요청 보내서이미지 저장

let res = await fetch(presigned_url, {
        method: "PUT",
        body: file
    });

이렇게 Put요청을 presigend_url로 보내면 이미지가 저장된다.

6. DB에는 S3에 저장된 이미지 경로 저장

사용자 클라이언트에 image_url에 S3이미지 경로(presigned_url에서 쿼리스트링을 뺀)로 보내주고 item게시 포스트 요청시 해당 url을 서버에 보내 이미지 URL을 DB에 저장되게 하였다.

const url = presigned_url.split("?")[0];    // 해당 url에서 쿼리스트링 뺀거
document.getElementById("imgUrl").value = url;

아래는 add api


@PostMapping("/add")
    String add(String title, int price, String imgUrl ,Model model, Authentication auth ) {
        String usrid=auth.getName();
        boolean result = itemService.SavaItem(title, price, model,usrid, imgUrl);


        System.out.println(imgUrl);
        if (!result) {
            return "item/write"; // 실패 시 다시 입력페이지로
        }
        return "redirect:/item/list"; // 성공 시 리스트로

    }


💁🏻 사용하는 이유

가. 서버 부담 감소
이미지를 직접 서버에 보내지 않아도 된다. 클라이언트에서 Put 요청을 통해 바로 S3에 올리는 형태이기 때문이다.

대신 DB는 S3에 저장된 이미지의 URL만 가지고 있음.


😏 기타

가. fetch(/url?쿼리파라미터) Get 요청
위 함수를 사용하면 서버에 Get 요청을 보낼 수 있다.

url 을 가진 서버 api에 get요청을 보내고 파라미터로 ? 뒤에 오는 쿼리파라미터를 보낸다.

나. @Value("${spring.cloud.aws.s3.bucket}")
application.properties에 있는 버킷명 가져와서 아래 버킷 변수에 넣어달라는 Spring 어노테이션

@Value("${spring.cloud.aws.s3.bucket}") //application.properties에 있는 버킷명 가져와서 아래 버킷 변수에 넣어달라는 어노테이션
  private String bucket;
  private final S3Presigner

📝 느낀점

서버의 부담이 상당히 감소하는 것을 느낄 수 있었다.


🛠️ 환경

항목Env Info
🖥️ 서버Tomcat
🍭 프레임워크Spring Boot
📀 데이터베이스MySQL with Azure
📝 JPAHibernate
🙈 외부 라이브러리lombok, thymeleaf, AJAX
📏 디자인 패턴MVC
profile
새끼 고양이 키우고 싶다

0개의 댓글