멀티파트 폼데이터 업로드 in React-native(with image-crop-picker, AWS S3)

HAN·2021년 5월 19일
5

task

이미지 크롭 피커를 활용해 얻은 이미지를 폼데이터에 넣어서 서버로 보낸 뒤 s3에 저장하고 프론트 이미지 태그에서 조회하기

task flow

  • 이미지 크롭 피커로 얻은 이미지 객체를 폼데이터 객체에 담기(image -> FormData)
  • 폼데이터 객체를 서버로 전송
  • AWS s3 버킷 생성
  • 멀터-s3를 이용해서 서버로 전달된 이미지 파일을 s3 버킷에 저장
  • 프론트로 이미지 주소를 보내주고, 이미지 태그에서 조회하기

폼 데이터에 담기 image -> FormData

일반적으로 멀티파트 폼데이터를 넣는 프론트 코드는 일반적으로 이렇다.

  • input type을 file로 지정하고 encType을 multipart/form-data로 지정
  • onChange 이벤트로 파일이 첨부되면 e.target.files[0]로 첨부된 파일이 들어온다.
  • 폼데이터에는 e.target.files를 넣어준다.

웹에서 다룰 때는 아래와 같이 썼고

<form method="post" encType="multipart/form-data" action='*'>
	<input type="file" />
</form>

리액트 네이티브도 크게 다를 게 없다. hidden속성을 내장된 첨부 창과 버튼을 숨기고 ref훅을 사용해서 다른 버튼을 통해 첨부를 클릭할 수 있게 해놓은 것 뿐이다.

<Form encType="multipart/form-data" onFinish={onSubmit}>
  <input type="file" name='image' multiple hidden ref={imageInput} onChange={onChangeImage}/>
  <Button onClick={onClickImageUpload}>이미지 업로드</Button>
</Form>

문제는 input 태그를 직접적으로 쓰지 않았고, react-native-image-crop-picker를 썼다는 것.

네이티브 코딩에서는 사진을 올릴 때 주로 스마트폰의 카메라나 갤러리에 접근하기 때문에 input을 곧이곧대로 사용하는 경우가 적다. 내 경우 'react-native-image-crop-picker'라는 라이브러리를 사용했다. 이 녀석은 사진을 찍거나 갤러리에서 선택하고 나면 프로미스 콜백의 인자로 image 라는 객체를 던진다. 처음에는 일단 한번 image를 폼 데이터 안에 넣어봤는데 역시 네트워크 에러가 난다.

image : {
	cropRect: {
		y: 0,		
        height: 1279,	
        width: 960,
        x: 0,
        },	
        modificationDate: "1621348613000”,
        width: 960,
        size: 198012,
        mime: "image/jpeg”,
        height: 1280,
        path: "file:///data/user/0/com.fuck/cache/react…”
   }

e.target.files[0]의 모양은 대략 이러했다. name, type, size 등..

저것만 넣으면 될까 싶었는데 깃헙에 보면 이렇게 하라고 나와있었다. uri, type, name의 속성값을 지정해주면 된다. type은 실험해보니 'multipart/form-data'도 가능하고 'image/jpeg'도 가능하다. name은 파일 고유이름을 가져가도 되지만 저장할 이름은 어차피 서버에서 고유하게 다듬을 것이니 상관없다. uri만 제대로 있으면 된다는 것. 그리고 세가지 속성 중에 하나라도 없으면 네트워크 에러가 난다.

폼데이터 객체를 서버로 전송

그리고 아래와 같이 axios로 서버쪽에 요청을 한다. imageFormData가 data로 들어오고 폼데이터를 보낼 때는 객체모양으로 다듬어주지 않는다.(json으로 보내진다)

AWS s3 버킷 생성

서버에서는 폼데이터를 바로 s3에 저장할 예정이므로, 우선 s3부터 생성해야한다.

aws에 로그인한 뒤, 서비스에서 s3로 들어가면 버킷생성하기가 있다. 생성하는 것 자체는 어렵지 않다. 여러 옵션 중에 이름을 잘 만들고 퍼블릭엑세스만 가능하게 한다. 그러면 아래와 같이 s3서버가 만들어진다.

이후 버킷 이름을 클릭한 뒤 권한 탭으로 들어가 버킷정책을 아래와 같이 편집해준다.

또 내 계정의 보안자격증명으로 들어가 새 액세스 키 만들기를 클릭한 뒤 다운로드 한다. 여기서 다운받은 파일에 들어가면 2개의 키를 준다. 이 키는 s3에 접근할 수 있도록 해주기 때문에, 서버에서 dotenv에 저장한다.

멀터-s3를 이용해서 서버로 전달된 이미지 파일을 s3 버킷에 저장

서버에서는 기본적으로 json 형식의 데이터만 다룰 수 있는데 multer를 통해 멀티파트 데이터를 다룰 수 있도록 해준다. 기존에 multer는 설치가 되어있고 aws-s3에 저장할 수 있도록 해주는 multer-s3를 별도로 설치한다. aws-sdk는 아마존에서 생성한 s3를 서버에서 자바스크립트 문법으로 다룰 수 있도록 해주는 패키지이다.

npm i multer-s3 aws-sdk

아마존 보안설정

설정설치가 끝나면 아마존에 접근할 수 있도록 아래와 같이 설정한다.

멀터 s3 미들웨어 생성

멀터S3 미들웨어를 만들어준다. 아래와 같이 s3를 생성하고, 버켓이름을 써준다음, file을 넣어 cb로 파일을 저장한다.

이제 서버는 멀티파트 데이터를 미들웨어로 인식하면, 자동으로 s3에 저장한다.

프론트로 이미지 주소를 보내주고, 이미지 태그에서 조회하기

멀터 s3 미들웨어 삽입 및 프론트로 uri배열 응답

이후 upload.array(“image”)를 미들웨어로 삽입한다. 그러면 멀티파트 폼데이터로 담겨진 파일들은 s3에 저장된 뒤, req.files로 들어간다.(이 녀석들은 배열 안에 여러 이미지파일 객체들이 들어간다. 아래와 같이 생겼다.)

req.files에 들어있는 파일 중에, 우리는 uri 주소의 배열만 프론트로 보내준다. (v.location)
s3서버에 이미지 파일이 있고, 프론트에서는 해당 uri만 image태그의 source에 넣어주면 자동으로 이미지를 불러들인다.

프론트에서 받는 데이터는 배열 안에 이미지 주소를 가진 객체가 전달된다. 이를 상태로 저장한다.

images = [{ uri : "https://blablabla.. "}, { uri : "https://blablabla.."}...]

이후 아래와 같이 적당한 곳에 잘 풀어주면 된다.

<images source={{ uri : images.uri[0] }} />
<images source={{ uri : images.uri[1] }} />

이미지크롭피커로 얻은 파일을 멀티파트데이터에 담기

profile
즐거운 배움이 되길

1개의 댓글

comment-user-thumbnail
2021년 10월 14일

도움이 많이 됐습니다 :) 감사합니다.

답글 달기