react에서 s3 를 이용해서 image파일 업로드하기

박진현·2021년 11월 12일
4

에러핸들링

목록 보기
5/28
import S3 from 'react-aws-s3';
import { v4 as uuidv4 } from 'uuid';
import dotenv from 'dotenv';
import axios from 'axios';
import styled from 'styled-components/macro';
import { BiImageAdd } from 'react-icons/bi';

const ProfileUploadWrap = styled.div`
  input[id='editicon'] {
    display: none;
  }
  input[id='editicon'] + label {
    width: 1.9rem;
    height: 1.9rem;
    padding: 0.35rem 0 0 0.2rem;
    font-size: 1.2em;
    text-align: center;
    color: #333;
    border-radius: 50%;
    background-color: #fff;
    border: 1px solid #f6f6f6;
    box-shadow: 1px 1px 3px 0 rgba(0, 0, 0, 0.2);
    display: block;
    position: absolute;
    bottom: 0;
    right: -0.8rem;
    cursor: pointer;
  }
  input[id='editicon'] + label:hover {
    color: #fff;
    background-color: brown;
  }
`;

dotenv.config();
// console.log((JSON.parse(localStorage.userInfo).image = data.location));
const ProfileUpload = () => {
  const imagePatchConfig = {
    headers: {
      Authorization: `Bearer ${JSON.parse(localStorage.getItem('userInfo')).token}`,
      'Content-Type': 'application/json',
    },
  };
  const handleClick = (event) => {
    const file = event.target.files[0];
    const newFileName = uuidv4();
    const config = {
      bucketName: process.env.REACT_APP_BUCKET_NAME,
      region: process.env.REACT_APP_REGION,
      accessKeyId: process.env.REACT_APP_ACCESS_ID,
      secretAccessKey: process.env.REACT_APP_ACCESS_KEY,
    };
    const ReactS3Client = new S3(config);
    if (file) {
      if (file.size >= 1 * 1024 * 1024) {
        alert('1mb 이하의 파일만 업로드 가능합니다.');
        event.target.value = null;
      } else {
        if (file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg') {
          ReactS3Client.uploadFile(file, newFileName).then((data) => {
            // console.log(data);
            axios
              .patch('서버주소', { image: data.location }, imagePatchConfig)
              .then((res) => {
                // console.log(res, '이미지 보내짐');
                if (JSON.parse(localStorage.getItem('userInfo')).image.split('.')[0] === 's3서버주소분기') {
                  ReactS3Client.deleteFile(JSON.parse(localStorage.getItem('userInfo')).image.split('/')[3])
                    .then((res) => {
                      localStorage.setItem(
                        'userInfo',
                        JSON.stringify({
                          ...JSON.parse(localStorage.userInfo),
                          image: data.location,
                        })
                      );
                      window.location.reload();
                      // console.log(res, '삭제');
                    })
                    .catch((err) => {
                      console.log(err, '삭제안됨');
                      console.log(JSON.parse(localStorage.getItem('userInfo')).image, 'tet');
                    });
                } else {
                  localStorage.setItem(
                    'userInfo',
                    JSON.stringify({
                      ...JSON.parse(localStorage.userInfo),
                      image: data.location,
                    })
                  );
                  window.location.reload();
                }
              })
              .catch((err) => {
                console.log(err, '이미지 변경 안됨');
              });
          });
        } else {
          alert('JPEG, PNG, JPG 파일만 업로드 가능합니다.');
          event.target.value = null;
        }
      }
    }
  };

  return (
    <ProfileUploadWrap>
      <input id='editicon' type='file' accept='image/*' onChange={handleClick} />
      <label htmlFor='editicon'>
        <BiImageAdd />
      </label>
    </ProfileUploadWrap>
  );
};

export default ProfileUpload;

주의사항)
1.s3에서는 파일 이름이 동일할 경우 파일이 올라가지 않기 때문에 uuid를 통해 고유 키를 나눠준다.
2.파일확장자가 jpeg,png,jpg가 아닐경우에도 파일이 올라가지기 때문에 if문을 걸어 예외처리해준다.
3.파일업로드가 완료되어 204를 받았을 경우 axios로 서버에 location 정보를 보내주어야한다. 언제 보내줄지 처리하는게 관건이고 성공적으로 유저 프로필을 변경하였을 경우 s3에 남아있는 기존 파일을 삭제시켜야한다. (이 과정이 생각보다 굉장히 복잡하다)
4.만약 취소를 누를경우 file.type 이 undefined 되기 때문에 if(file)로 핸들링해줘야함
5.파일사이즈가 1mb 이하일때만 서버에 보내야함
6.파일선택창에서 취소를 누를경우 file을 초기화해줘야함
7.images가 아닌 파일들을 선택하지 못하도록 accept="image/*"로 이미지만 선택
8. delete로 이전 이미지 삭제하기 위해선 버킷 cors정책에서 delete 추가해야합니다.
9. 로컬스토리지 업데이트할때 setitem으로 기존 걸 구조분해해서 업데이트해야합니다..
10. delete할때 파일이름만 적어야하는데 앞에 주소까지 같이 적혀서 split으로 잘라야 한다.
11. 처음 가입한 회원일 경우 /images/user.jpeg 라는 파일로 지정되어 있어서 s3에서 삭제하면 안된다. if로 예외처리 해줘야한다

아니 이게 이렇게 복잡할 일이었나.. 참 프론트의 세계는 단순해보여도 뒤에서 보면 복잡한 것들 투성이다.
그리고 생각나는 거의 모든 예외들을 처리했다. 백앤드했다가 프론트했다가 계속 왔다갔다 하니까 머리가 터질 것 같다. s3는 왜 맨날 날 괴롭히는 걸까 ec2반만큼이라도 따라갔으면 좋겠네

profile
👨🏻‍💻 호기심이 많고 에러를 좋아하는 프론트엔드 개발자 박진현 입니다.

1개의 댓글

comment-user-thumbnail
2022년 3월 29일

s3는 요청시마다 요금이 부과되므로 삭제요청은 따로 안하는게 바람직한 것 같습니다.

답글 달기