본문 바로가기
junior developer :)/React

[react] aws S3 이용하여 이미지 업로드(presigned URL/프론트)

by ㅁ윤슬ㅁ 2023. 6. 12.
728x90
반응형

이번 프로젝트에서 상품 이미지를 업로드 해야할 일이 있었는데, 이미지 업로드의 방법으로 aws S3 버킷에 presigned URL을 이용하여 업로드를 하기로 결정되었다.

프론트엔드에서만 업로드 하는 방법도 있지만, 보안 상의 이유로 presigned URL을 백엔드에서 발급받아 관리 하는 것이 더 좋다고 생각했기 때문이다.

presigned URL의 경우 S3에 파일을 업로드 하기 위해 필요한 권한을 보유한 URL이다.
모든 객체 및 버킷은 기본적으로 비공개 상태인데, 객체를 업로드하기 위해 미리 서명된 URL을 수신하는 경우 미리 서명된 URL의 생성자가 해당 객체를 업로드 하는데 필요한 권한을 보유하는 경우에만 객체를 업로드 할 수 있다. 즉, 해당 S3에 대한 접근 권한을 인증해 주는 URL이라고 생각하면 편하다.

업로드 구현영상

presigned URL을 이용한 이미지 업로드는 아래와 같은 순서로 이루어진다.

1. S3 버킷 생성 
2. [프론트] POST요청을 통해서 presigned URL 생성을 위한 요청을 보냄
3. [백] 프론트에서 버킷 정보와 파일 정보를 받아 AWS S3에 presigned URL 요청(POST)
4.  [AWS] presigned URL 백엔드에 전달, 백엔드에서는 프론트로 전달
5.  [프론트] 받아온 presigned URL에 이미지 업로드 요청 보내기(PUT)

1단계. S3 버킷 생성 단계에서 주의해야 할 점은 

생각보다 IAM이나 S3 버킷 설정을 잘못해서 생기는 오류도 많으니,, 허용하는 메소드, allow origin등 정확하게 써주기 !!
s3정책 추가, IAM 사용자의 접근 권한, cors 설정을 다 진행해야 한다.

2,3단계. 사진을 FormData 형태로 전달해 presigned URL 받아오기

presigned URL

 formData로 감싼 파일을 POST로 보내 준 뒤 받아온 값을 uploadImageToS3 함수에 전달해주었다 (하단 코드 참고)

4,5 단계. 받은 presigned URL에 파일 정보 전달

uploadFile을 console에 찍어본 값

위 파일 형태의 값을 presigened URL에 담아 PUT요청을 보내면 아래와 같은 200코드가 나오면서 성공 !

PUT 성공 후 확인할 수 있는 response값

 


전체코드는 아래와 같다

 
  const submitImage = (e: any) => {
    e.preventDefault();
    const file = e.target.files[0];
    setSelectedFile(file);

// 선택된 이미지 미리보기
    const reader = new FileReader();
    reader.onload = () => {
      setImageUrl(reader.result);
    };
    reader.readAsDataURL(file);
    if (e.target.files) {
      const uploadFile = e.target.files[0];
      const formData = new FormData();
      formData.append("itemPicture", uploadFile);
      console.log(formData.getAll("itemPicture"));

      axios
        .post(`http://.../items/upload`, formData, {
          headers: {
            "content-type": "multipart/form-data",
            Authorization: localStorage.getItem("accessToken"),
          },
        })
        .then((res) => {
          const presignedUrl = res.data.data;
              console.log(presignedUrl);
          setImgFile(res.data.data);
          uploadImageToS3(presignedUrl, uploadFile)
          console.log(res.data.data);
        })
        .catch((err) => {
          console.log(err);
        });
    }
  };


function uploadImageToS3(presignedUrl: string, uploadFile: File) {
    console.log(uploadFile);
    setUploadedFile(uploadFile.name)
    console.log(uploadedFile) // 업로드할 파일 확인
  
    axios
      .put(presignedUrl, uploadFile, {
        headers: {
          'Content-Type': 'image/png', // 업로드할 파일의 콘텐츠 유형 지정
        },
      })
      .then((response) => console.log(response))
      .catch((error) => console.error(error));
  }

return (
...
<img
          src={imageUrl === null ? <S.Image /> : imageUrl}
          alt="프로필 이미지"
          style={{ width: "40%", borderRadius: "20px" }}
        /> // 이미지 미리보기
<input
          type="file"
          accept="image/*"
          name="goods_img"
          id="productImg"
          onChange={submitImage}
          ref={imgRef}
        />
        )

사진을 포함한 게시글 등록 POST요청까지 잘 되는 것 확인 !

 

presigned URL을 이용한 업로드는 두번째 진행해보는데, 두번정도 진행해 보니 더욱 잘 이해 될 수 있었던 것 같다.
다음번에 할 때는 더 수월하게 할 수 있길..!!


참고

https://songsong.dev/entry/S3에-파일을-업로드하는-세-가지-방법

https://docs.aws.amazon.com/AmazonS3/latest/userguide/PresignedUrlUploadObject.html

https://velog.io/@seeh_h/AWS-S3-presignedURL%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%B4%EC%84%9C-image-Upload-%ED%95%98%EA%B8%B0-qvqo81gk

https://velog.io/@devjade/2%EC%A3%BC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%8B%A4%EC%9D%B4%EC%96%B4%EB%A6%AC-%EC%9D%B4%EB%AF%B8%EC%A7%80-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B3%A0-preview-%EB%9D%84%EC%9A%B0%EA%B8%B0-feat.-AWS-S3

728x90
반응형