본문 바로가기
프로그래밍/Web Basic

[Express] S3 이미지 업로드

by YuminK 2023. 12. 24.

전체적인 프로세스: S3 버킷 생성 -> IAM 사용자 추가 -> 백엔드 및 프론트엔드 연동(express, react 환경)

 

S3 버킷 생성 

1. S3 검색, 버킷 만들기 클릭

2. 버킷 만들기

리전, 버킷 이름 입력 -> ACL 활성화 체크(다른 계정이 버킷 객체를 소유할 수 있음) -> 모든 퍼블릭 액세스 차단 off 

-> 퍼블릭상태가 될 수 있음을 알고 있습니다 체크 -> 버전관리 off, 기본 암호화, 버킷 키 활성화(기본 셋팅대로 진행)

 

3. 버킷 정책 수정

버킷 이름을 클릭 -> 탭에 권한 -> 버킷 정책 부분에 편집 버튼 클릭 -> 정책 생성기 버튼 클릭 

-> s3 Bucket Policy 체크 -> Principal에 * 입력 -> Actions에 PutObject, GetObject 추가 -> 

ARN에는 이전 탭에 있던 버킷 ARM 이름을 복사하여 붙여넣기 그리고 /* 문자 추가(arn:aws:s3:::*****/*) 

-> Add Statement 클릭 -> Generate Policy 클릭하여 복사 -> 이전 챕의 정책에 붙여넣기 -> 변경사항 저장

https://velog.io/@jinseoit/AWS-S3-bucket

 

IAM 사용자 추가

1. 아마존 콘솔에서 우측 상단에 보안자격 증명 클릭

2. 액세스 관리 - 사용자 클릭

3. 사용자 생성

이름 입력 -> 다음 -> 직접 정책 연결 체크 -> s3 검색하여 full access 체크 추가

 

4. 이름을 클릭하여 세부 항목으로 들어간다 -> 보안 자격 증명 -> 액세스 키 만들기 클릭 -> CLI(기본설정) -> 확인-> 

CSV 파일을 다운로드한다.

https://celdan.tistory.com/38

 

백엔드 및 프론트엔드 연동

백엔드 .env 파일에 csv 정보를 추가한다.

참고로 region은 서울이다.

S3_BUCKET_NAME=test
S3_BUCKET_REGION=ap-northeast-2
S3_ACCESS_KEY=test
S3_SECRET_ACCESS_KEY=test

 

패키지 추가

yarn add aws-sdk, multer, multer-s3@^2.10.0, @aws-sdk/client-s3

 

참고로 aws-sdk가 v3로 오르면서 하나씩 분리되기 시작했는데, 여기서는 기존 방식(v2)로 설명한다. 

multer-s3는 2.10.0 버전을 맞춰야 multer 관련 이슈가 생기지 않는다. 

 

upload.js

let aws = require("aws-sdk");
aws.config.update({
  accessKeyId: process.env.S3_ACCESS_KEY,
  secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
  region: process.env.S3_BUCKET_REGION,
});

let multer = require("multer");
let multerS3 = require("multer-s3");
let s3 = new aws.S3();

const uploadS3 = multer({
  storage: multerS3({
    s3: s3,
    bucket: process.env.S3_BUCKET_NAME,
    contentType: multerS3.AUTO_CONTENT_TYPE,
    key: (req, file, callback) => {
      callback(null, `test`);
    },
    acl: "public-read-write",
  }),
  limits: { fileSize: 10 * 1024 * 1024 },
});

module.exports = {
  uploadS3,
  s3,
};

 

env  파일에서 적은 정보를 토대로 config를 설정해준다. uploadS3는 미들웨어로서 사용되는데 중간에 key에 적힌 부분이 s3에 올릴 경로를 정하는 부분이다. 프로그램의 로직에 맞게 수정한다. 

 

이후 express의 미들웨어에 맞게 사용하면 된다. formData 형태로 들어온 데이터에서 image 값이 있으면 위에서 정의한 미들웨어가 처리되는 식이다. 데이터가 없으면 그냥 넘어가고, 데이터가 있으면 req.file 값이 로그로 찍힌다. 

 

router.post("/editPost", uploadS3.single("image"), (req, res) => { console.log(req.file); res.send(200);})

 

여기까지 진행한 이후에 Postman을 통해 formData로 이미지를 전달하여 테스트해보길 권장한다. AWS s3 페이지, 프로그램 콘솔 모두 확인 ㄱㄱ

 

react내에서 사용

// 이미지 변경 버튼(파일 선택)
<input
  type="file"
  name="file"
  id="file"
  accept="image/*"
  onChange={onChange}
/>

<Image
  boxSize="200"
  border="0.5px solid grey"
  src={preview}
/>

// onChange 함수 및 useState 
  const [imageFile, setImageFile] = useState();
  const [preview, setPreview] = useState("");

  const onChange = (e) => {
    const img = e.target.files[0];
    setImageFile(img);
    console.log(img);

    var reader = new FileReader();
    reader.readAsDataURL(img);
    reader.onload = (e) => {
      setPreview(e.target.result);
    };
  };
  
  // 버튼에서 호출
   const clickEdit = async () => {
    try {
      let formData = new FormData();
      formData.append("image", imageFile);
      let res = await axios.post(`/editPost`, formData, {
        baseURL: "http://localhost:3000",
        withCredentials: true,
        "Content-Type": "multipart/form-data",
      });

      if (res.status === 200) {
        console.log("성공");
      } else {
        console.log("실패");
      }
    } catch (e) {
      console.log(e);
      console.log("실패");
    }
  };

 


기타 이슈 사항들...

 

이슈 1

처리하는 key(경로)를 동일하게 하여 이미지를 업로드하면 자동으로 수정이 된다. 다만 DB에서 값을 가져왔을 때 url 정보가 변경이 되지 않아 컴포넌트를 다시 그리지 않는 이슈가 있어 매번 이미지를 새로 생성하고 이전에 올린 이미지는 삭제하는 로직으로 처리했다. 

// DB에서 가져온 imagePath 정보가 있다면... 
s3.deleteObject(
  {
    Bucket: process.env.S3_BUCKET_NAME,
    Key: imagePath, // 삭제하고 싶은 이미지의 key
  },
  (err, data) => {
    if (err) console.log(err); 
    else console.log(data);
  }
);

 

이슈2

formData 형태로 데이터를 전송하기 위해서는 Content-Type을 multipart/form-data를 맞춰줘야 한다. 기존에 생성했던 axios client는 json으로 설정해서 쓰다보니 뭔가 잘 안 되더라. axios 라이브러리를 직접 사용하여 따로 처리했다. 

 

이슈3

블로그 찾아보면 이미지랑 다른 값을 같이 보내려면 Blob을 써야 한다는 둥, json을 string으로 만들어서 같이 던져야 한다는 둥 뭔 이상한 소리 하는 애들 많은데, 나는 당연히 Postman을 믿었고 전혀 문제 없었다. 자신이 던지는 데이터가 String 아니면 File만 된다는 것만 인식하고 있으면 된다. (image랑 다른 값을 같이 던져도 상관없다. 그냥 formData.append해서 보내면 된다.)

 

그리고 express에서 req.body.image를 출력해도 콘솔 상에서는 {} (emtpy json)으로 표시되는데 정상 처리되고 있는 거다. 이 부분에서 많이 낚였다.

'프로그래밍 > Web Basic' 카테고리의 다른 글

크롬 익스텐션 Storage API 정리  (0) 2024.01.03
Web Storage API 정리  (0) 2024.01.02
[Express] MySql 연동  (0) 2023.12.17
[Express] express 공식 문서 정리  (0) 2023.12.05
Nginx 웹서버 정리 3  (0) 2023.11.17

댓글